> For the complete documentation index, see [llms.txt](/llms.txt).

# Interact with smart contracts

Use MetaMask Connect EVM to read and write smart contract data from your JavaScript dapp. MetaMask Connect EVM provides an EIP-1193 provider that works with viem, ethers.js, and web3.js for calling contract functions, deploying contracts, listening to events, and managing transaction states with proper error handling.

With MetaMask Connect EVM:

- **Read data** from smart contracts.
- **Write data** to smart contracts.
- **Handle contract events**.
- **Manage transaction states**.
- **Handle contract errors**.

The following examples demonstrate how to use MetaMask Connect EVM with viem, web3.js, ethers.js, Ethereum APIs, or Wagmi to interact with Solidity smart contracts.

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

- Follow the [JavaScript quickstart](/metamask-connect/evm/quickstart/javascript/) or [Wagmi quickstart](/metamask-connect/evm/quickstart/wagmi/) to install, initialize, and connect the EVM client.
- Create and deploy a simple smart contract. For example, the following contract allows anyone to read and write a message to it.  
<details class="details_lb9f alert alert--info details_b_Ee" data-collapsed="true"><summary>Hello World contract</summary><div><div class="collapsibleContent_i85q"><div><div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// SPDX-License-Identifier: GPL-3.0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">pragma solidity </span><span class="token operator">&gt;=</span><span class="token number">0.7</span><span class="token number">.0</span><span class="token plain"> </span><span class="token operator">&lt;</span><span class="token number">0.9</span><span class="token number">.0</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">contract </span><span class="token maybe-class-name">HelloWorld</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token function" style="color:rgb(80, 250, 123)">constructor</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token plain"> memory initMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    message </span><span class="token operator">=</span><span class="token plain"> initMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">update</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token plain"> memory newMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    message </span><span class="token operator">=</span><span class="token plain"> newMessage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div></div></div></div></details>

## Read from contracts[​](#read-from-contracts "Direct link to Read from contracts")

- viem
- web3.js
- ethers.js
- Ethereum API
- Wagmi

```
import { createEVMClient } from '@metamask/connect-evm'
import { createPublicClient, custom } from 'viem'
import { sepolia } from 'viem/chains'

const evmClient = await createEVMClient({
  dapp: {
    name: 'MetaMask Connect EVM Example',
    url: window.location.href,
    iconUrl: 'https://mydapp.com/icon.png', // Optional
  },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      '0xaa36a7': 'https://sepolia.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})
const provider = evmClient.getProvider()

const publicClient = createPublicClient({
  chain: sepolia,
  transport: custom(provider),
})

const contractABI = [
  {
    inputs: [{ internalType: 'string', name: 'initMessage', type: 'string' }],
    stateMutability: 'nonpayable',
    type: 'constructor',
  },
  {
    inputs: [],
    name: 'message',
    outputs: [{ internalType: 'string', name: '', type: 'string' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'string', name: 'newMessage', type: 'string' }],
    name: 'update',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
]

const contractAddress = '0x8AA6820B3F197384874fAdb355361758258cb981' // On Sepolia testnet, replace with your contract address

// Read message from smart contract
const message = await publicClient.readContract({
  address: contractAddress,
  abi: contractABI,
  functionName: 'message',
})

```

```
import { createEVMClient } from '@metamask/connect-evm'
import { Web3 } from 'web3'

const evmClient = await createEVMClient({
  dapp: {
    name: 'MetaMask Connect EVM Example',
    url: window.location.href,
    iconUrl: 'https://mydapp.com/icon.png', // Optional
  },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      '0xaa36a7': 'https://sepolia.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})
const provider = evmClient.getProvider()

const web3 = new Web3(provider)

const contractABI = [
  {
    inputs: [{ internalType: 'string', name: 'initMessage', type: 'string' }],
    stateMutability: 'nonpayable',
    type: 'constructor',
  },
  {
    inputs: [],
    name: 'message',
    outputs: [{ internalType: 'string', name: '', type: 'string' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'string', name: 'newMessage', type: 'string' }],
    name: 'update',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
]
const contractAddress = '0x04cA407965D60C2B39d892a1DFB1d1d9C30d0334'
const contract = new web3.eth.Contract(contractABI, contractAddress)

// Read message from smart contract
const message = await contract.methods.message().call()

```

```
import { createEVMClient } from '@metamask/connect-evm'
import { ethers } from 'ethers'

const evmClient = await createEVMClient({
  dapp: {
    name: 'MetaMask Connect EVM Example',
    url: window.location.href,
    iconUrl: 'https://mydapp.com/icon.png', // Optional
  },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      '0xaa36a7': 'https://sepolia.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})
const provider = evmClient.getProvider()

const ethersProvider = new ethers.BrowserProvider(provider)
const signer = await ethersProvider.getSigner()

const contractABI = [
  {
    inputs: [{ internalType: 'string', name: 'initMessage', type: 'string' }],
    stateMutability: 'nonpayable',
    type: 'constructor',
  },
  {
    inputs: [],
    name: 'message',
    outputs: [{ internalType: 'string', name: '', type: 'string' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'string', name: 'newMessage', type: 'string' }],
    name: 'update',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
]
const contractAddress = '0x04cA407965D60C2B39d892a1DFB1d1d9C30d0334'
const contract = new ethers.Contract(
  contractAddress,
  JSON.parse(JSON.stringify(contractABI)),
  signer
)

// Read message from smart contract
const message = await contract.message()

```

```
import { createEVMClient } from '@metamask/connect-evm'

const evmClient = await createEVMClient({
  dapp: {
    name: 'MetaMask Connect EVM Example',
    url: window.location.href,
    iconUrl: 'https://mydapp.com/icon.png', // Optional
  },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      '0xaa36a7': 'https://sepolia.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})
const provider = evmClient.getProvider()

async function getMessage(contractAddress, userAddress) {
  try {
    // Create function signature for balanceOf(address)
    const functionSignature = '0x06fdde03'
    // Pad address to 32 bytes
    const encodedAddress = userAddress.slice(2).padStart(64, '0')
    const result = await provider.request({
      method: 'eth_call',
      params: [
        {
          to: contractAddress,
          data: functionSignature + encodedAddress,
        },
      ],
    })
    return result
  } catch (error) {
    console.error('Error reading message:', error)
    throw error
  }
}

// Example usage
async function displayMessage() {
  const status = document.getElementById('status')
  try {
    const message = await getMessage('0xContractAddress', '0xUserAddress')
    status.textContent = `Message: ${message}`
  } catch (error) {
    status.textContent = `Error: ${error.message}`
  }
}

```

```
import { useReadContract } from 'wagmi'

function TokenBalance() {
  const {
    data: balance,
    isError,
    isLoading,
  } = useReadContract({
    address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
    abi: [
      {
        name: 'balanceOf',
        type: 'function',
        stateMutability: 'view',
        inputs: [{ name: 'owner', type: 'address' }],
        outputs: [{ name: 'balance', type: 'uint256' }],
      },
    ],
    functionName: 'balanceOf',
    args: ['0x03A71968491d55603FFe1b11A9e23eF013f75bCF'],
  })

  if (isLoading) return <div>Loading balance...</div>
  if (isError) return <div>Error fetching balance</div>

  return <div>Balance: {balance?.toString()}</div>
}

```

### Batch contract reads[​](#batch-contract-reads "Direct link to Batch contract reads")

With Wagmi, perform multiple contract read operations using the [useReadContracts](https://wagmi.sh/react/api/hooks/useReadContracts) hook. This hook batches contract calls internally, returning the results as an array. For example:

```
import { useReadContracts } from "wagmi";

// Example contract definitions with their address and ABI
const contractA = {
  address: "0xContractAddress1",
  abi: contractABI1,
} as const;

const contractB = {
  address: "0xContractAddress2",
  abi: contractABI2,
} as const;

function MyBatchReadComponent() {
  const { data, isError, isLoading } = useReadContracts({
    contracts: [
      {
        ...contractA,
        functionName: "getValueA",
      },
      {
        ...contractA,
        functionName: "getValueB",
      },
      {
        ...contractB,
        functionName: "getValueX",
        args: [42],
      },
      {
        ...contractB,
        functionName: "getValueY",
        args: [42],
      },
    ],
  });

  if (isLoading) return <div>Loading...</div>;
  if (isError) return <div>Error fetching data.</div>;

  return (
    <div>
      <p>getValueA: {data?.[0]?.toString()}</p>
      <p>getValueB: {data?.[1]?.toString()}</p>
      <p>getValueX: {data?.[2]?.toString()}</p>
      <p>getValueY: {data?.[3]?.toString()}</p>
    </div>
  );
}

```

In this example, four contract read calls are batched together. The results are returned as an array in the same order as the calls, allowing you to process each result accordingly.

info

"Batching" can also refer to [batching JSON-RPC requests](/metamask-connect/evm/guides/metamask-exclusive/batch-requests/) using MetaMask Connect EVM's `metamask_batch` method, or [sending atomic batch transactions](/metamask-connect/evm/guides/send-transactions/batch-transactions/) in MetaMask.

## Write to contracts[​](#write-to-contracts "Direct link to Write to contracts")

- viem
- web3.js
- ethers.js
- Ethereum API
- Wagmi

```
import { createEVMClient } from '@metamask/connect-evm'
import { createPublicClient, custom } from 'viem'
import { sepolia } from 'viem/chains'

const evmClient = await createEVMClient({
  dapp: {
    name: 'MetaMask Connect EVM Example',
    url: window.location.href,
    iconUrl: 'https://mydapp.com/icon.png', // Optional
  },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      '0xaa36a7': 'https://sepolia.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})
const provider = evmClient.getProvider()

const contractABI = [
  {
    inputs: [{ internalType: 'string', name: 'initMessage', type: 'string' }],
    stateMutability: 'nonpayable',
    type: 'constructor',
  },
  {
    inputs: [],
    name: 'message',
    outputs: [{ internalType: 'string', name: '', type: 'string' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'string', name: 'newMessage', type: 'string' }],
    name: 'update',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
]
const publicClient = createPublicClient({
  chain: sepolia,
  transport: custom(provider),
})

const walletClient = createWalletClient({
  chain: sepolia,
  transport: custom(provider),
})

const contractAddress = '0x8AA6820B3F197384874fAdb355361758258cb981' // On Sepolia, replace with your contract address
const address = await walletClient.getAddresses()

// Submit transaction to the blockchain
const hash = await walletClient.writeContract({
  account: address[0],
  address: contractAddress,
  abi: JSON.parse(JSON.stringify(contractABI)),
  functionName: 'update',
  args: ['NEW_MESSAGE'],
})

// Send transaction to smart contract to update message
const receipt = await publicClient.waitForTransactionReceipt({ hash })

```

```
import { createEVMClient } from '@metamask/connect-evm'
import { Web3 } from 'web3'

const evmClient = await createEVMClient({
  dapp: {
    name: 'MetaMask Connect EVM Example',
    url: window.location.href,
    iconUrl: 'https://mydapp.com/icon.png', // Optional
  },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      '0xaa36a7': 'https://sepolia.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})
const provider = evmClient.getProvider()

const web3 = new Web3(provider)

const contractABI = [
  {
    inputs: [{ internalType: 'string', name: 'initMessage', type: 'string' }],
    stateMutability: 'nonpayable',
    type: 'constructor',
  },
]
const contractAddress = '0x04cA407965D60C2B39d892a1DFB1d1d9C30d0334'
const contract = new web3.eth.Contract(contractABI, contractAddress)

// Send transaction to smart contract to update message
const tx = await contract.methods.update('NEW_MESSAGE').send({ from: signer.getAddress() })

// Wait for transaction to finish
const receipt = await tx.wait()

```

```
import { createEVMClient } from '@metamask/connect-evm'
import { ethers } from 'ethers'

const evmClient = await createEVMClient({
  dapp: {
    name: 'MetaMask Connect EVM Example',
    url: window.location.href,
    iconUrl: 'https://mydapp.com/icon.png', // Optional
  },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      '0xaa36a7': 'https://sepolia.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})
const provider = evmClient.getProvider()

const ethersProvider = new ethers.BrowserProvider(provider)
const signer = await ethersProvider.getSigner()

const contractABI = [
  {
    inputs: [{ internalType: 'string', name: 'initMessage', type: 'string' }],
    stateMutability: 'nonpayable',
    type: 'constructor',
  },
  {
    inputs: [],
    name: 'message',
    outputs: [{ internalType: 'string', name: '', type: 'string' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'string', name: 'newMessage', type: 'string' }],
    name: 'update',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
]
const contractAddress = '0x04cA407965D60C2B39d892a1DFB1d1d9C30d0334'
const contract = new ethers.Contract(
  contractAddress,
  JSON.parse(JSON.stringify(contractABI)),
  signer
)

// Send transaction to smart contract to update message
const tx = await contract.update('NEW_MESSAGE')

// Wait for transaction to finish
const receipt = await tx.wait()

```

```
import { createEVMClient } from '@metamask/connect-evm'

const evmClient = await createEVMClient({
  dapp: {
    name: 'MetaMask Connect EVM Example',
    url: window.location.href,
    iconUrl: 'https://mydapp.com/icon.png', // Optional
  },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      '0xaa36a7': 'https://sepolia.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})
const provider = evmClient.getProvider()

async function updateMessage(contractAddress, userAddress, newMessage) {
  try {
    const tx = await provider.request({
      method: 'update',
      params: [contractAddress, newMessage],
    })
    return tx
  } catch (error) {
    console.error('Error updating message:', error)
    throw error
  }
}

// Example usage
async function updateMessageExample() {
  const status = document.getElementById('status')
  try {
    const tx = await updateMessage('0xContractAddress', '0xUserAddress', 'NEW_MESSAGE')
    status.textContent = `Transaction: ${tx}`
  } catch (error) {
    status.textContent = `Error: ${error.message}`
  }
}

```

```
import { useWriteContract, useWaitForTransactionReceipt } from 'wagmi'

function MintNFT() {
  const { writeContract, data: hash, error, isPending } = useWriteContract()

  const { isLoading: isConfirming, isSuccess: isConfirmed } = useWaitForTransactionReceipt({
    hash,
  })

  function mint() {
    writeContract({
      address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
      abi: [
        {
          name: 'mint',
          type: 'function',
          stateMutability: 'nonpayable',
          inputs: [{ name: 'tokenId', type: 'uint256' }],
          outputs: [],
        },
      ],
      functionName: 'mint',
      args: [123n], // Token ID
    })
  }

  return (
    <div>
      <button onClick={mint} disabled={isPending || isConfirming}>
        {isPending ? 'Confirming...' : 'Mint NFT'}
      </button>

      {hash && (
        <div>
          Transaction Hash: {hash}
          {isConfirming && <div>Waiting for confirmation...</div>}
          {isConfirmed && <div>NFT Minted Successfully!</div>}
        </div>
      )}

      {error && <div>Error: {error.message}</div>}
    </div>
  )
}

```

## Best practices[​](#best-practices "Direct link to Best practices")

Follow these best practices when interacting with smart contracts.

### Contract validation[​](#contract-validation "Direct link to Contract validation")

- Always **verify contract addresses**.
- Double check **ABI correctness**.
- **Validate input data** before sending.
- Use **typed data** when possible (for example, using [Viem](https://viem.sh/)).

### Error handling[​](#error-handling "Direct link to Error handling")

- Handle [common errors](#common-errors) like **user rejection** and **contract reverts**.
- Provide **clear error messages** to users.
- Implement proper **error recovery** flows.
- Consider **gas estimation failures**.

### User experience[​](#user-experience "Direct link to User experience")

- Show **clear loading states**.
- Display **transaction progress**.
- Provide **confirmation feedback**.
- Enable proper **error recovery**.

## Common errors[​](#common-errors "Direct link to Common errors")

| Error code | Description                 | Solution                                                       |
| ---------- | --------------------------- | -------------------------------------------------------------- |
| 4001       | User rejected transaction   | Show a retry option and a clear error message.                 |
| -32000     | Invalid input               | Validate the input data before sending.                        |
| -32603     | Contract execution reverted | Check the contract conditions and handle the error gracefully. |
| -32002     | Request already pending     | Prevent multiple concurrent transactions.                      |

## Next steps[​](#next-steps "Direct link to Next steps")

See the following guides to add more functionality to your dapp:

- [Manage user accounts](/metamask-connect/evm/guides/manage-user-accounts/)
- [Manage networks](/metamask-connect/evm/guides/manage-networks/)
- [Send transactions](/metamask-connect/evm/guides/send-transactions/)
