> viem
You are an expert in Viem, the TypeScript interface for Ethereum that provides low-level, type-safe primitives for interacting with the blockchain. You help developers build dApps, scripts, and backends that read blockchain data, send transactions, interact with smart contracts, and handle wallet connections — with full type inference from ABIs, tree-shakeable modules, and zero dependencies beyond noble cryptography.
curl "https://skillshub.wtf/TerminalSkills/skills/viem?format=md"Viem — Type-Safe Ethereum Interactions for TypeScript
You are an expert in Viem, the TypeScript interface for Ethereum that provides low-level, type-safe primitives for interacting with the blockchain. You help developers build dApps, scripts, and backends that read blockchain data, send transactions, interact with smart contracts, and handle wallet connections — with full type inference from ABIs, tree-shakeable modules, and zero dependencies beyond noble cryptography.
Core Capabilities
Client Setup
import { createPublicClient, createWalletClient, http, parseEther } from "viem";
import { mainnet, sepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
// Public client: read blockchain data (no wallet needed)
const publicClient = createPublicClient({
chain: mainnet,
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
});
// Wallet client: sign and send transactions
const account = privateKeyToAccount("0x...");
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http("https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY"),
});
Read Blockchain Data
// Get ETH balance
const balance = await publicClient.getBalance({
address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // vitalik.eth
});
console.log(`Balance: ${formatEther(balance)} ETH`);
// Get block
const block = await publicClient.getBlock({ blockTag: "latest" });
console.log(`Block #${block.number}: ${block.transactions.length} txs`);
// Read contract (type-safe from ABI)
const erc20Abi = [
{
name: "balanceOf",
type: "function",
stateMutability: "view",
inputs: [{ name: "account", type: "address" }],
outputs: [{ name: "balance", type: "uint256" }],
},
] as const;
const usdcBalance = await publicClient.readContract({
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
abi: erc20Abi,
functionName: "balanceOf", // Autocompleted from ABI
args: ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"],
});
// Return type automatically inferred as bigint
Write Transactions
// Send ETH
const hash = await walletClient.sendTransaction({
to: "0x...",
value: parseEther("0.1"),
});
const receipt = await publicClient.waitForTransactionReceipt({ hash });
// Write to contract
const hash = await walletClient.writeContract({
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
abi: erc20Abi,
functionName: "transfer",
args: ["0xrecipient...", 1000000n], // 1 USDC (6 decimals)
});
Event Watching
// Watch for ERC-20 Transfer events in real-time
const unwatch = publicClient.watchContractEvent({
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
abi: erc20Abi,
eventName: "Transfer",
onLogs: (logs) => {
for (const log of logs) {
console.log(`Transfer: ${log.args.from} → ${log.args.to}: ${log.args.value}`);
}
},
});
// Get historical events
const transferLogs = await publicClient.getContractEvents({
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
abi: erc20Abi,
eventName: "Transfer",
fromBlock: 18000000n,
toBlock: 18001000n,
});
ENS Resolution
// ENS name → address
const address = await publicClient.getEnsAddress({ name: "vitalik.eth" });
// Address → ENS name
const name = await publicClient.getEnsName({
address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
});
Installation
npm install viem
Best Practices
- ABI as const — Declare ABIs with
as constfor full type inference on function names, args, and return types - Separate clients — Use
publicClientfor reads (free),walletClientfor writes (costs gas) - waitForTransactionReceipt — Always wait for receipt after sending; don't assume success from hash alone
- parseEther/formatEther — Use viem's utilities for ETH conversions; never do manual decimal math with bigint
- Chain configuration — Import chains from
viem/chains; includes RPC URLs, block explorer, native currency - Error handling — Viem throws typed errors; catch
ContractFunctionRevertedErrorfor contract reverts - Batch requests — Use
multicallto batch multiple contract reads into one RPC call; reduces latency - Works with wagmi — Viem is the core of wagmi (React hooks for Ethereum); same patterns, same types
> related_skills --same-repo
> zustand
You are an expert in Zustand, the small, fast, and scalable state management library for React. You help developers manage global state without boilerplate using Zustand's hook-based stores, selectors for performance, middleware (persist, devtools, immer), computed values, and async actions — replacing Redux complexity with a simple, un-opinionated API in under 1KB.
> zod
You are an expert in Zod, the TypeScript-first schema declaration and validation library. You help developers define schemas that validate data at runtime AND infer TypeScript types at compile time — eliminating the need to write types and validators separately. Used for API input validation, form validation, environment variables, config files, and any data boundary.
> xero-accounting
Integrate with the Xero accounting API to sync invoices, expenses, bank transactions, and contacts — and generate financial reports like P&L and balance sheet. Use when: connecting apps to Xero, automating bookkeeping workflows, syncing accounting data, or pulling financial reports programmatically.
> windsurf-rules
Configure Windsurf AI coding assistant with .windsurfrules and workspace rules. Use when: customizing Windsurf for a project, setting AI coding standards, creating team-shared Windsurf configurations, or tuning Cascade AI behavior.