この記事では、SolidityのEventを活用してNFTの一覧を取得する方法を解説します。
ブロックチェーン・スマートコントラクトの基礎知識がある方を対象に、ethers.jsを使った実装手順とサンプルコードを含めて紹介します。
通常、スマートコントラクトからNFTの一覧を取得しようとする場合、各トークンIDに対して逐次的にコントラクトの関数を呼び出す必要があります。これは、トークンの総数が多いほど繰り返しの呼び出しが増え、結果としてブロックチェーンへのアクセス回数が膨大になります。
ブロックチェーンに複数回アクセスする必要があるため負荷の原因となる
このような連続的な問い合わせは、ブロックチェーンに負荷をかけるだけでなく、処理時間の増加リスクも伴います。効率の悪いアクセスはアプリケーション全体のパフォーマンス低下につながりかねません。
そこで有効なのが、SolidityのEventを活用した一覧取得です。Eventは、トランザクションやスマートコントラクト実行の副産物として、ブロックチェーン上にログとして記録されるものである。この記録されたEventログを活用することで高速なフィルタリングやNFT一覧の取得が可能になります。
eventにより一回のアクセスでNFT一覧をまとめて取得することが可能となる
しかしEventだけでは、計算処理等を実行することが難しいため使いどころに注意する必要があります。
TransferのEventなどを利用すれば、過去のNFTの発行履歴や所有者の転送履歴を一括で取得でき、トークンIDの抽出も効率的に行えます。
今回は3つのフェーズに分けてEvnetを利用したNFT一覧の取得方法について紹介します。今回はEventの概要を紹介するのでより詳しい実装や応用方法等についてはsolidityやethers.jsの公式ドキュメント参照をお願いします。
NFT一覧取得のシーケンス
- SolidityスマートコントラクトによるEventの定義・発行
- ethers.jsを用いたEventログの取得
- Eventログを基にスマートコントラクトを呼び出し
SolidityスマートコントラクトによるEventの定義・発行
Solidityを用いたEvnetの定義と定義したEventログを発行する方法について紹介します。
Eventの定義
3つの引数を定義します。TransferEventは、ERC-721では、Transfer Eventは定義済みなので自身で定義する必要はないです。
- from: NFT送信するアドレス
- to: NFTを受け取るアドレス
- tokenId: 転送したNFTのトークンID(識別子)
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
次にEventの発行方法について紹介します。fromやto、tokenIdには実引数が入ります。通常、emitはスマートコントラクトの関数内で呼び出されます。これによりスマートコントラクトが正しく実行されていることがEventログから参照することが可能になります。
emit Transfer(from, to, tokenId)
これらのEvnetログは、etherscanなどのブロックチェーンエクスプローラーでも参照することが可能です。
ethers.jsを用いたEventログの取得
ethers.jsを利用したEventの取得には2つの方法があります。
方法①:Eventクエリー
1つ目の方法はイベントを一括で取得する方法です。こちらの方法はqueryFilterを活用することで取得するブロックの範囲のフィルタリングすることが可能です。NFTを一覧で取得する場合は、こちらの方法①の方がおすすめです。
import ethers from 'ethers';
// ブロックチェーンの接続とコントラクトインスタンスの作成
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider);
async function fetchPastTransfers() {
const filter = contract.filters.Transfer(null, null); // ウォレットアドレスのフィルタの設定
const logs = await contract.queryFilter(filter, START_BLOCK, "latest"); // 取得する期間を指定してログをクエリーする
return logs;
}
方法②:Eventリスニング
2つ目はリアルタイムでブロックチェーンを監視し、イベントの発行の度に情報を取得する場合です。この方法はリアルタイムで情報を取得するため新しいイベントにすぐに反応することが可能です。
async function fetchPastTransfers() {
// 取得するEvent名を指定してリアルタイム監視を行う
contract.on("Transfer", (from, to, tokenId, event) => {
console.log({ from, to, tokenId: tokenId.toString() });
});
}
Eventログを基にスマートコントラクトを呼び出し
ethers.jsを使ってNFTコントラクトのTransferイベントに対するフィルターを作成し、イベントログを一括で取得しました。これらの情報をそのまま利用することができます。
for (const log of logs) {
const { from, to, tokenId } = log.args;
// 標準出力
console.log(`Transfer: from=${from}, to=${to}, tokenId=${tokenId.toString()}`);
}
さらにログから得られたtokenIdを使って、スマートコントラクトのtokenURI関数を呼び出します。これにより、現在そのNFTを所有しているToken URIを取得することができます。こうして、過去の移動履歴とコンテンツ情報を照合することが可能になります。
// Eventログを用いたtokenURI関数の呼び出し
const tokens = await Promise.all(logs.map(async (log) => {
const tokenId = Number((log as EventLog).args![2]); // ログからToken IDのみを抽出
const owner = await contract.tokenURI(tokenId);
return { tokenId: tokenId, owner: owner };
}));
しかしスマートコントラクトを活用した情報取得の回数が増加するとEventログを利用して取得した意味が薄れてしまうため、取得する回数に限度を設ける必要があります。
まとめ
- スマートコントラクトを用いて一覧をしようとするとアクセス回数の増加に伴う負荷やガス代などの問題に繋がる可能性があります
- スマートコントラクト実行時に記録したEventログを参照する方法を用いることにより一括で情報を取得することが可能になるます
- これらを応用することでスマートコントラクトを複数回呼び出さなくても高効率にブロックチェーンからデータを取得することが可能となります
参考リンク
- Solidity公式ドキュメント:https://docs.soliditylang.org/ja/latest/contracts.html
- ethers.js公式ドキュメント:https://docs.ethers.org/v6/
- ERC-721規格:https://eips.ethereum.org/EIPS/eip-721