# Solidity 课程 9: 事件

By [Novar](https://paragraph.com/@novar) · 2022-05-23

---

概念
--

**事件**允许我们方便地使用 EVM 的日志基础设施。 我们可以先在 dapp 的用户界面中监听事件，然后在EVM 的日志机制中发射事件，从而在监听事件处调用 Javascript 回调函数。它具有两个特点：

*   响应：应用程序（`ether.js`）可以通过`RPC`接口订阅和监听这些事件，并在前端做响应。
    
*   经济：事件是`EVM`上比较经济的存储数据的方式，每个大概消耗2,000-5,000 `gas`不等。相比之下，存储一个新的变量至少需要20,000 `gas`。
    

使用规则
----

### 定义

简单例子：

    contract Event {
     
        event Log(address indexed sender, string message);
        event AnotherLog();
    
        function test() public {
            emit Log(msg.sender, "Hello World!");
            emit Log(msg.sender, "Hello EVM!");
            emit AnotherLog();
        }
    }
    

事件的声明由`event`关键字开头，然后跟事件名称，括号里面写好事件需要记录的变量类型和变量名。以`ERC20`代币合约的`Transfer`事件为例：

    event Transfer(address indexed from, address indexed to, uint256 value);
    

我们可以看到，`Transfer`事件共记录了3个变量`from`，`to`和`value`，分别对应代币的转账地址，接收地址和转账数量。同时`from`和`to`前面带着`indexed`关键字，表示很重要，程序可以轻松的筛选出特定转账地址和接收地址的转账事件。**每个事件最多有3个带**`indexed`**的变量。**

### 发送事件

我们可以在函数里释放事件。在下面的例子中，每次用\_transfer()函数进行转账操作的时候，都会释放Transfer事件，并记录相应的变量。

    // 定义_transfer函数，执行转账逻辑
    function _transfer(
        address from,
        address to,
        uint256 amount
    ) external {
    
        _balances[from] = 10000000; // 给转账地址一些初始代币
    
        _balances[from] -=  amount; // from地址减去转账数量
        _balances[to] += amount; // to地址加上转账数量
    
        // 释放事件
        emit Transfer(from, to, amount);
    }
    

`emit`：是发送事件的关键字，初始化事件直接通过事件名+参数。

### 监听

定义事件：

    pragma solidity ^0.4.0;
    
    contract ClientReceipt {
        event Deposit(
            address indexed _from,
            bytes32 indexed _id,
            uint _value
        );
    
        function deposit(bytes32 _id) public payable {
            // 我们可以过滤对 `Deposit` 的调用，从而用 Javascript API 来查明对这个函数的任何调用（甚至是深度嵌套调用）。
            Deposit(msg.sender, _id, msg.value);
        }
    }
    

使用 JavaScript API 调用事件的用法如下：

    var abi = /* abi 由编译器产生 */;
    var ClientReceipt = web3.eth.contract(abi);
    var clientReceipt = ClientReceipt.at("0x1234...ab67" /* 地址 */);
    
    var event = clientReceipt.Deposit();
    
    // 监视变化
    event.watch(function(error, result){
        // 结果包括对 `Deposit` 的调用参数在内的各种信息。
        if (!error)
            console.log(result);
    });
    
    // 或者通过回调立即开始观察
    var event = clientReceipt.Deposit(function(error, result) {
        if (!error)
            console.log(result);
    });
    

总结
--

这节学习了事件的定义、发送和监听。

**全部代码：**

[https://github.com/Luca-Hsu/SuperSolidity/blob/main/09\_Events/Events.sol](https://github.com/Luca-Hsu/SuperSolidity/blob/main/09_Events/Events.sol)

**Reference:**

[https://mirror.xyz/ninjak.eth/nGSCuFbPHMo8mL1ErZMUwOZG\_OUECzIWEsGhX0a5eOw](https://mirror.xyz/ninjak.eth/nGSCuFbPHMo8mL1ErZMUwOZG_OUECzIWEsGhX0a5eOw)

[https://solidity-by-example.org/events](https://solidity-by-example.org/events)

[https://solidity-cn.readthedocs.io/zh/develop/contracts.html#events](https://solidity-cn.readthedocs.io/zh/develop/contracts.html#events)

---

*Originally published on [Novar](https://paragraph.com/@novar/solidity-9)*
