スマートコントラクトをデプロイしたら、次はそのコントラクトと対話するアプリケーション(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を作りながら、これらのライブラリの使い方に慣れていきましょう!🚀
コメント