Skip to content

MetaMask Integration ​

Connect your Civra application to MetaMask for Web3 functionality.

Overview ​

MetaMask is the most popular Ethereum wallet, allowing users to interact with your dApp directly from their browser.

Quick Start ​

Civra provides built-in MetaMask integration:

typescript
import { WalletButton } from '@civra/web3'

export function App() {
  return <WalletButton provider="metamask" />
}

Installation ​

MetaMask integration comes pre-configured in Civra projects. For custom setup:

bash
npm install @civra/web3

Basic Usage ​

Connect Wallet ​

typescript
import { useWallet } from '@civra/web3'

export function ConnectButton() {
  const { connect, isConnecting } = useWallet()

  return (
    <button onClick={() => connect('metamask')} disabled={isConnecting}>
      {isConnecting ? 'Connecting...' : 'Connect MetaMask'}
    </button>
  )
}

Get Wallet Info ​

typescript
import { useWallet } from '@civra/web3'

export function WalletInfo() {
  const { address, isConnected, balance } = useWallet()

  if (!isConnected) return <div>Not connected</div>

  return (
    <div>
      <p>Address: {address}</p>
      <p>Balance: {balance} ETH</p>
    </div>
  )
}

Disconnect ​

typescript
const { disconnect } = useWallet()

<button onClick={disconnect}>Disconnect</button>

Advanced Features ​

Check if MetaMask is Installed ​

typescript
import { checkMetaMaskInstalled } from '@civra/web3'

export function MetaMaskCheck() {
  const isInstalled = checkMetaMaskInstalled()

  if (!isInstalled) {
    return (
      <div>
        <p>MetaMask not detected</p>
        <a href="https://metamask.io" target="_blank">
          Install MetaMask
        </a>
      </div>
    )
  }

  return <WalletButton provider="metamask" />
}

Switch Network ​

typescript
import { useSwitchNetwork } from '@civra/web3'

export function NetworkSwitcher() {
  const { switchNetwork } = useSwitchNetwork()

  return (
    <div>
      <button onClick={() => switchNetwork(1)}>
        Ethereum Mainnet
      </button>
      <button onClick={() => switchNetwork(137)}>
        Polygon
      </button>
      <button onClick={() => switchNetwork(10)}>
        Optimism
      </button>
    </div>
  )
}

Add Network ​

typescript
import { addNetwork } from '@civra/web3'

const addPolygon = async () => {
  await addNetwork({
    chainId: 137,
    chainName: 'Polygon Mainnet',
    nativeCurrency: {
      name: 'MATIC',
      symbol: 'MATIC',
      decimals: 18
    },
    rpcUrls: ['https://polygon-rpc.com'],
    blockExplorerUrls: ['https://polygonscan.com']
  })
}

Add Token to MetaMask ​

typescript
import { addToken } from '@civra/web3'

const addCustomToken = async () => {
  await addToken({
    address: '0x...',
    symbol: 'MTK',
    decimals: 18,
    image: 'https://...'
  })
}

Signing Messages ​

Sign Message ​

typescript
import { useSignMessage } from '@civra/web3'

export function SignMessage() {
  const { signMessage, signature } = useSignMessage()

  const handleSign = async () => {
    const sig = await signMessage('Hello from Civra!')
    console.log('Signature:', sig)
  }

  return <button onClick={handleSign}>Sign Message</button>
}

Verify Signature ​

typescript
import { verifyMessage } from '@civra/web3'

const isValid = verifyMessage({
  message: 'Hello from Civra!',
  signature: '0x...',
  address: '0x...'
})

Transactions ​

Send Transaction ​

typescript
import { useSendTransaction } from '@civra/web3'

export function SendETH() {
  const { sendTransaction } = useSendTransaction()

  const handleSend = async () => {
    const tx = await sendTransaction({
      to: '0x...',
      value: parseEther('0.1')
    })
    await tx.wait()
  }

  return <button onClick={handleSend}>Send 0.1 ETH</button>
}

Contract Interaction ​

typescript
import { useContract } from '@civra/web3'

const contract = useContract(CONTRACT_ADDRESS, ABI)

// Read
const balance = await contract.read.balanceOf(address)

// Write
const tx = await contract.write.transfer(to, amount)
await tx.wait()

Events ​

Listen for Account Changes ​

typescript
import { useEffect } from 'react'
import { onAccountsChanged } from '@civra/web3'

useEffect(() => {
  const unsubscribe = onAccountsChanged((accounts) => {
    console.log('Account changed:', accounts[0])
  })

  return unsubscribe
}, [])

Listen for Network Changes ​

typescript
import { onChainChanged } from '@civra/web3'

useEffect(() => {
  const unsubscribe = onChainChanged((chainId) => {
    console.log('Network changed:', chainId)
  })

  return unsubscribe
}, [])

Error Handling ​

typescript
import { useWallet } from '@civra/web3'

export function SafeConnect() {
  const { connect } = useWallet()

  const handleConnect = async () => {
    try {
      await connect('metamask')
    } catch (error) {
      if (error.code === 4001) {
        // User rejected
        console.log('Connection rejected')
      } else if (error.code === -32002) {
        // Request pending
        console.log('Please check MetaMask')
      } else {
        console.error('Connection error:', error)
      }
    }
  }

  return <button onClick={handleConnect}>Connect</button>
}

Common Error Codes ​

  • 4001: User rejected the request
  • 4100: Unauthorized
  • 4200: Unsupported method
  • 4900: Disconnected
  • -32002: Request pending
  • -32603: Internal error

Best Practices ​

  1. Check Installation: Always verify MetaMask is installed
  2. Handle Errors: Provide clear error messages
  3. Show Status: Display connection and transaction states
  4. Network Validation: Ensure users are on correct network
  5. Mobile Support: Add WalletConnect for mobile users

Mobile Considerations ​

For mobile users without MetaMask:

typescript
import { isMobile } from '@civra/web3'

export function WalletConnect() {
  if (isMobile() && !checkMetaMaskInstalled()) {
    return <WalletConnectButton />
  }

  return <MetaMaskButton />
}

Testing ​

Use MetaMask's test networks:

  • Sepolia (Ethereum testnet)
  • Mumbai (Polygon testnet)
  • Goerli (deprecated, use Sepolia)

Get testnet ETH from faucets:

Security ​

Never Request Private Keys ​

typescript
// ❌ NEVER DO THIS
const privateKey = prompt('Enter private key')

// ✅ Always use wallet signing
const signature = await signMessage(message)

Validate Inputs ​

typescript
import { isAddress } from '@civra/web3'

if (!isAddress(recipientAddress)) {
  throw new Error('Invalid address')
}

Resources ​

Support ​

Having issues?

  • Check MetaMask is unlocked
  • Verify correct network
  • Clear browser cache
  • Try incognito mode
  • Contact Support

Built with Civra - Web3 Development Platform