检索事件

本文主要介绍如何使用ethers.js读取智能合约释放的事件。

事件 Event

智能合约释放出的事件存储于以太坊虚拟机的日志中。日志分为两个主题topics和数据data部分,其中事件哈希和indexed变量存储在topics中,作为索引方便以后搜索;没有indexed变量存储在data中,不能被直接检索,但可以存储更复杂的数据结构。

以ERC20代币中的Transfer转账事件为例,在合约中它是这样声明的:

event Transfer(address indexed from, address indexed to, uint256 amount);

它共记录了3个变量fromtoamount,分别对应代币的发出地址,接收地址和转账数量,其中fromto前面带有indexed关键字。转账时,Transfer事件会被记录,可以在etherscan查到

从上图中可以看到,Transfer事件被记录到了EVM的日志中,其中Topics包含3个数据,分别对应事件哈希,发出地址from,和接收地址to;而Data中包含一个数据,对应转账数额amount

检索事件

我们可以利用Ethers中合约类型的queryFilter()函数读取合约释放的事件。

const transferEvents = await contract.queryFilter('事件名', 起始区块, 结束区块)

queryFilter()包含3个参数,分别是事件名(必填),起始区块(选填),和结束区块(选填)。检索结果会以数组的方式返回。

注意:要检索的事件必须包含在合约的abi中。

例子:检索WETH合约中的Transfer事件

创建provider

import { ethers } from "ethers";
// 利用Alchemy的rpc节点连接以太坊网络
// 准备 alchemy API 可以参考https://github.com/AmazingAng/WTFSolidity/blob/main/Topics/Tools/TOOL04_Alchemy/readme.md 
const ALCHEMY_GOERLI_URL = 'https://eth-goerli.alchemyapi.io/v2/GlaeWuylnNM3uuOo-SAwJxuwTdqHaY5l';
const provider = new ethers.JsonRpcProvider(ALCHEMY_GOERLI_URL);

创建包含检索事件的abi。

// WETH ABI,只包含我们关心的Transfer事件
const abiWETH = [
    "event Transfer(address indexed from, address indexed to, uint amount)"
];

声明WETH合约实例。

// 测试网WETH地址
const addressWETH = '0xb4fbf271143f4fbf7b91a5ded31805e42b2208d6'
// 声明合约实例
const contract = new ethers.Contract(addressWETH, abiWETH, provider)

获取过去10个区块内的Transfer事件,并打印出1个。我们可以看到,topics中有3个数据,对应事件哈希,from,和to;而data中只有一个数据amount。另外,ethers还会根据ABI自动解析事件,结果显示在args成员中。

// 得到当前block
const block = await provider.getBlockNumber()
console.log(`当前区块高度: ${block}`);
console.log(`打印事件详情:`);
const transferEvents = await contract.queryFilter('Transfer', block - 10, block)
// 打印第1个Transfer事件
console.log(transferEvents[0])

读取事件的解析结果。

// 解析Transfer事件的数据(变量在args中)
console.log("\n2. 解析事件:")
const amount = ethers.formatUnits(ethers.getBigInt(transferEvents[0].args["amount"]), "ether");
console.log(`地址 ${transferEvents[0].args["from"]} 转账${amount} WETH 到地址 ${transferEvents[0].args["to"]}`)

总结

本文主要介绍了Solidity中的事件,并介绍如何用ethers检索智能合约释放的事件。要注意的一点:要检索的事件必须包含在合约abi中。