文章目录
- Solidity智能合约事件(event)
-
- 什么是event
- 事件event有什么作用
- 日志内容位于区块链的什么地方?
- 【重要】以太坊交易获取
- [如何在 Solidity 中使用事件?](#如何在 Solidity 中使用事件?)
- 参考
Solidity智能合约事件(event)
什么是event
EVM有一个日志功能,用于将数据"写"到智能合约之外的数据结构中。其中一个重要的数据是Solidity事件。事件允许我们"打印"在区块链上的信息,这种方式比在智能合约中保存到公共存储变量更容易搜索,且更省gas费。
当被发送事件(调用)时,会触发参数存储到交易的日志中(一种区块链上的特殊数据结构)。这些日志与合约的地址关联,并记录到区块链中.
Solidity event,在更通用的以太坊和 EVM 中称为logs,Solidity中的事件(event)是EVM上日志的抽象。日志是指事件保存在区块链上的数据。事件强调操作行为,日志强调存储内容。
以太坊中的Events和Logs基本上算是同一个概念。Solidity和web3.js中称为Events,以太坊黄皮书中称为Logs。可以理解为:以太坊通过Logs实现Events(事件)功能。智能合约代码通过LOG 将日志写入区块链中。
事件event有什么作用
事件Event的作用的可以总结为以下几点:
- 异步获取执行结果
- 和前端交互
我们可以通过监听event来做到及时更新前端显示 - 存储合约数据,相比storage要便宜很多(storage存储的大概价格为:每32字节需要消耗20000Gas,而日志存储价格大概为每字节8Gas)
事件有 3 中主要的使用场景
- 智能合约给用户的返回值
- 异常触发
- 更便宜的数据存储
日志内容位于区块链的什么地方?
日志内容是交易收据(Transaction Receipts)的一部分 ,整个日志内容,包括Receipts的其它内容会生成一个ReceiptsRoot存储在区块的头部。而完整数据则是链下存储。
【重要】以太坊交易获取
以太坊交易信息及event、input、logs、topics等概念机制
参考URL: https://learnblockchain.cn/article/3274
当上述事件在合约中调用后,我们通过其交易hash获取交易信息。从以太坊得到一条交易信息的方式有两种:
- eth_getTransactionByHash: :返回指定交易对应的交易信息
- eth_getTransactionReceipt :返回指定交易对应的收据信息
eth_getTransactionByHash 返回的具体信息如下
bash
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockHash": "0xb0d0e3b6c5e59b7b3e7e16701f6d6cb0c3c93487415b03839e88b3f7a241c528",
"blockNumber": "0xd19505",
"from": "0xb8262c6a2dcabd92a77df1d5bd074afd07fc5829",
"gas": "0x10e3d",
"gasPrice": "0x274daee580",
"maxFeePerGas": "0x2d48ddd9f1",
"maxPriorityFeePerGas": "0x6ccc91d0",
"hash": "0xae2a33da8396a6bc40e874b0f32b9967113a3dbf071ab1290c44c62d86873d36",
"input": "0xa9059cbb000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000016512c902",
"nonce": "0x14",
"to": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"transactionIndex": "0x71",
"value": "0x0",
"type": "0x2",
"accessList": [],
"chainId": "0x1",
"v": "0x1",
"r": "0xa1d7455286525df11602aab34e9e8ab21b092e2c7853a0d6beca0dfb2a78b2e8",
"s": "0x75a485b8c378173a829b27a2e55312311fdb33c68ae65f4c74e5f9cc0a748e0d"
}
}
eth_getTransactionReceipt
bash
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"blockHash": "0xb0d0e3b6c5e59b7b3e7e16701f6d6cb0c3c93487415b03839e88b3f7a241c528",
"blockNumber": "0xd19505",
"contractAddress": null,
"cumulativeGasUsed": "0x6c847e",
"effectiveGasPrice": "0x274daee580",
"from": "0xb8262c6a2dcabd92a77df1d5bd074afd07fc5829",
"gasUsed": "0xa169",
"logs": [
{
"address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x000000000000000000000000b8262c6a2dcabd92a77df1d5bd074afd07fc5829",
"0x000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7"
],
"data": "0x000000000000000000000000000000000000000000000000000000016512c902",
"blockNumber": "0xd19505",
"transactionHash": "0xae2a33da8396a6bc40e874b0f32b9967113a3dbf071ab1290c44c62d86873d36",
"transactionIndex": "0x71",
"blockHash": "0xb0d0e3b6c5e59b7b3e7e16701f6d6cb0c3c93487415b03839e88b3f7a241c528",
"logIndex": "0xa0",
"removed": false
}
],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000010000000000000000000000000000000000000000000000000000000008000000000080000000000000000000000000000000000000000000000000000000000000000000200000000000000010000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000080000000000000000000000000000000000000004000000002000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000200",
"status": "0x1",
"to": "0xdac17f958d2ee523a2206206994597c13d831ec7",
"transactionHash": "0xae2a33da8396a6bc40e874b0f32b9967113a3dbf071ab1290c44c62d86873d36",
"transactionIndex": "0x71",
"type": "0x2"
}
}
getTransaction返回结果中包含input,而getTransactionReceipts返回结果中包含logs。
如何在 Solidity 中使用事件?
solidity中,使用关键字event来定义事件,其中参数列表就是需要保存到区块链上的数据。
- 在 solidity 中如何定义事件?
- 事件在函数外部定义
- 事件名必须和函数名称有所区分
- 事件名称必须是大写
- 为了减少混淆事件名称至少得 2 个字母和函数名称不一样
事件的声明由event关键字开头,然后跟事件名称 ,括号里面写好事件需要记录的变量类型 和变量名。以ERC20代币合约的Transfer事件为例:
javascript
event Transfer(address indexed from, address indexed to, uint256 value);
在这个事件中,有两种参数:有索引的和无索引的。索引参数也被称为"主题",是事件中的可搜索参数。
每个事件最多有3个带indexed的变量,Transfer事件共记录了3个变量from,to和value,分别对应代币的转账地址,接收地址和转账数量。同时from和to前面带着indexed关键字,表示很重要,程序可以轻松的筛选出特定转账地址和接收地址的转账事件。
一个事件可以分解为:
- Address:地址,
发出事件的合约或账户的地址
。 当前交易被调用的合约地址 - Topics:主题,事件的索引参数。
- Data:数据,事件的非索引参数的ABI编码或"哈希"。由于我们知道合约的ABI(因为我们在Etherscan上验证了合约),我们可以在"Dec"或"Decoded"模式下查看它,或者在其原始的"hex"、"Hexidecimal"或 "Encoded"模式下查看。如果我们没有验证过合约,我们就无法看到解码的版本。
-
如何来使用事件
在函数内部通过 emit 关键词,后面跟上事件的名称和事件所需的参数(如果有的话)。
如果函数会触发事件,则不能将其定义为view或pure。这是因为触发事件会将数据写入区块链(到日志中)。
-
匿名事件
在 solidity 中事件可以是匿名的。默认情况下所有的事件都会有一个主题,来自定义时候的函数名称。使用 anonymous 来记录一个没有主题的事件。匿名事件也会成为 ABI 的一部分。
javascript
event Message(
address _recipient,
string _message
) anonymous;
如果事件声明为anonymous
,在合约 ABI 中,事件的"anonymous"字段将标记为true。
匿名事件的使用成本更低。但是匿名事件不能通过名称去筛选,只能通过合约地址。
参考
web3.js与智能合约交互监听合约事件
参考URL: https://www.b2bchain.cn/8083.html
web3j开发以太坊过滤器(filter)和智能合约事件(event)
参考URL: https://blog.csdn.net/rejames/article/details/81294638
智能合约Solidity教程-事件和日志(一)
参考URL: https://blog.csdn.net/weixin_34252090/article/details/88720360
[强烈推荐,写的很详细]以太坊交易信息及event、input、logs、topics等概念机制