SuperAPI AI 工具集

viem

编程与开发

查看项目

更新于 10-15 08:50

技能介绍

用途

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 项目。

限制

资料未提供关于收费、价格、平台支持范围或性能效果的具体信息。