スマートコントラクトをデプロイしたら、次はそのコントラクトと対話するアプリケーション(DApp: Decentralized Application)を構築します。WebベースのDAppでは、フロントエンド(ユーザーが見る画面)からEthereumネットワーク上のスマートコントラクトを操作するために、JavaScriptライブラリが不可欠です。今回は、その代表的なライブラリである web3.js と ethers.js の基本的な使い方を学びましょう!
web3.js の基本操作
web3.js は、Ethereumエコシステムで最も古くから使われているライブラリの一つです。Ethereumノードと通信するための豊富な機能を提供します。
1. インストール
まず、プロジェクトに web3.js をインストールします。
npm install web3
または
yarn add web3
2. 初期化とプロバイダ設定
web3.js を使用するには、Ethereumネットワークへの接続点となる「プロバイダ」を設定する必要があります。ブラウザ環境では MetaMask が提供するプロバイダ (`window.ethereum`) を使うのが一般的です。Node.js環境などでは、Infura のようなノードプロバイダサービスのエンドポイントURLを指定します。
// ブラウザ環境 (MetaMaskなど)
import Web3 from 'web3';
let web3;
if (window.ethereum) { web3 = new Web3(window.ethereum); try { // アカウントへのアクセスを要求 await window.ethereum.request({ method: 'eth_requestAccounts' }); } catch (error) { console.error("User denied account access"); }
} else if (window.web3) { // 古い DApp ブラウザの場合 web3 = new Web3(window.web3.currentProvider);
} else { // MetaMaskなどがインストールされていない場合のフォールバック const provider = new Web3.providers.HttpProvider('YOUR_INFURA_ENDPOINT'); // 例: InfuraのURL web3 = new Web3(provider); console.log('Non-Ethereum browser detected. You should consider trying MetaMask!');
}
// Node.js 環境の例
// const Web3 = require('web3'); // CommonJSの場合
// const web3 = new Web3('YOUR_INFURA_ENDPOINT');
3. 基本的な操作例
アカウント取得
async function getAccounts() { try { const accounts = await web3.eth.getAccounts(); console.log('Accounts:', accounts); return accounts; } catch (error) { console.error('Error fetching accounts:', error); }
}
getAccounts();
ネットワークID取得
async function getNetworkId() { try { const networkId = await web3.eth.net.getId(); console.log('Network ID:', networkId); return networkId; } catch (error) { console.error('Error fetching network ID:', error); }
}
getNetworkId();
スマートコントラクトの操作
コントラクトを操作するには、コントラクトのABI(Application Binary Interface)とデプロイされたアドレスが必要です。ABIはコンパイル時に生成されるJSONファイルで、コントラクトの関数やイベントの定義情報が含まれています。
// 例: シンプルなカウンターコントラクト
const contractABI = [ /* ... コントラクトのABIをここに貼り付け ... */ ];
const contractAddress = 'YOUR_CONTRACT_ADDRESS'; // デプロイしたコントラクトのアドレス
// コントラクトインスタンスの作成
const contract = new web3.eth.Contract(contractABI, contractAddress);
// --- データの読み取り (call) ---
async function getCount() { try { // 'getCount' はコントラクト内の view または pure 関数の名前 const count = await contract.methods.getCount().call(); console.log('Current count:', count); return count; } catch (error) { console.error('Error calling getCount:', error); }
}
// --- データの書き込み (send) ---
async function incrementCount() { try { const accounts = await web3.eth.getAccounts(); if (accounts.length === 0) { console.error("No accounts found. Make sure MetaMask is connected."); return; } const senderAccount = accounts[0]; // 'increment' はコントラクト内の状態を変更する関数の名前 // send() はトランザクションを送信し、ガス代が必要 const receipt = await contract.methods.increment().send({ from: senderAccount }); console.log('Transaction receipt:', receipt); // トランザクション成功後、再度カウントを取得して確認 await getCount(); } catch (error) { console.error('Error sending increment transaction:', error); }
}
// 実行例
getCount();
// incrementCount(); // 実行するとMetaMaskが起動し、トランザクションの承認を求められます
ethers.js の基本操作
ethers.js は、web3.js と同様の目的を持つライブラリですが、よりモダンで軽量、そしてシンプルで一貫性のあるAPIを提供することを目指しています。ENS(Ethereum Name Service)のネイティブサポートなども特徴です。近年、多くの新しいプロジェクトで採用されています。
1. インストール
npm install ethers
または
yarn add ethers
2. 初期化とプロバイダ/サイナー設定
ethers.js では、「プロバイダ(Provider)」と「サイナー(Signer)」という概念が重要です。
- Provider: Ethereumネットワークへの読み取り専用の接続を提供します。アカウントの状態を知る必要はありません。
- Signer: Providerの機能に加え、特定のアカウント(秘密鍵を持つ)としてトランザクションに署名し、送信することができます。
// ブラウザ環境 (MetaMaskなど)
import { ethers } from 'ethers';
let provider;
let signer;
if (window.ethereum) { // MetaMask が提供するプロバイダを使用 provider = new ethers.BrowserProvider(window.ethereum); try { // アカウントへの接続と署名者の取得 signer = await provider.getSigner(); console.log('Signer obtained:', await signer.getAddress()); } catch (error) { console.error("Error getting signer:", error); }
} else { // MetaMaskなどがインストールされていない場合 (読み取り専用) // 例: Infura を使う場合 provider = new ethers.InfuraProvider('mainnet', 'YOUR_INFURA_API_KEY'); // または JsonRpcProvider console.log('Connected to Infura (read-only)'); // この場合、signer は取得できないため、書き込み操作は不可
}
// Node.js 環境の例 (読み取り専用)
// import { InfuraProvider } from 'ethers'; // ES Modules
// const provider = new InfuraProvider('mainnet', 'YOUR_INFURA_API_KEY');
// Node.js 環境の例 (秘密鍵を使って書き込み可能)
// import { Wallet, JsonRpcProvider } from 'ethers';
// const provider = new JsonRpcProvider('YOUR_RPC_ENDPOINT');
// const privateKey = 'YOUR_PRIVATE_KEY'; // 注意: 秘密鍵の管理には十分気をつけてください
// const wallet = new Wallet(privateKey, provider);
// const signer = wallet;
3. 基本的な操作例
アカウント(署名者のアドレス)取得
async function getSignerAddress() { if (!signer) { console.error('Signer not available.'); // 必要に応じて再度接続を試みるか、ユーザーに接続を促す // await provider.send("eth_requestAccounts", []); // 再度MetaMaskに接続要求 // signer = await provider.getSigner(); return; } try { const address = await signer.getAddress(); console.log('Signer Address:', address); return address; } catch (error) { console.error('Error getting signer address:', error); }
}
getSignerAddress();
ネットワーク情報取得
async function getNetworkInfo() { if (!provider) { console.error('Provider not available.'); return; } try { const network = await provider.getNetwork(); console.log('Network Name:', network.name); console.log('Network Chain ID:', network.chainId); } catch (error) { console.error('Error getting network info:', error); }
}
getNetworkInfo();
スマートコントラクトの操作
web3.js と同様に、コントラクトのABIとアドレスが必要です。ethers.js では ethers.Contract
クラスを使用します。
// 例: シンプルなカウンターコントラクト
const contractABI = [ /* ... コントラクトのABIをここに貼り付け ... */ ];
const contractAddress = 'YOUR_CONTRACT_ADDRESS';
// 読み取り専用のコントラクトインスタンス (Providerを使用)
const readOnlyContract = new ethers.Contract(contractAddress, contractABI, provider);
// 書き込み可能なコントラクトインスタンス (Signerを使用)
// Signerが取得できている場合のみ作成可能
const writableContract = signer ? new ethers.Contract(contractAddress, contractABI, signer) : null;
// --- データの読み取り ---
async function getCountEthers() { try { // 'getCount' はコントラクト内の view または pure 関数の名前 const count = await readOnlyContract.getCount(); // Provider経由で呼び出し console.log('Current count (ethers):', Number(count)); // BigIntで返ってくることが多いのでNumber()で変換 return count; } catch (error) { console.error('Error calling getCount (ethers):', error); }
}
// --- データの書き込み ---
async function incrementCountEthers() { if (!writableContract) { console.error('Writable contract instance not available. Check if wallet is connected.'); return; } try { // 'increment' はコントラクト内の状態を変更する関数の名前 // Signer経由で呼び出すとトランザクションが送信される const tx = await writableContract.increment(); console.log('Transaction sent:', tx.hash); // トランザクションがブロックに含まれるのを待つ (任意) const receipt = await tx.wait(); console.log('Transaction confirmed:', receipt); // トランザクション成功後、再度カウントを取得して確認 await getCountEthers(); } catch (error) { console.error('Error sending increment transaction (ethers):', error); }
}
// 実行例
getCountEthers();
// if (writableContract) {
// incrementCountEthers(); // 実行するとMetaMaskが起動し、トランザクションの承認を求められます
// }
web3.js vs ethers.js どっちを選ぶ?
どちらのライブラリも Ethereum との対話を実現する強力なツールですが、いくつかの違いがあります。
特徴 | web3.js | ethers.js |
---|---|---|
歴史と普及度 | 古くからあり、多くの既存プロジェクトで使用されている。情報量も多い。 | 比較的新しいが、急速に普及。モダンなプロジェクトでの採用例が増加。 |
APIデザイン | 機能は豊富だが、API がやや複雑で一貫性に欠ける部分があるとの指摘も。 | シンプルで一貫性のあるAPIを目指している。Provider/Signer の概念が明確。 |
ライブラリサイズ | 比較的多機能な分、サイズが大きめ。 | より軽量。 |
TypeScript対応 | 対応しているが、型定義のメンテナンスが追いついていない場合がある。 | ネイティブでTypeScriptをサポートしており、型定義が充実。 |
ENSサポート | 基本的なサポートあり。 | ネイティブで強力なENSサポートを提供。 |
コミュニティとドキュメント | 長年の実績があり、コミュニティは大きい。 | 活発なコミュニティと、整理された公式ドキュメント。 |
結論として:
- これから新しいプロジェクトを始める場合、ethers.js はシンプルさ、軽量さ、TypeScriptとの親和性の高さから、有力な選択肢となります。
- 既存のプロジェクトで web3.js が使われている場合や、豊富な情報量を重視する場合は、web3.js も引き続き良い選択です。
どちらのライブラリも基本的な操作(アカウント取得、ネットワーク接続、コントラクト呼び出し)は可能です。まずは両方のドキュメントやサンプルコードを見て、自分に合った方を選んでみましょう!
まとめ
今回は、フロントエンドとスマートコントラクトを繋ぐための重要なライブラリである web3.js と ethers.js の基本的な使い方を紹介しました。
- プロバイダ/サイナーの設定方法
- アカウントやネットワーク情報の取得
- スマートコントラクトの読み取り (call/読み取り専用) と書き込み (send/Signer経由)
これらの基本をマスターすれば、いよいよ本格的なDApp開発に進むことができます。実際に簡単なDAppを作りながら、これらのライブラリの使い方に慣れていきましょう!