说明
Events 是 Solidity 中记录事件的工具,可以简单理解为日志。Events 的优点在于,一是能够利用较少的 Gas 就能将数据记录在区块链上,二是可以方便链下对链上数据进行监听。
在前面http://oooverflow.com/2022/09/20/web3-filter-usage/的这篇文章介绍web3py如何使用filter的方式对event进行监控。本篇文章将着重介绍event事件的原理。
基本上,在很多关键的函数中,都会增加一些event事件日志。以第三方openzeppelin为例:
1 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); |
openzeppelin内部在调用_mint()函数时,通过Transfer(address(0), account, amount)通过0地址向目标地址转了amount数量的NFT。以实际的这个0xeb144bd886f3ffcbcd6be12c660dbce1595caa2461a28f042963c2534fa340b7mint交易为例,在mint时对应产生了一个event事件。
最终在etherscan的交易对应的event中就可以看到具体信息:
介绍
在solidity中,通过event关键词来申明一个event事件方法。比如:
1 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); |
通过emit关键词生成一个event事件发布到链上。
1 | emit Transfer(address(0), to, tokenId); |
发布到链上之后,通过etherscan找到对应的交易的log信息就可以查看到对应的event事件了。很多时候一个交易可能不止一个event事件,而是有多个event事件。像0x04a9f5007f353b705aa29d33234a6bcfa61793da36e6b710b2b01446e810b4dd这个交易就存在18个event事件。
Event结构分析
Address
表示交互的合约地址,在[]这个例子中的address就是代表了mint的地址是0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d
Topic
一般情况下Topics会包含4个topic。以()为例,具体的topic信息如下所示:
Topics中最多只能包含4个Topic。有关Topic的含义在前面一篇文章Web3的Filter使用入门介绍中也有简单的说明。
topic 的第一个字段默认是事件签名,在上例中,Topics[0] 中的 0xddf25…b3ef 哈希值就是 Transfer 事件的签名:计算方法是:
1 | event_signature_hash = Web3.keccak(text="Transfer(address,address,uint256)").hex() |
与上述实际案例中的Topics[0]的内容完全是一致的。
Topics数组中最多只有4个元素,第一个元素Topic[0]固定就是函数的hash值。剩余的三个字段可以使用indexed,表示是可以被检索的。这些检索字段就可以用在event过滤函数中,对特定的indexed的内容进行过滤。还是以上述的例子为例:
1 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); |
from,to,tokenId前面是设置成为了indexed,表示都是可以检索的。至于无法检索的参数就会放在data字段里面。如果所有的参数全部被设置为了indexed,那么data字段就是空了。
以event swap中的例子为例:
1 | event Swap( |
其中,只有sender和to被设置为了indexed表示可以被检索的。那么其他的参数内容就会全部保留在data中。

data
签名也说过了,对于没有设置为indexed的参数,其参数内容会全部写入到data字段中。我们还是以一个正常的例子来说明:
1 | pragma solidity 0.8.10; |
此时,message没有设置indexed,按照预期这个值就会被放到data中。实际的结果如下图所示:
可以看到最终的message数据确实是被放在了data中。
接下来,尝试将message配置为indexed,按照预期message的值会被放入到topics[3]中。
1 | pragma solidity 0.8.10; |
实际情况如下所示, message的内容的确是被放入到了Topics[3]中。
由于所有的参数全部被加入了indexed,所以data的内容就是0x。通过计算Hello world,
1 | event_signature_hash = Web3.keccak(text="Hello World").hex() |
和实际的现实是一致的。
Gas消耗
以太坊黄皮书对Event的Gas的规定如下:
日志的基础费用是 375 Gas。另外每个 topic 同样需要花费 375 Gas,data 中每个字节需要花费 8 Gas。
因此,我们前面的 Transfer 事件花费的 Gas 为:
1756 = 375(基础费用)+ 375 * 3(3 个 topic)+ 32 * 8(data 中共 32 字节)