Network switching without confusion
How to detect when a wallet is on the wrong network, ask it to switch or add a chain, show the current network clearly, and keep testnets obvious.
5 min read
An EVM wallet is connected to one network at a time, and your dApp may support only some of them. When the two disagree, people see a "Wrong network" banner, a disabled button, or worse, a transaction that fails for no visible reason. Most of this confusion comes from three gaps: the interface doesn't show which network is active, it doesn't explain what a network is, and it asks to switch at the wrong moment.
This guide covers detecting the network, showing it, asking the wallet to switch or add a chain, and keeping testnets clearly separate from real funds.
Detect the network, then keep listening
Ask the provider for the current chain with eth_chainId, which returns a hex string, and listen for changes. People switch networks in their wallet at any time, and your interface has to follow.
const chainId = parseInt(await provider.request({ method: 'eth_chainId' }), 16)
provider.on('chainChanged', (hexChainId) => {
setChainId(parseInt(hexChainId, 16))
})
Store chain IDs as numbers in your app and convert to hex only at the provider boundary. A few common ones:
| Network | Chain ID | Hex |
|---|---|---|
| Ethereum | 1 | 0x1 |
| Polygon PoS | 137 | 0x89 |
| Base | 8453 | 0x2105 |
| Arbitrum One | 42161 | 0xa4b1 |
| Sepolia (testnet) | 11155111 | 0xaa36a7 |
Read balances and contract state for the network your page is about, using your own RPC endpoint, regardless of what the wallet is on. Only transactions and signatures need the wallet to be on the right network. This lets a page about Base work fully for someone whose wallet is set to Ethereum, up to the moment they press a button.
Show the current network, always
The active network belongs next to the account in the header: the chain logo plus its name, for example the Ethereum or Arbitrum One mark with the name beside it. Never show a bare chain ID, and don't rely on the logo alone. Several networks have similar marks, and testnets usually reuse their mainnet's logo.
Clicking the network indicator should open a switcher listing the networks you support. Show each with its logo and name, mark the current one, and keep the order stable so people learn where things are. Chain logos in network switchers goes into logo sizes, pairing logos with names and telling testnets apart.
Ask to switch at the point of action
A banner on page load saying "Wrong network, please switch" interrupts people who were only looking. Put the switch request where it's needed: on the action button.
If the person is on Ethereum and wants to swap on Base, the swap button reads "Switch to Base" with the Base logo. One click switches, and the button becomes "Swap". The person never has to understand chain IDs or open their wallet settings.
The request itself uses wallet_switchEthereumChain, defined in EIP-3326. If the wallet doesn't know the network yet, add it with wallet_addEthereumChain from EIP-3085. Many wallets, including MetaMask, report an unknown chain with error code 4902:
const BASE = {
chainId: '0x2105',
chainName: 'Base',
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
rpcUrls: ['https://mainnet.base.org'],
blockExplorerUrls: ['https://basescan.org'],
}
async function switchToBase(provider) {
try {
await provider.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: BASE.chainId }],
})
} catch (error) {
if (error.code === 4902) {
await provider.request({ method: 'wallet_addEthereumChain', params: [BASE] })
return
}
throw error
}
}
A few practical notes:
- Don't trust the promise alone. After switching or adding, confirm with
eth_chainIdor wait forchainChanged. Some wallets add a network without switching to it, and some offer to switch as a separate step. - Not every wallet returns
4902consistently. Some wrap the original error or use a generic code. Test with the wallets your users have, and fall back to offering the add request when switching fails for an unrecognized reason. - Handle the person saying no. If they decline, error code
4001under EIP-1193, keep the "Switch to Base" button in place and say nothing alarming. Declining is a normal choice, not an error. - Use a reliable RPC URL. The
rpcUrlsyou pass can become the endpoint the wallet uses for that network for anyone who adds it through your app. Use the chain's official public endpoint or one you're prepared to support.
Some wallets can't switch programmatically at all, particularly some mobile wallets connected through a pairing protocol. Detect a failed request and give manual instructions: "Open your wallet and switch the network to Base."
When the network isn't supported
If the wallet is on a network your app doesn't support at all, don't blank the page. Keep showing content, disable actions that need a transaction, and offer the switch:
Your wallet is on a network this app doesn't support. Switch to Ethereum, Base or Arbitrum One to continue.
Each network name can be its own button. Avoid "Unsupported network" as the whole message; it describes the problem without suggesting a fix.
Testnets need to look different
Testnets are where developers and testers spend their days, and testnet tokens have no value. The danger runs in both directions: someone thinks they're on a testnet when they're not, or they're confused why their "ETH" is worth nothing.
Make testnets impossible to miss:
- Add a text label to the network indicator, for example "Sepolia" or "Testnet", in addition to the reused mainnet logo.
- Show a persistent, low-key banner across the top of the app on any testnet.
- Separate testnets in the switcher, under their own heading, and hide them by default in production builds unless a setting enables them.
- Never show fiat values for testnet balances. A dollar value next to test ETH is simply wrong.
Testnet names change as networks are retired and replaced, so keep the list in configuration rather than spread through components.
Beyond EVM
Everything above applies to Ethereum-compatible wallets. Non-EVM chains such as Solana work differently: the dApp usually chooses the cluster through its RPC connection, and wallets such as Phantom handle network settings in their own interface. The display advice still holds. Always show which network the person is on, with logo and name, and treat test networks as visibly separate.
If your app covers many chains at once, see designing for many chains for balances and badges across networks.