$npx -y skills add Uniswap/uniswap-ai --skill viem-integrationIntegrate EVM blockchains using viem. Use when user says "read blockchain data", "send transaction", "interact with smart contract", "connect to Ethereum", "use viem", "use wagmi", "wallet integration", "viem setup", or mentions blockchain/EVM development with TypeScript.
| 1 | # viem Integration |
| 2 | |
| 3 | Integrate EVM blockchains using viem for TypeScript/JavaScript applications. |
| 4 | |
| 5 | ## Quick Decision Guide |
| 6 | |
| 7 | | Building... | Use This | |
| 8 | | -------------------------- | ------------------------------ | |
| 9 | | Node.js script/backend | viem with http transport | |
| 10 | | React/Next.js frontend | wagmi hooks (built on viem) | |
| 11 | | Real-time event monitoring | viem with webSocket transport | |
| 12 | | Browser wallet integration | wagmi or viem custom transport | |
| 13 | |
| 14 | ## Installation |
| 15 | |
| 16 | ```bash |
| 17 | # Core library |
| 18 | npm install viem |
| 19 | |
| 20 | # For React apps, also install wagmi |
| 21 | npm install wagmi viem @tanstack/react-query |
| 22 | ``` |
| 23 | |
| 24 | ## Core Concepts |
| 25 | |
| 26 | ### Clients |
| 27 | |
| 28 | viem uses two client types: |
| 29 | |
| 30 | | Client | Purpose | Example Use | |
| 31 | | ---------------- | -------------------- | ---------------------------------------- | |
| 32 | | **PublicClient** | Read-only operations | Get balances, read contracts, fetch logs | |
| 33 | | **WalletClient** | Write operations | Send transactions, sign messages | |
| 34 | |
| 35 | ### Transports |
| 36 | |
| 37 | | Transport | Use Case | |
| 38 | | ------------- | --------------------------------- | |
| 39 | | `http()` | Standard RPC calls (most common) | |
| 40 | | `webSocket()` | Real-time event subscriptions | |
| 41 | | `custom()` | Browser wallets (window.ethereum) | |
| 42 | |
| 43 | ### Chains |
| 44 | |
| 45 | viem includes 50+ chain definitions. Import from `viem/chains`: |
| 46 | |
| 47 | ```typescript |
| 48 | import { mainnet, arbitrum, optimism, base, polygon } from 'viem/chains'; |
| 49 | ``` |
| 50 | |
| 51 | --- |
| 52 | |
| 53 | ## Input Validation Rules |
| 54 | |
| 55 | Before interpolating ANY user-provided value into generated TypeScript code: |
| 56 | |
| 57 | - **Ethereum addresses**: MUST match `^0x[a-fA-F0-9]{40}$` — use viem's `isAddress()` for validation |
| 58 | - **Chain IDs**: MUST be from viem's supported chain definitions |
| 59 | - **Private keys**: MUST NEVER be hardcoded — always use `process.env.PRIVATE_KEY` with runtime validation |
| 60 | - **RPC URLs**: MUST use `https://` or `wss://` protocols only |
| 61 | - **ABI inputs**: Validate types match expected Solidity types before encoding |
| 62 | |
| 63 | ## Quick Start Examples |
| 64 | |
| 65 | ### Read Balance |
| 66 | |
| 67 | ```typescript |
| 68 | import { createPublicClient, http, formatEther } from 'viem'; |
| 69 | import { mainnet } from 'viem/chains'; |
| 70 | |
| 71 | const client = createPublicClient({ |
| 72 | chain: mainnet, |
| 73 | transport: http(), |
| 74 | }); |
| 75 | |
| 76 | const balance = await client.getBalance({ |
| 77 | address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', |
| 78 | }); |
| 79 | |
| 80 | console.log(`Balance: ${formatEther(balance)} ETH`); |
| 81 | ``` |
| 82 | |
| 83 | ### Read Contract |
| 84 | |
| 85 | ```typescript |
| 86 | import { createPublicClient, http, parseAbi } from 'viem'; |
| 87 | import { mainnet } from 'viem/chains'; |
| 88 | |
| 89 | const client = createPublicClient({ |
| 90 | chain: mainnet, |
| 91 | transport: http(), |
| 92 | }); |
| 93 | |
| 94 | const abi = parseAbi([ |
| 95 | 'function balanceOf(address) view returns (uint256)', |
| 96 | 'function decimals() view returns (uint8)', |
| 97 | ]); |
| 98 | |
| 99 | const balance = await client.readContract({ |
| 100 | address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC |
| 101 | abi, |
| 102 | functionName: 'balanceOf', |
| 103 | args: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'], |
| 104 | }); |
| 105 | ``` |
| 106 | |
| 107 | ### Send Transaction |
| 108 | |
| 109 | ```typescript |
| 110 | import { createWalletClient, http, parseEther } from 'viem'; |
| 111 | import { privateKeyToAccount } from 'viem/accounts'; |
| 112 | import { mainnet } from 'viem/chains'; |
| 113 | |
| 114 | const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); |
| 115 | |
| 116 | const client = createWalletClient({ |
| 117 | account, |
| 118 | chain: mainnet, |
| 119 | transport: http(), |
| 120 | }); |
| 121 | |
| 122 | const hash = await client.sendTransaction({ |
| 123 | to: '0x...', |
| 124 | value: parseEther('0.1'), |
| 125 | }); |
| 126 | |
| 127 | console.log(`Transaction hash: ${hash}`); |
| 128 | ``` |
| 129 | |
| 130 | ### Write to Contract |
| 131 | |
| 132 | ```typescript |
| 133 | import { createWalletClient, createPublicClient, http, parseAbi, parseUnits } from 'viem'; |
| 134 | import { privateKeyToAccount } from 'viem/accounts'; |
| 135 | import { mainnet } from 'viem/chains'; |
| 136 | |
| 137 | const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`); |
| 138 | |
| 139 | const walletClient = createWalletClient({ |
| 140 | account, |
| 141 | chain: mainnet, |
| 142 | transport: http(), |
| 143 | }); |
| 144 | |
| 145 | const publicClient = createPublicClient({ |
| 146 | chain: mainnet, |
| 147 | transport: http(), |
| 148 | }); |
| 149 | |
| 150 | const abi = parseAbi(['function transfer(address to, uint256 amount) returns (bool)']); |
| 151 | |
| 152 | // Simulate first to catch errors |
| 153 | const { request } = await publicClient.simulateContract({ |
| 154 | address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', |
| 155 | abi, |
| 156 | functionName: 'transfer', |
| 157 | args: ['0x...', parseUnits('100', 6)], |
| 158 | account, |
| 159 | }); |
| 160 | |
| 161 | // Execute the transaction |
| 162 | const hash = await walletClient.writeContract(request); |
| 163 | |
| 164 | // Wait for confirmation |
| 165 | const receipt = await publicClient.waitForTransactionReceipt({ |