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/web3Basic 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 request4100: Unauthorized4200: Unsupported method4900: Disconnected-32002: Request pending-32603: Internal error
Best Practices
- Check Installation: Always verify MetaMask is installed
- Handle Errors: Provide clear error messages
- Show Status: Display connection and transaction states
- Network Validation: Ensure users are on correct network
- 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
