viem Integration
Integrate EVM blockchains using viem for TypeScript/JavaScript applications.
Quick Decision Guide
Building... Use This
Node.js script/backend viem with http transport
React/Next.js frontend wagmi hooks (built on viem)
Real-time event monitoring viem with webSocket transport
Browser wallet integration wagmi or viem custom transport
Installation
Core library
npm install viem
For React apps, also install wagmi
npm install wagmi viem @tanstack/react-query
Core Concepts
Clients
viem uses two client types:
Client Purpose Example Use
PublicClient Read-only operations Get balances, read contracts, fetch logs
WalletClient Write operations Send transactions, sign messages
Transports
Transport Use Case
http()
Standard RPC calls (most common)
webSocket()
Real-time event subscriptions
custom()
Browser wallets (window.ethereum)
Chains
viem includes 50+ chain definitions. Import from viem/chains :
import { mainnet, arbitrum, optimism, base, polygon } from 'viem/chains';
Input Validation Rules
Before interpolating ANY user-provided value into generated TypeScript code:
-
Ethereum addresses: MUST match ^0x[a-fA-F0-9]{40}$ — use viem's isAddress() for validation
-
Chain IDs: MUST be from viem's supported chain definitions
-
Private keys: MUST NEVER be hardcoded — always use process.env.PRIVATE_KEY with runtime validation
-
RPC URLs: MUST use https:// or wss:// protocols only
-
ABI inputs: Validate types match expected Solidity types before encoding
Quick Start Examples
Read Balance
import { createPublicClient, http, formatEther } from 'viem'; import { mainnet } from 'viem/chains';
const client = createPublicClient({ chain: mainnet, transport: http(), });
const balance = await client.getBalance({ address: '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', });
console.log(Balance: ${formatEther(balance)} ETH);
Read Contract
import { createPublicClient, http, parseAbi } from 'viem'; import { mainnet } from 'viem/chains';
const client = createPublicClient({ chain: mainnet, transport: http(), });
const abi = parseAbi([ 'function balanceOf(address) view returns (uint256)', 'function decimals() view returns (uint8)', ]);
const balance = await client.readContract({ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC abi, functionName: 'balanceOf', args: ['0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'], });
Send Transaction
import { createWalletClient, http, parseEther } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { mainnet } from 'viem/chains';
const account = privateKeyToAccount(process.env.PRIVATE_KEY as 0x${string});
const client = createWalletClient({ account, chain: mainnet, transport: http(), });
const hash = await client.sendTransaction({ to: '0x...', value: parseEther('0.1'), });
console.log(Transaction hash: ${hash});
Write to Contract
import { createWalletClient, createPublicClient, http, parseAbi, parseUnits } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { mainnet } from 'viem/chains';
const account = privateKeyToAccount(process.env.PRIVATE_KEY as 0x${string});
const walletClient = createWalletClient({ account, chain: mainnet, transport: http(), });
const publicClient = createPublicClient({ chain: mainnet, transport: http(), });
const abi = parseAbi(['function transfer(address to, uint256 amount) returns (bool)']);
// Simulate first to catch errors const { request } = await publicClient.simulateContract({ address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', abi, functionName: 'transfer', args: ['0x...', parseUnits('100', 6)], account, });
// Execute the transaction const hash = await walletClient.writeContract(request);
// Wait for confirmation
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log(Confirmed in block ${receipt.blockNumber});
Reference Documentation
For deeper coverage of specific topics:
Topic Reference File
Client setup, transports, chains Clients & Transports
Reading blockchain data Reading Data
Sending transactions Writing Transactions
Private keys, HD wallets Accounts & Keys
ABI handling, multicall Contract Patterns
React/wagmi hooks Wagmi React
Related Plugins
Once you're comfortable with viem basics, the uniswap-trading plugin provides comprehensive Uniswap swap integration:
-
Uniswap Trading API integration
-
Universal Router SDK usage
-
Token swap implementations
Install it with: claude plugin add @uniswap/uniswap-trading
Common Utilities
Unit Conversion
import { parseEther, formatEther, parseUnits, formatUnits } from 'viem';
// ETH parseEther('1.5'); // 1500000000000000000n (wei) formatEther(1500000000000000000n); // "1.5"
// Tokens (e.g., USDC with 6 decimals) parseUnits('100', 6); // 100000000n formatUnits(100000000n, 6); // "100"
Address Utilities
import { getAddress, isAddress } from 'viem';
isAddress('0x...'); // true/false getAddress('0x...'); // checksummed address
Hashing
import { keccak256, toHex } from 'viem';
keccak256(toHex('hello')); // 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
Error Handling
viem throws typed errors that can be caught and handled:
import { ContractFunctionExecutionError, InsufficientFundsError } from 'viem'
try { await client.writeContract(...) } catch (error) { if (error instanceof ContractFunctionExecutionError) { console.error('Contract call failed:', error.shortMessage) } if (error instanceof InsufficientFundsError) { console.error('Not enough ETH for gas') } }
Resources
-
viem Documentation
-
wagmi Documentation
-
viem GitHub
-
wagmi GitHub