Event原理介绍和格式详解

说明

Events 是 Solidity 中记录事件的工具,可以简单理解为日志。Events 的优点在于,一是能够利用较少的 Gas 就能将数据记录在区块链上,二是可以方便链下对链上数据进行监听。

在前面http://oooverflow.com/2022/09/20/web3-filter-usage/的这篇文章介绍web3py如何使用filter的方式对event进行监控。本篇文章将着重介绍event事件的原理。

基本上,在很多关键的函数中,都会增加一些event事件日志。以第三方openzeppelin为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");

_beforeTokenTransfer(address(0), to, tokenId);

// Check that tokenId was not minted by `_beforeTokenTransfer` hook
require(!_exists(tokenId), "ERC721: token already minted");

unchecked {
// Will not overflow unless all 2**256 token ids are minted to the same owner.
// Given that tokens are minted one by one, it is impossible in practice that
// this ever happens. Might change if we allow batch minting.
// The ERC fails to describe this case.
_balances[to] += 1;
}

_owners[tokenId] = to;

emit Transfer(address(0), to, tokenId);

_afterTokenTransfer(address(0), to, 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
2
event_signature_hash = Web3.keccak(text="Transfer(address,address,uint256)").hex()
print(event_signature_hash) # 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

与上述实际案例中的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
2
3
4
5
6
7
8
event Swap(
address indexed sender,
uint256 amount0In,
uint256 amount1In,
uint256 amount0Out,
uint256 amount1Out,
address indexed to
);

其中,只有senderto被设置为了indexed表示可以被检索的。那么其他的参数内容就会全部保留在data中。

data

签名也说过了,对于没有设置为indexed的参数,其参数内容会全部写入到data字段中。我们还是以一个正常的例子来说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
pragma solidity 0.8.10;

contract EventsDemo {
event Message(
address indexed from,
address indexed to,
string message
);

function sendMessage(address to, string memory message) public {
emit Message(msg.sender, to, message);
}
}

此时,message没有设置indexed,按照预期这个值就会被放到data中。实际的结果如下图所示:

image-20220922155604710

可以看到最终的message数据确实是被放在了data中。

接下来,尝试将message配置为indexed,按照预期message的值会被放入到topics[3]中。

1
2
3
4
5
6
7
8
9
10
11
12
13
pragma solidity 0.8.10;

contract EventsDemo {
event Message(
address indexed from,
address indexed to,
string indexed message
);

function sendMessage(address to, string memory message) public {
emit Message(msg.sender, to, message);
}
}

实际情况如下所示, message的内容的确是被放入到了Topics[3]中。

image-20220922160622612

由于所有的参数全部被加入了indexed,所以data的内容就是0x。通过计算Hello world,

1
2
event_signature_hash = Web3.keccak(text="Hello World").hex()
print(event_signature_hash) # 0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba

和实际的现实是一致的。

Gas消耗

以太坊黄皮书对Event的Gas的规定如下:

日志的基础费用是 375 Gas。另外每个 topic 同样需要花费 375 Gas,data 中每个字节需要花费 8 Gas。

因此,我们前面的 Transfer 事件花费的 Gas 为:

1756 = 375(基础费用)+ 375 * 3(3 个 topic)+ 32 * 8(data 中共 32 字节)

参考

  1. https://mirror.xyz/xyyme.eth/qMydtAiNX1f_uD9HZPgShDfxFEsU1J_hwgaTCTGBZJI
  2. https://medium.com/mycrypto/understanding-event-logs-on-the-ethereum-blockchain-f4ae7ba50378
文章作者: Oooverflow
文章链接: https://www.oooverflow.com/2022/09/21/solidity-event-intro/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Oooverflow