Developer Guide: Using MetaMask with dApps

Try Tangem secure wallet →

Table of contents


Overview

This guide explains how to integrate MetaMask with your dApp. Expect practical code snippets, real-world gotchas, and explanations of how the provider works under the hood. I use MetaMask daily for testing DeFi flows, and I’ll call out where things break (and why).

How MetaMask exposes a provider (EIP-1193)

MetaMask injects a provider object into the page (window.ethereum). That object implements the EIP-1193 request interface — a generic RPC wrapper your dApp should call instead of reading private keys or assuming accounts exist. Why? Because the extension or mobile app controls the keys and user consent.

Detecting an injected provider looks like this (simple):

if (typeof window !== 'undefined' && window.ethereum) {
  // Provider detected
  const isMetaMask = !!window.ethereum.isMetaMask;
}

This is the baseline for web3 autodetect metamask account flows. (Yes, some browsers block injections; always feature-detect.)

Basic connect flow — code samples

There are two common patterns: call provider.request('eth_requestAccounts') and use libraries that wrap it (web3.js / ethers.js / wagmi). web3.eth.getAccounts will return the currently unlocked accounts, but it does not prompt the user to connect. That distinction matters.

web3.eth.getAccounts metamask example:

// Using web3.js after injection
const accounts = await web3.eth.getAccounts();
// returns [] if locked/disconnected

Preferred connect (prompts user):

// EIP-1193 style
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
// accounts[0] is the selected address

Or with ethers.js:

const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = provider.getSigner();

Note: web3 metamask get account patterns are still common in legacy code. I recommend switching to provider.request for predictable permission prompts.

Handle these events from the provider: accountsChanged, chainChanged, connect, disconnect. Reactively updating UI on those events is essential for correct UX.

Adding and switching networks (wallet_addEthereumChain)

Want to add a custom RPC from your dApp? The RPC method is wallet_addEthereumChain (often written wallet_addethereumchain metamask in search queries). Use it carefully — the call asks the user to approve adding a network.

Example:

await window.ethereum.request({
  method: 'wallet_addEthereumChain',
  params: [{
    chainId: '0x89', // hex for 137
    chainName: 'Polygon Mainnet',
    nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
    rpcUrls: ['https://rpc-mainnet.matic.network/'],
    blockExplorerUrls: ['https://polygonscan.com']
  }]
});

If the chain already exists, use wallet_switchEthereumChain with the chainId. Always handle user rejection and provider errors. See how to add custom networks for a step-by-step.

Adding tokens from your dApp (React example)

Users expect a one-click “Add token” flow. That’s wallet_watchAsset.

react add to wallet metamask example:

function AddTokenButton({ tokenAddress, symbol, decimals, image }) {
  async function addToken() {
    try {
      const added = await window.ethereum.request({
        method: 'wallet_watchAsset',
        params: {
          type: 'ERC20',
          options: { address: tokenAddress, symbol, decimals, image },
        },
      });
      console.log('Token added:', added);
    } catch (err) {
      console.error(err);
    }
  }

  return <button onClick={addToken}>Add token to wallet</button>;
}

This only prompts the user; they must confirm. And yes, some mobile browsers do not expose the injected provider reliably — test both desktop and mobile.

Mobile: WalletConnect, SDKs, and wagmi

Mobile is different. Many users run MetaMask as a mobile app and expect deep links or WalletConnect sessions. WalletConnect creates a remote provider session (QR/deeplink) that works well when your web app opens a mobile browser or uses an in-app browser.

If you use wagmi, you get connectors that simplify both desktop and mobile flows. For example, use MetaMaskConnector (injected) on desktop and WalletConnectConnector for mobile to reach MetaMask Mobile. wagmi metamask mobile setups often use a WalletConnect bridge under the hood for mobile UIs.

If you need a programmatic provider for mobile without WalletConnect, consider the official SDK connection metamask option (an SDK that exposes a provider over a tunnel). Each method has trade-offs for UX and security.

See walletconnect-and-mobile-browser and walletconnect-mobile-browser for testing notes.

Security and dev best practices

For approval revocation guidance, link users to revoke approvals and to general security best practices.

Common problems and debugging tips

If transactions appear stuck, consult stuck-pending-transactions and gas-fees-and-eip-1559.

Quick comparison: connection methods

Method UX When to use
Injected provider (window.ethereum) Instant (desktop) dApps focused on desktop extension users
WalletConnect Mobile-friendly (QR/deeplink) Mobile-first dApps or wallets that don't inject
SDK connection (remote provider) Programmatic tunnel Apps that need a controlled mobile provider (advanced)

Who should use this, who should look elsewhere

Who this is for: frontend engineers building EVM-compatible DeFi UIs, token dashboards, and dApp onboarding flows. If you expect users to switch networks, implement wallet_addEthereumChain flows and clear error handling.

Who should look elsewhere: teams needing on-chain custody or institutional custody solutions (hot wallet is for user keys). If users need hardware-level signing, add explicit Ledger/Trezor support (see connect-ledger).

FAQ

Q: Is it safe to keep crypto in a hot wallet? A: Hot wallets balance convenience and risk. For daily DeFi, use a hot wallet for small balances and a hardware wallet for large holdings. See backup-and-recovery-options.

Q: How do I revoke token approvals? A: Use on-chain revoke UIs or call smart contract functions that set allowance to zero. Follow the guide at revoke-approvals.

Q: What happens if I lose my phone? A: If your seed phrase is backed up, restore to another device. If not, funds are likely lost. Read backup-and-recovery-options for secure workflows.

Conclusion & next steps

MetaMask provider integration is straightforward once you respect EIP-1193 patterns, handle user permissions, and test desktop and mobile flows. I’ve built flows that work across L2s and mobile, and the common failure points are predictable: missing provider, wrong chainId format, and unhandled user rejection. Want practical how-tos next? Read the mobile setup guide and the step-by-step on adding networks: setup-mobile and how-to-wallet-addethereumchain.

Try Tangem secure wallet →