用途
Viem 是面向以太坊的现代 TypeScript 接口。本技能用于确保在使用 Viem 进行底层 EVM 区块链交互时采用正确的模式,包括合约交互、客户端设置和类型安全。适用于编写读取或写入区块链的 Node 脚本、CLI 工具或后端服务。
版本:Viem 2.x。
主要能力
- 客户端设置:公共客户端(只读操作)与钱包客户端(写操作)。
- ABI 类型安全:使用
as const获得完整类型推断。 - 合约读取模式。
- 合约写入模式(先模拟再执行)。
- 事件监听。
- 使用 Multicall 进行批量读取。
- 链配置(内置链与自定义链)。
- 错误处理。
使用方式
快速参考
import { createPublicClient, createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'
1. 客户端设置
公共客户端(只读操作):
const publicClient = createPublicClient({
chain: mainnet,
transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'),
})
钱包客户端(写操作):
const account = privateKeyToAccount('0x...')
const walletClient = createWalletClient({
account,
chain: mainnet,
transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'),
})
2. ABI 类型安全(关键)
始终对 ABI 使用 as const 以获得完整类型推断:
// ✅ 正确 - 完整类型安全
const abi = [
{
name: 'balanceOf',
type: 'function',
stateMutability: 'view',
inputs: [{ name: 'owner', type: 'address' }],
outputs: [{ name: '', type: 'uint256' }],
},
] as const
// ❌ 错误 - 无类型推断
const abi = [{ name: 'balanceOf', ... }] // 缺少 `as const`
3. 合约读取模式
const balance = await publicClient.readContract({
address: '0x...', // 合约地址
abi,
functionName: 'balanceOf',
args: ['0x...'], // 使用 `as const` 时参数完全类型化
})
4. 合约写入模式(先模拟!)
写入前始终先模拟,以便尽早捕获错误:
// 第 1 步:模拟
const { request } = await publicClient.simulateContract({
account,
address: '0x...',
abi,
functionName: 'transfer',
args: ['0x...', 1000000n], // uint256 使用 BigInt
})
// 第 2 步:执行
const hash = await walletClient.writeContract(request)
// 第 3 步:等待回执
const receipt = await publicClient.waitForTransactionReceipt({ hash })
5. 事件监听
const unwatch = publicClient.watchContractEvent({
address: '0x...',
abi,
eventName: 'Transfer',
onLogs: (logs) => {
for (const log of logs) {
console.log(log.args.from, log.args.to, log.args.value)
}
},
})
// 清理
unwatch()
6. 使用 Multicall 进行批量读取
const results = await publicClient.multicall({
contracts: [
{ address: '0x...', abi, functionName: 'balanceOf', args: ['0x...'] },
{ address: '0x...', abi, functionName: 'totalSupply' },
],
})
// results[0].result, results[1].result
常见错误
| 错误 | 修复 |
|---|---|
ABI 缺少 as const |
添加 as const 以获得类型推断 |
| 使用 Number 表示金额 | 使用 BigInt 字面量:1000000n |
| 未模拟就写入 | 始终先 simulateContract |
| 硬编码 gas | 让 viem 估算,或使用 gas: await publicClient.estimateGas(...) |
| 未等待回执 | 使用 waitForTransactionReceipt 确认 |
链配置
import { mainnet, polygon, arbitrum, optimism, base } from 'viem/chains'
// 自定义链
const customChain = {
id: 123,
name: 'My Chain',
nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
rpcUrls: {
default: { http: ['https://rpc.mychain.com'] },
},
}
错误处理
import { BaseError, ContractFunctionRevertedError } from 'viem'
try {
await publicClient.simulateContract({ ... })
} catch (err) {
if (err instanceof BaseError) {
const revertError = err.walk(e => e instanceof ContractFunctionRevertedError)
if (revertError instanceof ContractFunctionRevertedError) {
const errorName = revertError.data?.errorName
// 处理特定 revert 原因
}
}
}
参考
详细模式请参见:
references/contract-patterns.md- 高级合约交互模式references/common-errors.md- 错误处理与调试指南- Viem 文档 - 官方文档
- Viem GitHub - 源码与发布
适用场景
适用于编写读取或写入 EVM 区块链的 Node 脚本、CLI 工具或后端服务,尤其是需要合约交互、类型安全和正确客户端设置的 TypeScript 项目。
限制
资料未提供关于收费、价格、平台支持范围或性能效果的具体信息。