viem-integration
Integrate 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.
Overview
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-queryCore 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'sisAddress()for validation - Chain IDs: MUST be from viem's supported chain definitions
- Private keys: MUST NEVER be hardcoded — always use
process.env.PRIVATE_KEYwith runtime validation - RPC URLs: MUST use
https://orwss://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 addressHashing
import { keccak256, toHex } from 'viem';
keccak256(toHex('hello')); // 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8Error 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
Files 7 files
Install it by hand
Deska runs this for you, for every agent you pick. To run it yourself in a terminal:
npx skills add uniswap/uniswap-ai --skill viem-integrationMore from uniswap
- configuratoruniswapConfigure CCA (Continuous Clearing Auction) smart contract parameters through an interactive bulk form flow. Use when user says "configure auction", "cca auction", "setup token auction", "auction configuration", "continuous auction", or mentions CCA contracts.1 installs on skills.sh0
- copy-tradeuniswapThis skill should be used when the user asks to "copy trades from" a wallet, "mirror a wallet", "follow this address", set up "copy trading", "track and replicate a trader", or mirror another account's swaps bounded by guardrails. Watches a target wallet and mirrors its trades, filtered by chain, asset match, position size, and the follower's own portfolio state.1 installs on skills.sh0
- dca-botuniswapThis skill should be used when the user wants to "dca into" a token, "buy X every day", set up a "recurring buy", "dollar cost average" into an asset, "schedule a buy", or "auto-buy on a dip". Buys a fixed amount into a token on a schedule, optionally only when a condition holds (for example only when ETH is below a price threshold). The host agent's scheduler wakes the skill on a cadence; each wake is one self-contained run.1 installs on skills.sh0
- deployeruniswapDeploy CCA (Continuous Clearing Auction) smart contracts using the Factory pattern. Use when user says "deploy auction", "deploy cca", "factory deployment", or wants to deploy a configured auction.1 installs on skills.sh0
- index-botuniswapThis skill should be used when the user asks to "create an index", "build a basket of top assets", "buy a weighted basket", "make a portfolio of assets", "equal-weight basket", "rebalance my portfolio", "track the top N tokens", or wants an automated, weighted multi-asset basket that buys in one pass and rebalances on a cadence. Builds the basket spec, delegates each buy and rebalance swap to the swap-integration Trading API flow, and records target weights in state.1 installs on skills.sh0