viem について
Ethereum と連携する TypeScript アプリを Viem 2.x で開発する人向けのスキルです。型安全なクライアント設定、コントラクトの読み書き、イベント監視、エラー対策を扱います。
主な機能
- 01パブリック・ウォレットクライアントの設定
- 02型安全な ABI の記述
- 03コントラクトの読み取りと書き込みシミュレーション
- 04コントラクトイベントの監視
- 05Multicall による一括読み取り
- 06エラー処理とチェーン設定
活用シーン
- 01読み取り専用またはウォレットクライアントの設定
- 02スマートコントラクトの読み書き
- 03コントラクトイベントの監視
- 04コントラクト読み取りの一括実行
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ 0xsardius/ onchain-typescript-skills --skill viemインストール後、Claude Code で/viemを入力して読み込まれたか確認します。
同梱ファイルreferences/common-errors.mdreferences/contract-patterns.md
詳細
Viem スキル
バージョン: Viem 2.x | 公式ドキュメント
Viem は、Ethereum 向けの最新の TypeScript インターフェースです。このスキルでは、コントラクトの操作、クライアントのセットアップ、型安全性に関する正しいパターンを紹介します。
クイックリファレンス
import { createPublicClient, createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'
重要なパターン
1. クライアントのセットアップ
Public Client(読み取り専用の操作):
const publicClient = createPublicClient({
chain: mainnet,
transport: http('https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY'),
})
Wallet Client(書き込み操作):
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 を使用してください。
// ✅ CORRECT - Full type safety
const abi = [
{
name: 'balanceOf',
type: 'function',
stateMutability: 'view',
inputs: [{ name: 'owner', type: 'address' }],
outputs: [{ name: '', type: 'uint256' }],
},
] as const
// ❌ WRONG - No type inference
const abi = [{ name: 'balanceOf', ... }] // Missing `as const`
3. コントラクトの読み取りパターン
const balance = await publicClient.readContract({
address: '0x...', // Contract address
abi,
functionName: 'balanceOf',
args: ['0x...'], // Args are fully typed when using `as const`
})
4. コントラクトの書き込みパターン(最初にシミュレーション!)
エラーを早期に検出するため、書き込みの前に必ずシミュレーションを実行してください。
// Step 1: Simulate
const { request } = await publicClient.simulateContract({
account,
address: '0x...',
abi,
functionName: 'transfer',
args: ['0x...', 1000000n], // Use BigInt for uint256
})
// Step 2: Execute
const hash = await walletClient.writeContract(request)
// Step 3: Wait for receipt
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)
}
},
})
// Clean up
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 を使う | 1000000n のような BigInt リテラルを使う |
| シミュレーションせずに書き込む | 必ず最初に simulateContract を実行する |
| ガスをハードコードする | viem に推定させるか、gas: await publicClient.estimateGas(...) を使う |
| レシートを待機しない | 確認のために waitForTransactionReceipt を使う |
チェーンの設定
import { mainnet, polygon, arbitrum, optimism, base } from 'viem/chains'
// Custom chain
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
// Handle specific revert reason
}
}
}
参考資料
詳しいパターンについては、以下を参照してください。
references/contract-patterns.md- 高度なコントラクト操作パターンreferences/common-errors.md- エラー処理とデバッグのガイド- Viem のドキュメント - 公式ドキュメント
- Viem の GitHub - ソースとリリース
コメント
Markdown 対応