ブロックチェーンとの連携をJavaScriptで実現する:Web3.js/Ethers.jsの基本技術
JavaScriptとブロックチェーン連携の重要性
Webエンジニアの皆様にとって、JavaScriptは最も馴染み深い言語の一つかと思います。ブロックチェーン技術を学び、将来的に分散型アプリケーション(dApps)の開発に携わる上で、このJavaScriptのスキルを活かせる機会は多々あります。特に、ユーザーインターフェース(フロントエンド)からブロックチェーンと対話し、情報を取得したり、トランザクションを送信したりする場面では、JavaScriptが主要な役割を果たします。
これは従来のWeb開発における、ブラウザのJavaScriptがバックエンドAPIとHTTP通信を行うのと似ています。しかし、ブロックチェーンとの連携は、単にデータを送受信するだけでなく、暗号署名やトランザクションの非同期処理、Gasの概念といったブロックチェーン特有の要素を理解する必要があります。
本記事では、JavaScriptを使ってブロックチェーンと連携するための基本的な技術、特に主要なライブラリであるWeb3.jsとEthers.jsに焦点を当て、その仕組みと使い方を解説します。
ブロックチェーンノードとの通信
JavaScriptアプリケーションがブロックチェーンとやり取りするためには、ブロックチェーンネットワーク上のノードと通信する必要があります。この通信には、一般的にJSON-RPC(Remote Procedure Call)というプロトコルが使用されます。JSON-RPCは、HTTPやWebSocketなどのトランスポート層の上で動作し、リモートのプロシージャ(関数)を呼び出すための軽量な方法を提供します。
しかし、アプリケーション開発者が直接JSON-RPCリクエストを作成・送信するのは手間がかかります。そこで登場するのが、Web3.jsやEthers.jsといったライブラリです。これらのライブラリは、JSON-RPC通信を抽象化し、JavaScriptオブジェクトや関数を通じて、より簡単にブロックチェーンの機能を利用できるようにします。
これらのライブラリを使用するためには、まず接続先のブロックチェーンノード(またはそのエンドポイント)を指定する必要があります。これは「プロバイダー(Provider)」と呼ばれ、ライブラリがブロックチェーンネットワークと通信するためのゲートウェイのような役割を担います。一般的なプロバイダーには以下のようなものがあります。
- HttpProvider: HTTP経由でノードと通信します。簡単な読み取り操作などに適しています。
- WebSocketProvider: WebSocket経由でノードと通信します。イベントのリアルタイム購読などに適しています。
- InjectedProvider: MetaMaskなどのブラウザ拡張ウォレットが提供するプロバイダーです。ユーザーの承認を得て、ウォレット内のアカウント情報へのアクセスやトランザクションの署名などを行います。
開発中は、InfuraやAlchemyのようなサービスが提供するRPCエンドポイントを利用することが多いです。これらのサービスは、自前でフルノードを運用する手間なく、安定したブロックチェーンノードへのアクセスを提供してくれます。
// 例: Ethers.jsを使ったHTTPプロバイダーでの接続
// const { ethers } = require("ethers"); // Node.jsの場合
// const provider = new ethers.providers.JsonRpcProvider("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID");
// 例: Web3.jsを使ったHTTPプロバイダーでの接続
// const Web3 = require('web3'); // Node.jsの場合
// const web3 = new Web3("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID");
※ 上記コードは概念を示すものであり、実行には適切な環境構築が必要です。
アカウントとウォレットの扱い
ブロックチェーン上で資産を保有したり、トランザクションを送信したりするためには、アカウント(公開鍵と秘密鍵のペア)が必要です。ユーザーがこれらのアカウントを管理するために使用するのがウォレットです。
JavaScriptからブロックチェーンを操作する際には、これらのウォレットと連携することが非常に一般的です。特にブラウザベースのdAppsでは、MetaMaskなどのブラウザ拡張ウォレットがwindow.ethereum
オブジェクトを介してInjectedProviderを提供し、アプリケーションはこれを利用して以下のような操作を行います。
- ユーザーのブロックチェーンアドレスの取得
- ユーザーへのトランザクション署名要求(秘密鍵はウォレットが管理)
- ネットワークの切り替え要求
Web3.jsやEthers.jsは、これらのウォレット連携をサポートするための機能を提供しています。アプリケーションは直接ユーザーの秘密鍵を扱うことはなく、トランザクションの作成や署名の要求をウォレットに委任する形で連携します。これにより、秘密鍵の安全性が確保されます。
// 例: MetaMaskなどのInjectedProviderを使用する場合 (Ethers.js)
// if (window.ethereum) {
// const provider = new ethers.providers.Web3Provider(window.ethereum);
// const signer = provider.getSigner(); // アカウント情報を取得し、トランザクションに署名するためのオブジェクト
// const userAddress = await signer.getAddress(); // ユーザーのアドレスを取得
// console.log("Connected account:", userAddress);
// } else {
// console.error("MetaMask or similar provider not detected");
// }
ブロックチェーンからの情報取得(読み込み操作)
ブロックチェーンから情報を取得する(データを読み込む)操作は、一般的に手数料(Gas)がかかりません。これは、単にノードが保持している台帳データのコピーを参照するだけであり、ネットワークの状態を変更しないためです。
JavaScriptライブラリを使用すると、以下のような情報を簡単に取得できます。
- 指定したアドレスのEther(ネイティブ通貨)残高
- 最新のブロック番号やブロック情報
- トランザクションの詳細
- スマートコントラクトの公開(
view
またはpure
)関数からのデータ取得
例えば、Ethers.jsを使ってアカウントの残高を取得するコードは以下のようになります。
// 例: Ethers.jsで残高を取得
// const address = "0x..."; // 取得したいアドレス
// const balance = await provider.getBalance(address); // 残高をWei単位で取得
// const etherBalance = ethers.utils.formatEther(balance); // Ether単位に変換
// console.log("Ether balance:", etherBalance);
トランザクションの送信(書き込み操作)
ブロックチェーンの状態を変更する操作、つまり新しいブロックを生成する可能性のある操作は「トランザクション」として実行され、手数料(Gas)が必要です。これには、ネイティブ通貨の送金や、スマートコントラクトの状態を変更する関数の呼び出しなどが含まれます。
トランザクション送信の基本的な流れは以下のようになります。
- トランザクションオブジェクトの作成(宛先、送金額、データなど)。
- 作成したトランザクションに秘密鍵で署名する(ウォレットが行う場合が多い)。
- 署名済みトランザクションをブロックチェーンネットワークにブロードキャストする。
- ネットワーク上のマイナー(またはバリデーター)がトランザクションを検証し、ブロックに取り込む。
- トランザクションがブロックチェーンに確定されるのを待つ。
JavaScriptライブラリは、これらのステップを抽象化し、開発者が扱いやすいAPIを提供します。特に、ウォレット連携を通じて署名を要求する機能は重要です。
// 例: Ethers.jsでEtherを送信 (署名者はsignerを使用)
// const recipientAddress = "0x...";
// const amount = ethers.utils.parseEther("0.01"); // 送信量 (Ether単位をWeiに変換)
// const tx = await signer.sendTransaction({
// to: recipientAddress,
// value: amount,
// });
// console.log("Transaction hash:", tx.hash);
// // トランザクションが確定するまで待機
// const receipt = await tx.wait();
// console.log("Transaction confirmed in block:", receipt.blockNumber);
スマートコントラクトの操作
dApps開発の核心は、スマートコントラクトとの連携にあります。JavaScriptからスマートコントラクトの関数を呼び出したり、イベント(ログ)を購読したりすることが可能です。
スマートコントラクトを操作するためには、以下の情報が必要です。
- コントラクトアドレス: デプロイされたスマートコントラクトのブロックチェーン上のアドレス。
- ABI (Application Binary Interface): スマートコントラクトの関数やイベントの情報を記述したJSON形式のデータです。これによって、ライブラリはどの関数をどのように呼び出せば良いか、どのようなデータを受け取れるかを理解します。
ライブラリは、これらの情報を使ってコントラクトのインスタンスを生成します。このインスタンスを通じて、スマートコントラクト上の関数をJavaScriptのメソッドとして呼び出せるようになります。
// 例: Ethers.jsでスマートコントラクトを操作
// const contractAddress = "0x..."; // スマートコントラクトのアドレス
// const contractABI = [ /* ABIのJSON配列 */ ];
// const contract = new ethers.Contract(contractAddress, contractABI, provider); // 読み取り操作用
// // 書き込み操作や署名が必要な場合は、signerを渡す
// // const contractWithSigner = new ethers.Contract(contractAddress, contractABI, signer);
// // 例: コントラクトの読み取り関数を呼び出す
// const value = await contract.someReadFunction();
// console.log("Value from contract:", value.toString());
// // 例: コントラクトの書き込み関数を呼び出す
// // const txResponse = await contractWithSigner.someWriteFunction(someParameter);
// // await txResponse.wait(); // トランザクションの確定を待つ
スマートコントラクトの操作において、状態を変更しない読み取り専用関数(ABIでview
やpure
とマークされている)は、通常call
として実行され、Gasは不要です。一方、状態を変更する関数はトランザクションとして実行され、Gasが必要です。ライブラリはこの違いをABIに基づいて自動的に判断し、適切なJSON-RPCメソッド(eth_call
またはeth_sendTransaction
)を使用します。
また、スマートコントラクトが特定のイベントを発生させた際に、そのイベントをリアルタイムで購読することも可能です。これは、dAppsのUIをブロックチェーン上の状態変化に応じて更新する際などに非常に役立ちます。
まとめと次のステップ
本記事では、Webエンジニアの皆様が既存のJavaScriptスキルを活かしてブロックチェーンと連携するための基本的な技術概要を解説しました。Web3.jsやEthers.jsといったライブラリが、JSON-RPC通信を抽象化し、ノードへの接続、アカウント/ウォレット連携、データの読み取り、トランザクション送信、スマートコントラクト操作といった一連のプロセスを容易にしてくれることをご理解いただけたかと思います。
これらのライブラリは、dAppsのフロントエンド開発における基盤となります。次のステップとしては、実際にこれらのライブラリを使って、簡単なウォレット接続、Ether残高表示、簡単なスマートコントラクトとのインタラクションを実装してみることをお勧めします。
さらに深く学習を進める場合は、以下のトピックにも触れると良いでしょう。
- Gasの見積もりや指定の方法
- トランザクションの署名プロセスに関する詳細
- スマートコントラクトのイベント購読
- 特定のネットワーク(Ethereum、Polygon、BNB Chainなど)への接続方法の違い
- TruffleやHardhatといった開発フレームワークとの連携
これらの知識を習得することで、ブロックチェーンを活用したWebアプリケーション開発の幅が大きく広がるはずです。