👌
Viemの基礎学習1日目(Ethereum(Anvil)チェーンとの対話)
Viemライブラリの学習と実践
日付: 2025年10月25日
学習内容: Viemライブラリを使用したEthereumブロックチェーンとの対話、JavaScriptからTypeScriptへの移行
1. Viemライブラリの概要
1.1 Viemとは
Viemは、Ethereumブロックチェーンとやり取りするためのTypeScriptライブラリです。主な特徴は以下の通りです:
- Ethereumクライアント: ブロックチェーンとの通信
- ウォレット統合: MetaMaskなどのウォレットとの連携
- スマートコントラクト操作: コントラクトの読み取り・書き込み
- トランザクション処理: トランザクションの送信・確認
- TypeScript対応: 型安全性を提供
1.2 他のライブラリとの比較
- ethers.js: より軽量でモダンな代替
- web3.js: より新しいAPI設計
- wagmi: React用のフックベースライブラリ(Viemを内部で使用)
2. 前提条件とセットアップ
2.1 前提条件
- Node.jsとnpmがインストール済み
- Foundryをインストール済み
# Foundryのインストール(まだの場合)
curl -L https://foundry.paradigm.xyz | bash
foundryup
2.2 プロジェクトの初期化
# Node.jsプロジェクトの初期化
npm init -y
# Viemのインストール
npm install viem
3. Anvilローカルチェーンの起動
3.1 Anvilの起動
# Anvilを起動(ローカルEthereumノード)
anvil
3.2 Anvilの情報
Anvilが起動すると、以下のような情報が表示されます:
- デフォルトポート: http://127.0.0.1:8545
- 10個のテストアカウント(各10,000 ETH)
- 各アカウントの秘密鍵
4. JavaScriptでの実装
4.1 balance.jsの作成
残高確認用のスクリプトを作成:
// Viemのインポート
import { createPublicClient, http } from 'viem';
import { foundry } from 'viem/chains';
// Public Clientの作成(読み取り専用)
const publicClient = createPublicClient({
chain: foundry,
transport: http('http://127.0.0.1:8545')
});
// アカウントの残高を取得
// AnvilのAccount #0のアドレスを使用
const address = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
const balance = await publicClient.getBalance({
address: address
});
console.log('Balance:', balance.toString(), 'Wei');
console.log('Balance:', (balance / BigInt(10**18)).toString(), 'ETH');
4.2 send-transaction.jsの作成
トランザクション送信用のスクリプトを作成:
import { createWalletClient, createPublicClient, http, parseEther } from 'viem';
import { foundry } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
// Anvilから取得した秘密鍵を使用(Account #0)
const account = privateKeyToAccount('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80');
// Wallet Clientの作成(書き込み用)
const walletClient = createWalletClient({
account,
chain: foundry,
transport: http('http://127.0.0.1:8545')
});
// Public Clientの作成(読み取り用)
const publicClient = createPublicClient({
chain: foundry,
transport: http('http://127.0.0.1:8545')
});
// Account #0 から Account #1 に 1 ETH送信
const hash = await walletClient.sendTransaction({
to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', // Account #1
value: parseEther('1') // 1 ETH
});
console.log('Transaction Hash:', hash);
// トランザクションの確認を待つ
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('Transaction Status:', receipt.status);
4.3 index.jsの作成
包括的なデモスクリプトを作成:
// index.js
import { createPublicClient, createWalletClient, http, parseEther } from 'viem';
import { foundry } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';
// Public Client(読み取り用)
const publicClient = createPublicClient({
chain: foundry,
transport: http('http://127.0.0.1:8545')
});
// AnvilのAccount #0の秘密鍵
const account = privateKeyToAccount('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80');
// Wallet Client(書き込み用)
const walletClient = createWalletClient({
account,
chain: foundry,
transport: http('http://127.0.0.1:8545')
});
async function main() {
console.log('=== Viem + Anvil Tutorial ===\n');
// Account #0 と #1 のアドレス
const account0 = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
const account1 = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8';
// 初期残高を確認
console.log('初期残高:');
const balance0Before = await publicClient.getBalance({ address: account0 });
const balance1Before = await publicClient.getBalance({ address: account1 });
console.log(`Account #0: ${balance0Before / BigInt(10**18)} ETH`);
console.log(`Account #1: ${balance1Before / BigInt(10**18)} ETH\n`);
// 1 ETHを送信
console.log('1 ETH を Account #0 から Account #1 に送信中...');
const hash = await walletClient.sendTransaction({
to: account1,
value: parseEther('1')
});
console.log('Transaction Hash:', hash);
// トランザクションの確認
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log('Transaction Status:', receipt.status, '\n');
// 送信後の残高を確認
console.log('送信後の残高:');
const balance0After = await publicClient.getBalance({ address: account0 });
const balance1After = await publicClient.getBalance({ address: account1 });
console.log(`Account #0: ${balance0After / BigInt(10**18)} ETH`);
console.log(`Account #1: ${balance1After / BigInt(10**18)} ETH`);
}
main().catch(console.error);
5. ES Module対応
5.1 package.jsonの設定
{
"type": "module"
}
5.2 import文への変更
// CommonJSからES Moduleへ
import { createPublicClient, http } from 'viem';
import { foundry } from 'viem/chains';
6. JavaScriptからTypeScriptへの移行
6.1 必要なパッケージのインストール
# TypeScript関連パッケージのインストール
npm install --save-dev @types/node ts-node tsx typescript
6.2 tsconfig.jsonの作成
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules"],
"ts-node": {
"esm": true
}
}
6.3 srcフォルダの作成とTypeScriptファイルの作成
balance_ts.ts
// src/balance.ts
import { createPublicClient, http } from 'viem'
import { foundry } from 'viem/chains'
import type { Address } from 'viem'
// Public Clientの作成(読み取り専用)
const publicClient = createPublicClient({
chain: foundry,
transport: http('http://127.0.0.1:8545')
})
// アカウントの残高を取得
// AnvilのAccount #0のアドレスを使用
const address: Address = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
const balance = await publicClient.getBalance({
address: address
})
console.log('Balance:', balance.toString(), 'Wei')
console.log('Balance:', (balance / BigInt(10**18)).toString(), 'ETH')
send-transaction_ts.ts
import { createWalletClient, createPublicClient, http, parseEther } from 'viem'
import { foundry } from 'viem/chains'
import { privateKeyToAccount } from 'viem/accounts'
import type { Address } from 'viem'
// Anvilから取得した秘密鍵を使用(Account #0)
const account = privateKeyToAccount('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80')
// Wallet Clientの作成(書き込み用)
const walletClient = createWalletClient({
account,
chain: foundry,
transport: http('http://127.0.0.1:8545')
})
// Public Clientの作成(読み取り用)
const publicClient = createPublicClient({
chain: foundry,
transport: http('http://127.0.0.1:8545')
})
// Account #0 から Account #1 に 1 ETH送信
const hash = await walletClient.sendTransaction({
to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8' as Address, // Account #1
value: parseEther('1') // 1 ETH
})
console.log('Transaction Hash:', hash)
// トランザクションの確認を待つ
const receipt = await publicClient.waitForTransactionReceipt({ hash })
console.log('Transaction Status:', receipt.status)
6.4 実行方法
# tsxを使用してTypeScriptファイルを実行
npx tsx src/balance_ts.ts
npx tsx src/send-transaction_ts.ts
npx tsx src/index_ts.ts
7. 重要な概念の理解
7.1 Public Client vs Wallet Client
- Public Client: 読み取り専用(残高確認、ブロック情報取得など)
- Wallet Client: 書き込み専用(トランザクション送信、スマートコントラクト実行など)
7.2 非同期処理とawait
// 非同期関数の定義
async function main() {
const balance = await publicClient.getBalance({ address });
// awaitでPromiseの完了を待機
}
7.3 ES6の短縮記法
// プロパティ名と変数名が同じ場合
const walletClient = createWalletClient({
account, // { account: account }の短縮
chain, // { chain: chain }の短縮
transport // { transport: transport }の短縮
});
7.4 テンプレートリテラル
// バッククォート(`)で囲む
console.log(`Account #0: ${balance / BigInt(10**18)} ETH`);
8. エラーハンドリング
8.1 .catch()メソッド
main().catch(console.error);
8.2 try/catch文
try {
await main();
} catch (error) {
console.error(error);
}
9. TypeScriptの型システム
9.1 型のインポート
import type { Address } from 'viem';
9.2 型注釈
const address: Address = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266';
9.3 型安全性の利点
- コンパイル時のエラー検出
- IDEサポートの向上
- コードの可読性向上
10. 実行結果の例
10.1 balance.jsの実行結果
Balance: 9992999750922420627000 Wei
Balance: 9992 ETH
10.2 send-transaction.jsの実行結果
Transaction Hash: 0x4e7f4dc278b4fc13ebd47728c0cbb42f14f3b373b9a5574174ed4a69d7bd69ab
Transaction Status: success
10.3 index.jsの実行結果
=== Viem + Anvil Tutorial ===
初期残高:
Account #0: 9994.999813140300722 ETH
Account #1: 10005 ETH
1 ETH を Account #0 から Account #1 に送信中...
Transaction Hash: 0x0261231904ae9f37e1a2c755765e6a33260b9ea135b2671d746d146669b5e7d4
Transaction Status: success
送信後の残高:
Account #0: 9993.999781358437651 ETH
Account #1: 10006 ETH
11. 学習のポイント
11.1 重要な理解事項
- Viemライブラリの基本概念
- Public ClientとWallet Clientの使い分け
- 非同期処理とPromiseの理解
- ES ModuleとCommonJSの違い
- TypeScriptの型システム
11.2 実践的なスキル
- ブロックチェーンとの対話
- トランザクションの送信と確認
- エラーハンドリング
- JavaScriptからTypeScriptへの移行
12. まとめ
Viemライブラリを使用することで、Ethereumブロックチェーンとの対話が簡単になりました。JavaScriptからTypeScriptへの移行により、型安全性とコードの可読性が向上しました。Anvilローカルチェーンを使用することで、安全な環境でブロックチェーン開発の基礎を学習できました。
12.1 習得した技術
- Viemライブラリの使用方法
- Ethereumブロックチェーンとの対話
- 非同期処理の理解
- TypeScriptの型システム
- ES Moduleの使用
12.2 今後の学習方向
- スマートコントラクトのデプロイと実行
- より複雑なトランザクション処理
- イベントの監視と処理
- ガス最適化の理解
Discussion