📌
Solidityとethers.jsでEventの発行と受け取りを実装する
コントラクト側でイベントを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
で発行されたイベントを受け取ることができる。filters
でTweeted
のイベントを指定しているので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