📌

Solidityとethers.jsでEventの発行と受け取りを実装する

2021/12/10に公開

コントラクト側でイベントをemitするとフロントエンドでそのイベント通知を受け取ることができる。

ユースケースとしては、例えばツイートをした時にコントラクト側でそのツイートがブロックに取り込まれたタイミングでemitする。フロントエンドではそのイベントを受け取ってタイムラインを更新する。こうすればsetTimeoutのような一定間隔ごとにタイムラインの更新を確認する無駄なポーリングを不要にできる。

コントラクトの実装としては下記のような感じ。eventを定義して特定の処理の中でemitしてイベントを発行する

pragma solidity ^0.8.0;
contract Twitter {
    event Tweet(address indexed _from, string _msg);
    function tweet(string memory _tweet) public {
       emit Tweet(msg.sender, _tweet);
    }
}

フロントエンドでこの通知を受け取るには下記のような感じでいける。ethers.jsとuseDappを使ってる場合。provider.onceを使うことで次のblockからの通知を受け取るようにイベントをサブスクライブする。contract.onでコントラクトで先ほど定義したemit Tweetで発行されたイベントを受け取ることができる。filtersTweetedのイベントを指定しているのでemit Tweetされた全てのイベントが流れてくるようになる。

import { utils, Contract, ethers } from "ethers"
...
const { library } = useEthers()
const subscribeTweet = async () => {
  const inteface = new utils.Interface(ABI.abi)
  const contract = new Contract(
    `${CONTRACT_ID}`,
    inteface,
    library?.getSigner()
  )
  const provider = new ethers.providers.Web3Provider(library.provider)
  const filters = contract.filters["Tweeted"]
  if (filters !== undefined) {
    provider.once("block", () => {
      contract.on(filters(), (author: string, content: string) => {
      })
    })
  }
}

先ほど定義したコントラクトではEventの引数にindexedと指定している箇所がある。これはフロントエンド側でイベントをサブスクライブするときにどのイベントを受け取るのか絞り込むのに使える。例えば上のフロントエンドのコードのfilter()の部分をfilters(<MY ADDRESS>)にすると自分の投稿のみのイベントが流れてくるようになる。

その他Eventがどこに保存されてるのか等詳細に関する情報はSolidityの公式Contracts — Solidity 0.5.4 ドキュメントを読むべし。

リンク

Discussion