事件
Solidity 事件在 EVM 的日志记录功能之上提供了一个抽象。应用程序可以通过以太坊客户端的 RPC 接口订阅和监听这些事件。
事件是合约的可继承成员。当你调用它们时,它们会导致参数存储在交易日志中——区块链中的一种特殊数据结构。这些日志与合约的地址相关联,并入区块链中,并在区块可访问时一直存在(从现在开始一直存在,但这可能会随着 Serenity 而改变)。无法从合约内部访问日志及其事件数据(甚至无法从创建它们的合约中访问)。
可以为日志请求 Merkle 证明,因此如果外部实体提供具有此类证明的合约,它可以检查日志是否确实存在于区块链中。你必须提供区块头,因为合约只能看到最后的 256 个区块哈希。
您可以将属性添加indexed到最多三个参数,这会将它们添加到称为“主题”的特殊数据结构中,而不是日志的数据部分。一个主题只能包含一个单词(32 个字节),因此如果您对索引参数使用引用类型,则该值的 Keccak-256 哈希值将存储为主题。
所有没有该indexed属性的参数都被ABI 编码 到日志的数据部分。
主题允许您搜索事件,例如在为某些事件过滤一系列块时。您还可以按发出事件的合约地址过滤事件。
例如,下面的代码使用 web3.jssubscribe("logs") 方法过滤与某个主题与某个地址值匹配的日志:
var options = { fromBlock: 0, address: web3.eth.defaultAccount, topics: ["0x0000000000000000000000000000000000000000000000000000000000000000", null, null] }; web3.eth.subscribe('logs', options, function (error, result) { if (!error) console.log(result); }) .on("data", function (log) { console.log(log); }) .on("changed", function (log) { });
事件签名的哈希是主题之一,除非您使用说明anonymous符声明事件。这意味着无法按名称过滤特定的匿名事件,只能按合约地址过滤。匿名事件的优点是部署和调用成本更低。它还允许您声明四个索引参数,而不是三个。
笔记
由于事务日志只存储事件数据而不存储类型,因此您必须知道事件的类型,包括索引哪个参数以及事件是否是匿名的,以便正确解释数据。特别是,可以使用匿名事件“伪造”另一个事件的签名。
// SPDX-License-Identifier: GPL-3.0 pragma solidity >=0.4.21 <0.9.0; contract ClientReceipt { event Deposit( address indexed from, bytes32 indexed id, uint value ); function deposit(bytes32 id) public payable { // Events are emitted using `emit`, followed by // the name of the event and the arguments // (if any) in parentheses. Any such invocation // (even deeply nested) can be detected from // the JavaScript API by filtering for `Deposit`. emit Deposit(msg.sender, id, msg.value); } }
JavaScript API 中的使用如下:
var abi = /* abi as generated by the compiler */; var ClientReceipt = web3.eth.contract(abi); var clientReceipt = ClientReceipt.at("0x1234...ab67" /* address */); var depositEvent = clientReceipt.Deposit(); // watch for changes depositEvent.watch(function(error, result){ // result contains non-indexed arguments and topics // given to the `Deposit` call. if (!error) console.log(result); }); // Or pass a callback to start watching immediately var depositEvent = clientReceipt.Deposit(function(error, result) { if (!error) console.log(result); });
上面的输出如下所示(修剪):
{ "returnValues": { "from": "0x1111…FFFFCCCC", "id": "0x50…sd5adb20", "value": "0x420042" }, "raw": { "data": "0x7f…91385", "topics": ["0xfd4…b4ead7", "0x7f…1a91385"] } }
了解事件的其他资源