viem-integration について
TypeScript や JavaScript アプリケーションで viem を使い、EVM ブロックチェーンと連携するためのスキルです。クライアントやトランスポートの選択、チェーン上の読み書き、wagmi の利用方法を説明します。
主な機能
- 01PublicClient と WalletClient の設定
- 02HTTP、WebSocket、カスタムトランスポート
- 03チェーンとアカウントの管理
- 04コントラクトの読み取りと書き込み
- 05アドレス、単位、エラー処理のユーティリティ
活用シーン
- 01残高やコントラクトデータの読み取り
- 02トランザクション送信やメッセージ署名
- 03リアルタイムのブロックチェーンイベント監視
- 04React アプリへの 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 との統合
TypeScript/JavaScript アプリケーションで viem を使用して EVM ブロックチェーンを統合します。
クイック判断ガイド
| 構築するもの | こちらを使用 |
|---|---|
| Node.js スクリプト/バックエンド | viem と http トランスポート |
| React/Next.js フロントエンド | wagmi フック(viem ベース) |
| リアルタイムイベントの監視 | viem と webSocket トランスポート |
| ブラウザーウォレットとの統合 | 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')
}
}
リソース
コメント
Markdown 対応