solidity: event机制

发布时间 2023-08-24 17:07:42作者: 若-飞

以太坊的事件(Event)机制是一种在智能合约中定义和触发事件的方式,用于实现合约与外部世界的通信和提供交易的可追溯性。事件机制可以让智能合约在特定条件满足时触发事件,并将相关信息记录在以太坊区块链上的日志中。

以下是以太坊事件机制的主要特点和使用方法:

  1. 定义事件:在 Solidity 合约中使用 event 关键字来定义事件。事件的定义包括事件名称和事件参数列表。例如:

    solidity
    event Transfer(address indexed from, address indexed to, uint256 value);
    ```
    
    上述示例定义了一个名为 "Transfer" 的事件,包含三个参数:`from`、`to` 和 `value`。
  2. 触发事件:在合约中的适当位置使用 emit 关键字来触发事件。例如,在转账函数中触发 "Transfer" 事件:

    solidity
    function transfer(address to, uint256 value) public {
        // 执行转账逻辑
        emit Transfer(msg.sender, to, value);
    }
    ```
    
    通过 `emit` 关键字,合约可以触发事件并传递事件参数的值。
  3. 事件参数:事件可以包含多个参数,每个参数可以具有不同的类型。事件参数可以用于记录和传递各种信息,如地址、整数、字符串等。事件参数还可以使用 indexed 关键字来标记为索引参数,以便更高效地进行事件过滤和搜索。

  4. 事件日志:当合约触发事件时,事件及其参数将被记录在以太坊区块链上的事件日志中。事件日志是一种特殊的数据结构,它包含了事件的名称、参数值和其他元数据。事件日志可以通过交易收据(transaction receipt)或以太坊区块链上的事件查询来访问和检索。

  5. 事件监听:DApp或其他外部系统可以监听合约的事件,以便在事件触发时采取相应的操作。通过监听事件,可以实现实时数据更新、用户界面的反馈、通知和其他与合约交互相关的功能。

以太坊的事件机制为智能合约提供了一种与外部世界通信的机制,使得合约的状态变化和重要事件能够被记录、检索和响应。事件机制在构建去中心化应用程序(DApp)和与智能合约集成的外部系统中非常有用,可以提供更好的用户体验和数据可追溯性。

 

以太坊的日志(Log)也是上链的,它们会被记录在以太坊区块链上。

当合约触发一个事件并记录日志时,该日志会被包含在合约交易的交易收据(transaction receipt)中,并且该交易将被打包到一个区块中。区块链上的每个节点都会验证和存储这个区块,从而将日志信息永久地记录在区块链中。

通过区块链的去中心化特性,以太坊日志具有以下特点:

  1. 不可篡改性:一旦日志被记录在区块链上,它们就无法更改或删除。这确保了事件的可追溯性和真实性。

  2. 可验证性:任何人都可以通过访问以太坊区块链上的交易收据,验证特定合约事件的发生和相关信息。

  3. 公开透明:以太坊区块链上的日志是公开可见的,任何人都可以查看和分析日志信息。

  4. 事件驱动的编程:以太坊日志为智能合约提供了事件驱动的编程模型。DApp或其他外部系统可以监听合约的事件,并根据事件触发执行相应的操作。

需要注意的是,以太坊的日志是一种存储数据的机制,而并非用于执行智能合约的代码逻辑。日志通常用于记录合约内部发生的事件和状态变化,以及与外部世界的通信。

 

我们写个例子来看下event的机制:

合约代码:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract FallbackLogContract  {
    event Basiya(string funName, address from, uint256 value, bytes data);

    receive() external payable {
        emit Basiya("receive", msg.sender, msg.value, "");
    }
}

定义了一个接收以太币的函数receive,内部调用一个Basiya的event:

我们给该合约发起1个以太币交易试试:

我们来看下具体的交易以及日志:


[vm]from: 0x5B3...eddC4to: FallbackLogContract.(receive) 0x049...A1Fd3value: 1000000000000000000 weidata: 0xlogs: 1hash: 0x6b7...03df8
status	true Transaction mined and execution succeed
transaction hash	0x6b79ee5deb7fa0e6a7ed4e6cbb99476aa36af3ca1e01c8d24ffc1e44fa003df8
block hash	0xaf99efc80bfefb9793678e51bb880aaf0a7fbc8f76ffc6e78862a7990cd9a005
block number	30
from	0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
to	FallbackLogContract.(receive) 0x0498B7c793D7432Cd9dB27fb02fc9cfdBAfA1Fd3
gas	27927 gas
transaction cost	24284 gas 
execution cost	3284 gas 
input	0x
decoded input	-
decoded output	 - 
logs	[
	{
		"from": "0x0498B7c793D7432Cd9dB27fb02fc9cfdBAfA1Fd3",
		"topic": "0xc35d6c22b7aaa1306d99950e95c973a1e0d6e6d6668b2e560df6153bec9e9fbc",
		"event": "Basiya",
		"args": {
			"0": "receive",
			"1": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
			"2": "1000000000000000000",
			"3": "0x",
			"funName": "receive",
			"from": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
			"value": "1000000000000000000",
			"data": "0x"
		}
	}
]
val	1000000000000000000 wei

可以看到logs那边有个event:Basiya, args里面的参数就是我们函数的内容:funName, from, value, data等 以及receive的参数信息