viem-v2 について
viem-v2は、TypeScript/JavaScriptアプリにEVMブロックチェーンを統合するためのガイドで、クライアント、トランスポート、チェーン、読み取り、書き込みを扱います。スクリプト、バックエンド、フロントエンドの開発者向けに、viemとwagmiの使い分けも説明します。
主な機能
- 01PublicClientとWalletClientの利用
- 02HTTP、WebSocket、カスタムトランスポート
- 03チェーンデータとコントラクトの読み取り
- 04トランザクション送信とメッセージ署名
- 05アドレス、単位変換、ハッシュのユーティリティ
活用シーン
- 01Node.jsスクリプトやバックエンドでのチェーンデータ取得
- 02リアルタイムのブロックチェーンイベント監視
- 03トランザクション送信やコントラクト書き込み
- 04アドレスや署名関連入力の検証
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ blockmatic/ basilic-skills --skill viem-v2インストール後、Claude Code で/viem-v2を入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/accounts-and-keys.mdclients-and-transports.mdcontract-patterns.mdreading-data.mdwagmi-react.mdwriting-transactions.md詳細
viem 統合
viem を使用して、TypeScript/JavaScript アプリケーションに EVM ブロックチェーンを統合します。
ウォレット UI: React のウォレット接続とフックについては、wagmi-v3 を参照してください。まだウォレット UI がないアプリに wagmi を追加しないでください。バックエンド、スクリプト、アドレス/署名の検証には viem を直接使用します。
クイック判断ガイド
| 構築するもの | 使用するもの |
|---|---|
| Node.js スクリプト/バックエンド | http トランスポートを使った viem |
| React/Next.js フロントエンド | viem を基盤とする wagmi フック |
| リアルタイムイベント監視 | webSocket トランスポートを使った viem |
| ブラウザーウォレット統合 | wagmi または viem のカスタムトランスポート |
インストール
# Core library
npm install viem
# For React apps, also install wagmi
npm install wagmi viem @tanstack/react-query
基本概念
クライアント
viem には 2 種類のクライアントがあります。
| クライアント | 用途 | 使用例 |
|---|---|---|
| PublicClient | 読み取り専用の操作 | 残高の取得、コントラクトの読み取り、ログの取得 |
| WalletClient | 書き込み操作 | トランザクションの送信、メッセージへの署名 |
トランスポート
| トランスポート | 使用場面 |
|---|---|
http() | 標準的な RPC 呼び出し(最も一般的) |
webSocket() | リアルタイムイベントのサブスクリプション |
custom() | ブラウザーウォレット(window.ethereum) |
チェーン
viem には 50 以上のチェーン定義が含まれています。viem/chains からインポートします。
import { mainnet, arbitrum, optimism, base, polygon } from 'viem/chains';
入力値の検証ルール
ユーザーが指定した値を生成した TypeScript コードに埋め込む前に、必ず次の検証を行ってください。
- Ethereum アドレス:
^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 の処理、マルチコール | コントラクトのパターン |
| 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')
}
}
関連項目
- React のウォレット UI → wagmi-v3(viem を基盤としています)
- Ethereum のハッシュ化(セレクター、トピック、EIP-712)→ nodejs-keccak256-v1
- 非 EVM チェーン → solana-v1
Resources
コメント
Markdown 対応