Build a DApp on MAGNE Testnets
Guide for developers to build decentralized applications on MAGNE L1 and M Hash L2 testnets using Ethers.js.
Testnet Only β This guide is for testnet DApp development only. MAGNE L1 Testnet and M Hash L2 Testnet are EVM-compatible environments. Testnet tokens have no monetary value. Network parameters may change as testing progresses.
- Works with standard Ethereum development tools and libraries
- Supports Ethers.js, Wagmi, Viem, and other Web3 libraries
- Compatible with MetaMask, WalletConnect, and other wallets
- Build frontend UI with React, Vue, or plain JavaScript
DApp Architecture
Frontend UI
React, Vue, or plain HTML/JS for user interface
Wallet Connection
MetaMask, WalletConnect, or other Web3 wallets
RPC Provider
Ethers.js or viem for blockchain interaction
Smart Contract
Your deployed contracts on testnet
Transaction Tracking
Block explorer for confirmation status
Network Constants
JavaScript Configuration
export const MAGNE_L1_TESTNET = {
chainId: '0x' + (20250810).toString(16),
chainName: "MAGNE L1 Testnet",
nativeCurrency: {
name: "MHA",
symbol: "MHA",
decimals: 18
},
rpcUrls: ["https://rpc.testnet.magicalhash.com"],
blockExplorerUrls: ["https://explorer.testnet.magicalhash.com"]
};
export const M_HASH_L2_TESTNET = {
chainId: '0x' + (20250827).toString(16),
chainName: "M Hash L2 Testnet",
nativeCurrency: {
name: "MHA",
symbol: "MHA",
decimals: 18
},
rpcUrls: ["https://l2-rpc.testnet.magicalhash.com"],
blockExplorerUrls: ["https://l2-explorer.testnet.magicalhash.com"]
};
Connect Wallet Example
Request Account Access
import { ethers } from 'ethers';
const provider = new ethers.BrowserProvider(window.ethereum);
async function connectWallet() {
try {
const accounts = await provider.send("eth_requestAccounts", []);
console.log("Connected:", accounts[0]);
return accounts[0];
} catch (error) {
console.error("Connection rejected:", error.message);
}
}
Switch Network
async function switchToL1() {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x' + (20250810).toString(16) }],
});
} catch (switchError) {
console.error("Switch failed:", switchError.message);
}
}
async function addNetwork() {
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [MAGNE_L1_TESTNET],
});
} catch (addError) {
console.error("Add network failed:", addError.message);
}
}
Tip: Call switchToL1 first. If it fails with chain not found error, call addNetwork to add the network to the wallet.
Read Contract Example
import { ethers } from 'ethers';
const CONTRACT_ADDRESS = "your_deployed_contract_address";
const ABI = [
"function message() view returns (string)",
"function setMessage(string newMessage)"
];
async function readContract() {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
try {
const message = await contract.message();
console.log("Current message:", message);
return message;
} catch (error) {
console.error("Read failed:", error.message);
}
}
Write Contract Example
async function writeContract(newMessage) {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer);
try {
const tx = await contract.setMessage(newMessage);
console.log("Transaction sent:", tx.hash);
// Wait for confirmation
const receipt = await tx.wait();
console.log("Confirmed:", receipt.hash);
return receipt;
} catch (error) {
if (error.message.includes("user rejected")) {
console.log("Transaction rejected by user");
} else {
console.error("Transaction failed:", error.message);
}
}
}
User Flow: When calling write functions, the wallet will prompt the user to review and approve the transaction. Users can reject if they do not recognize the action.
Transaction Tracking
Track Transaction Status
function getExplorerUrl(chainId, txHash) {
const explorers = {
20250810: "https://explorer.testnet.magicalhash.com",
20250827: "https://l2-explorer.testnet.magicalhash.com"
};
return `${explorers[chainId]}/tx/${txHash}`;
}
async function trackTransaction(tx) {
console.log(`View on explorer: ${getExplorerUrl(20250810, tx.hash)}`);
// Wait for 1 confirmation
const receipt = await tx.wait(1);
if (receipt.status === 1) {
console.log("Transaction successful!");
} else {
console.log("Transaction failed!");
}
}
UI / UX Notes
- Show network name β Display the current network (MAGNE L1 or M Hash L2) in the UI so users know where they are
- Show testnet warning β Display a clear banner indicating testnet environment
- Display transaction status β Show pending, confirmed, or failed states
- Provide explorer links β Link to transaction and contract pages on the block explorer
- Handle wallet rejection β Gracefully handle when users reject transactions
- Handle chain mismatches β Prompt user to switch to correct network
- Show gas estimates β Display estimated transaction cost before confirmation
Security Notes
β οΈ Security Requirements
Never ask users for seed phrases or private keys. Legitimate DApps never need these credentials.
Never store private keys in frontend code, localStorage, or cookies.
Use dedicated test wallets for all testnet development.
Testnet tokens have no monetary value.
Smart contract and DApp examples are for testing and educational purposes only. Always audit code before production use.
Troubleshooting
Wallet not detected
Ensure the user has a Web3 wallet installed (MetaMask, Rabby, etc.). Check for window.ethereum availability.
Wrong network selected
Call wallet_switchEthereumChain with the correct chainId. Show users a prompt to switch if on wrong network.
User rejected request
Handle rejection gracefully. Check error.message.includes("user rejected") to identify this case.
RPC timeout
Check your internet connection. Try refreshing the page. If persistent, the RPC may be under maintenance.
Contract address not found
Verify the contract is deployed to the correct network. Check that you're connected to the expected network in the wallet.
Transaction pending
Check the block explorer for pending status. L2 transactions typically confirm faster than L1.
Gas estimation failed
This usually means the transaction would fail. Check contract state, parameters, and wallet balance.