事件
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"]
}
}
了解事件的其他资源