【Solidity】event / indexedの使い方を徹底解説!【ethers.jsでのlisten方法】

Web3
まるお
まるお
イベント?
好っきやで?
 
かとてん
かとてん

んー、たぶんイメージしているものがちょっと違うねんな笑

 
 

eventとは?

Solidityにおけるeventとは、「スマートコントラクトでの動作結果をEVMのログ(Transactionレシート)に保存・抽出してフロントエンドに伝える」役割を持つ機能です。
つまり、eventを利用することで必要な情報をフロントエンドに送ることができるということになります。
 
ちなみに、ログ(Transactionレシート)にはイベント名と引数の型のハッシュ値、引数の値の羅列、ブロックハッシュなどが保存されています。
その中で、eventで利用されるものは基本的にlogsパートのData内に保存されますが、後述するindexedを付けた引数はTopics内に保存されます。(以下はイメージです。)
 
【ログの中身(イメージ)】
{
 ”jsonrpc”: “2.0”,
 ”id”: 0,
 ”method”: “test”,
 ”prams”: [
  {
   ”address”: “0x0…”,
   ”topics”: [
    ”0x0…”
   ]
  }
 ]
}
 
eventは必ずEmitと共に利用されます。
スマートコントラクトに以下のように記述することで、eventがフロントエンドにemit(発する)され、eventで指定している中身をフロントエンドで利用することができます。
 
【サンプル】
event NewTweet(address indexed from, uint256 timestamp, string message);

 function tweet(string memory _message) public {
 // 上段でTweet型の配列に必要な情報を保存して、下段で上記で定義したNewTweeイベントをemit
  allTweetsData.push(Tweet(msg.sender, block.timestamp, _message));
  emit NewTweet(msg.sender, block.timestamp, _message);
 }
 
 
 

indexedとは?

indexedは、eventのパラメータに使われる修飾子です。
indexed修飾子を付与すると、付与されたパラメータ単位でeventをフィルタリングすることができます。
 
なお、ここで言うフィルタリングは、フロントエンドで受け取るか受け取らないかの意味になります。
つまり、フィルタリングされている場合は、フロントエンドでemitされたeventを受け取らないのです。
 
以下の例だと、address型でフィルタリングできるようになります。
ether.jsでは、フロントエンドで指定したaddress型のみを受け取るように設定することができます。
 
【サンプル】
event NewTweet(address indexed from, uint256 timestamp, string message);
 
indexed修飾子を付けられる数は最大で3つまでと決まっているため注意が必要です。
 
 
 

サンプルコード(ether.js使用)

説明だけでは分かりづらいと思いますので、以下にコードのサンプルを記述します。
実際に動かしてみていただければ、動きが分かるかと思います。
 
// Twitter.sol
contract Twitter {
 …
 event NewTweet(address indexed from, string msg);
 function tweet(string memory _tweet) public {
  emit NewTweet(msg.sender, _tweet);
 }
 …
}
 
// App.tsx
useEffect (() => {
 const onNewTweet = (from: string, msg: string) => {
  console.log(“from: “, from);
  console.log(“message: “, msg);
 };
 …
 twitterContract = new ethers.Contract(contractAddress, contractABI, signer);
 const filter = twitterContract.filters.NewTweet(“0x0…”, null);
 twitterContract.on(filter, onNewTweet);
 …
}, []);
 
コントラクト側では、tweet関数が動いたときにeventで定義されているaddress型のアドレス(indexed)とstring型のメッセージをemitします。
そして、フロントエンド側では、NewTweetイベントがemitされた際にtwitterContract.filters.NewTweetで指定された”0x0…”のアカウントがイベントとして発行された場合のみイベントを購読しています。
 
以下は補足です。
・twitterContract.filters.NewTweetのNewTweetには対象のイベント名が入ります。
・twitterContract.filters.NewTweet(“0x0…”, null);の(“0x0…”, null)はイベントで定義した引数です。(この例ではNewTweet(address indexed _from, string _msg)です。)
・イベントで定義した引数の数やindexedの数が増えた場合、(“0x0…”, null, null, null)や(1, null “0x0…”, null, null)のように指定します。(indexed修飾子がついている引数のみ値を指定できます。)
 
詳細はこちらを参照してください。
 
 
 

さいごに

eventはdAppsのフロントエンドとコントラクトを繋ぐ架け橋のようなものです。
ほとんどのdAppsで使われるのではないでしょうか。
 
そんなeventを使いこなしていきましょう!
 
 
 
タイトルとURLをコピーしました