--- name: wallet-integration description: Use when integrating wallets into dApps. Covers WalletConnect, injected providers, EIP-1193, account abstraction (EIP-4337, EIP-7702), Safe SDK, multi-sig interaction, hardware wallets, and network switching. --- # Wallet Integration Patterns ## EIP-1193 Provider Standard All wallets expose a standard provider interface: ```typescript interface EIP1193Provider { request(args: { method: string; params?: unknown[] }): Promise; on(event: string, listener: (...args: any[]) => void): void; removeListener(event: string, listener: (...args: any[]) => void): void; } // Common methods: await provider.request({ method: 'eth_requestAccounts' }); await provider.request({ method: 'eth_chainId' }); await provider.request({ method: 'personal_sign', params: [message, address] }); await provider.request({ method: 'eth_sendTransaction', params: [txParams] }); ``` ## Wallet Connection with RainbowKit ```tsx import { ConnectButton } from '@rainbow-me/rainbowkit'; function Header() { return ( ); } ``` Custom connect button: ```tsx import { ConnectButton } from '@rainbow-me/rainbowkit'; function CustomConnect() { return ( {({ account, chain, openAccountModal, openChainModal, openConnectModal, mounted }) => { const connected = mounted && account && chain; if (!connected) { return ; } if (chain.unsupported) { return ; } return (
); }}
); } ``` ## WalletConnect v2 ```typescript import { walletConnect } from 'wagmi/connectors'; const wcConnector = walletConnect({ projectId: process.env.NEXT_PUBLIC_WC_PROJECT_ID!, metadata: { name: 'My dApp', description: 'A great dApp', url: 'https://mydapp.xyz', icons: ['https://mydapp.xyz/icon.png'], }, showQrModal: true, }); ``` ## Injected Wallet Detection ```typescript import { injected } from 'wagmi/connectors'; import { useConnect } from 'wagmi'; function WalletList() { const { connectors, connect } = useConnect(); return (
{connectors.map((connector) => ( ))}
); } ``` ## Network Switching (EIP-3085) ```typescript import { useSwitchChain } from 'wagmi'; function NetworkSwitch() { const { chains, switchChain, isPending } = useSwitchChain(); return (
{chains.map((chain) => ( ))}
); } // Add custom chain if not recognized async function addChain(provider: EIP1193Provider) { await provider.request({ method: 'wallet_addEthereumChain', params: [{ chainId: '0x2105', // Base (8453) chainName: 'Base', nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 }, rpcUrls: ['https://mainnet.base.org'], blockExplorerUrls: ['https://basescan.org'], }], }); } ``` ## Account Abstraction (EIP-4337) ```typescript import { createSmartAccountClient } from 'permissionless'; import { signerToSimpleSmartAccount } from 'permissionless/accounts'; import { createPimlicoClient } from 'permissionless/clients/pimlico'; import { createPublicClient, http } from 'viem'; import { base } from 'viem/chains'; const publicClient = createPublicClient({ chain: base, transport: http() }); const pimlicoClient = createPimlicoClient({ transport: http(`https://api.pimlico.io/v2/base/rpc?apikey=${PIMLICO_KEY}`), entryPoint: { address: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', version: '0.7' }, }); const smartAccount = await signerToSimpleSmartAccount(publicClient, { signer: walletClientSigner, entryPoint: { address: '0x0000000071727De22E5E9d8BAf0edAc6f37da032', version: '0.7' }, }); const smartAccountClient = createSmartAccountClient({ account: smartAccount, chain: base, bundlerTransport: http(`https://api.pimlico.io/v2/base/rpc?apikey=${PIMLICO_KEY}`), paymaster: pimlicoClient, }); // Gasless transaction const hash = await smartAccountClient.sendTransaction({ to: '0xRecipient', value: 0n, data: '0x...', }); ``` ## Safe (Multi-Sig) Integration ```typescript import SafeApiKit from '@safe-global/api-kit'; import Safe from '@safe-global/protocol-kit'; const apiKit = new SafeApiKit({ chainId: 8453n }); // Propose a transaction const safeSdk = await Safe.init({ provider: rpcUrl, signer: signerPrivateKey, safeAddress: '0xSafeAddress', }); const safeTransaction = await safeSdk.createTransaction({ transactions: [{ to: '0xToken', data: encodeFunctionData({ abi: erc20Abi, functionName: 'transfer', args: [recipient, amount] }), value: '0', }], }); const safeTxHash = await safeSdk.getTransactionHash(safeTransaction); const signature = await safeSdk.signHash(safeTxHash); await apiKit.proposeTransaction({ safeAddress: '0xSafeAddress', safeTransactionData: safeTransaction.data, safeTxHash, senderAddress: signerAddress, senderSignature: signature.data, }); ``` ## Connection State Management ```tsx import { useAccount, useBalance, useDisconnect } from 'wagmi'; function WalletStatus() { const { address, isConnected, chain, connector } = useAccount(); const { data: balance } = useBalance({ address }); const { disconnect } = useDisconnect(); if (!isConnected) return

Not connected

; return (

Address: {address}

Chain: {chain?.name}

Wallet: {connector?.name}

Balance: {balance?.formatted} {balance?.symbol}

); } ``` ## Wallet Connection Flow 1. User clicks "Connect Wallet" 2. Modal shows available wallets (injected, WalletConnect, Coinbase) 3. User selects wallet → `eth_requestAccounts` 4. Check chain → `wallet_switchEthereumChain` if wrong 5. Store connection (wagmi handles persistence) 6. Listen for `accountsChanged` and `chainChanged` events 7. Handle disconnection gracefully ## Security Considerations - Validate `chainId` before every transaction - Don't trust `msg.sender` from wallet (user can spoof in dApp context) - Sign typed data (EIP-712) instead of raw messages when possible - Display human-readable transaction details before signing - Handle rejected transactions gracefully (user cancelled) - Clear sensitive state on disconnect - Validate addresses before sending (checksummed, not zero)