关于 viem-integration
用于在 TypeScript 和 JavaScript 应用中通过 viem 集成 EVM 区块链的技能,介绍客户端与传输方式选择、链上读写,以及相关 wagmi 用法。
主要功能
- 01配置 PublicClient 和 WalletClient
- 02HTTP、WebSocket 与自定义传输
- 03链和账户管理
- 04读取与写入合约
- 05地址、单位和错误处理工具
适用场景
- 01读取余额和合约数据
- 02发送交易或签署消息
- 03监控实时区块链事件
- 04为 React 应用接入 EVM
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ uniswap/ uniswap-ai --skill viem-integration安装后在 Claude Code 中输入/viem-integration确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/accounts-and-keys.mdclients-and-transports.mdcontract-patterns.mdreading-data.mdwagmi-react.mdwriting-transactions.md详细介绍
viem 集成
使用 viem 为 TypeScript/JavaScript 应用集成 EVM 区块链。
快速选择指南
| 正在构建…… | 使用此方案 |
|---|---|
| Node.js 脚本/后端 | 使用 http 传输的 viem |
| React/Next.js 前端 | wagmi 钩子(基于 viem 构建) |
| 实时事件监控 | 使用 webSocket 传输的 viem |
| 浏览器钱包集成 | wagmi 或 viem 自定义传输 |
安装
# Core library
npm install viem
# For React apps, also install wagmi
npm install wagmi viem @tanstack/react-query
核心概念
客户端
viem 使用两种客户端类型:
| 客户端 | 用途 | 示例用途 |
|---|---|---|
| PublicClient | 只读操作 | 获取余额、读取合约、获取日志 |
| WalletClient | 写入操作 | 发送交易、签署消息 |
传输方式
| 传输方式 | 使用场景 |
|---|---|
http() | 标准 RPC 调用(最常见) |
webSocket() | 实时事件订阅 |
custom() | 浏览器钱包(window.ethereum) |
链
viem 内置了 50 多种链定义。可从 viem/chains 导入:
import { mainnet, arbitrum, optimism, base, polygon } from 'viem/chains';
输入验证规则
在将任何用户提供的值插入生成的 TypeScript 代码之前:
- 以太坊地址:必须匹配
^0x[a-fA-F0-9]{40}$— 使用 viem 的isAddress()进行验证 - 链 ID:必须来自 viem 支持的链定义
- 私钥:绝不能硬编码 — 始终使用
process.env.PRIVATE_KEY并在运行时进行验证 - RPC URL:只能使用
https://或wss://协议 - ABI 输入:编码前,验证类型是否与预期的 Solidity 类型匹配
快速入门示例
读取余额
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`);
读取合约
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'],
});
发送交易
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}`);
写入合约
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}`);
参考文档
如需深入了解特定主题:
| 主题 | 参考文件 |
|---|---|
| 客户端设置、传输方式、链 | 客户端与传输方式 |
| 读取区块链数据 | 读取数据 |
| 发送交易 | 写入交易 |
| 私钥、HD 钱包 | 账户与密钥 |
| ABI 处理、multicall | 合约模式 |
| React/wagmi 钩子 | Wagmi React |
相关插件
熟悉 viem 基础知识后,uniswap-trading 插件可提供全面的 Uniswap 兑换集成:
- Uniswap Trading API 集成
- Universal Router SDK 的使用
- 代币兑换实现
使用以下命令安装:claude plugin add @uniswap/uniswap-trading
常用工具函数
单位转换
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"
地址工具函数
import { getAddress, isAddress } from 'viem';
isAddress('0x...'); // true/false
getAddress('0x...'); // checksummed address
哈希
import { keccak256, toHex } from 'viem';
keccak256(toHex('hello')); // 0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8
错误处理
viem 会抛出带类型的错误,可捕获并处理:
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')
}
}
资源
评论
支持 Markdown