Events
本节是《Solidity by Example》的中文翻译与深入讲解,专为零基础或刚接触区块链开发的小白朋友打造。我们将通过“示例 + 解说 + 提示”的方式,带你逐步理解每一段 Solidity 代码的实际用途与背后的逻辑。
Solidity 是以太坊等智能合约平台使用的主要编程语言,就像写网页要用 HTML 和 JavaScript,写智能合约就需要会 Solidity。
如果你从没写过区块链代码也没关系,只要你了解一点点编程概念,比如“变量”“函数”“条件判断”,我们就能从最简单的例子开始,一步步建立你的 Solidity 编程思维。
Events
事件
事件允许在以太坊区块链上记录日志。事件的一些用途包括:
- 监听事件并更新用户界面
- 作为一种廉价的存储方式
什么是 Solidity 事件?
在 Solidity 中,事件(Events)是智能合约与外部世界(如前端应用或链下服务)通信的机制,用于记录特定操作或状态变化到区块链的日志(logs)。
-
比喻:事件像合约发出的广播信号,通知外界“某事发生了”,前端或链下服务可以监听这些信号并做出响应(如更新界面)。
-
事件数据存储在区块链的日志中,成本远低于修改
storage
变量,是高效的链上记录方式。 -
事件的用途:
- 监听事件并更新用户界面:
- 前端(如 DApp)通过 Web3.js 或 Ethers.js 监听事件,实时更新页面显示(如显示转账记录)。
- 比喻:像餐厅服务员喊“订单完成”,前台收到信号更新订单状态。
- 廉价存储:
- 事件数据存储在区块链的交易日志(logs)中,Gas 成本远低于
storage
变量(约 375 Gas 每 32 字节 vs. 20,000+ Gas 写storage
)。 - 适合记录历史数据(如交易记录、状态变化),但不能直接在合约内读取。
- 比喻:像在账本上记笔记,便宜但不能直接翻回去查(需链下工具)。
- 事件数据存储在区块链的交易日志(logs)中,Gas 成本远低于
- 监听事件并更新用户界面:
// SPDX-License-Identifier: MIT
// 使用 MIT 许可证,允许自由使用、修改和分发代码。pragma solidity ^0.8.26;
// 指定 Solidity 编译器版本,必须为 0.8.26 或更高(但低于 0.9.0)。contract Event {// 定义一个名为 Event 的智能合约,展示事件的定义和触发。// Event declaration// Up to 3 parameters can be indexed.// Indexed parameters help you filter the logs by the indexed parameter// 事件声明// 最多可以有 3 个参数被索引(indexed)。// 被索引的参数有助于通过该参数过滤日志。event Log(address indexed sender, string message);// 定义一个 AscII text// 定义一个事件 Log,包含两个参数:// - sender:address 类型,标记为 indexed(索引),可通过该参数高效过滤日志。// - message:string 类型,未索引,仅存储在日志数据中。// indexed 参数存储在日志的 topics 中,方便链下工具(如 Web3.js)按 sender 过滤事件。// 最多 3 个参数可标记为 indexed(Solidity 限制)。// 事件本身不消耗 storage,仅记录在交易日志中。event AnotherLog();// 定义一个无参数的事件 AnotherLog。// 无参数事件仅记录事件发生(无额外数据),适合简单通知场景。// 不带 indexed 参数,日志仅包含事件签名。function test() public {// 定义一个公共函数 test,用于触发事件。// public:可被外部和内部调用。// 不标记为 pure 或 view,因为触发事件会写入区块链日志,消耗 Gas。emit Log(msg.sender, "Hello World!");// 触发 Log 事件,记录:// - sender:调用者的地址(msg.sender,索引参数)。// - message:"Hello World!"(非索引参数)。// 事件数据写入交易日志,链下可监听。emit Log(msg.sender, "Hello EVM!");// 再次触发 Log 事件,记录:// - sender:调用者的地址(msg.sender,索引参数)。// - message:"Hello EVM!"(非索引参数)。emit AnotherLog();// 触发 AnotherLog 事件,无参数,仅记录事件发生。// 适合简单状态通知(如“某操作完成”)。}
}
Event
合约展示如何定义和触发事件:
- 定义两个事件:
Log
(带参数,部分索引)和AnotherLog
(无参数)。 - 使用
test
函数触发事件,记录调用者地址和消息到区块链日志。 - 演示事件的基本用法和
indexed
参数的作用。
事件的本质
- 事件是 Solidity 合约的日志记录机制,将数据写入区块链的交易日志,供链下工具(如前端、分析服务)读取和处理。
- 比喻:
- 事件如餐厅的公告板,记录“某人点了什么菜”(
Log(sender, message)
),前端可读取并显示给用户。 indexed
参数像公告板上的分类标签(如按用户地址排序),便于快速查找。- 事件不像
storage
变量(昂贵的账本),而是廉价的“便签”,记录后不可更改或直接查询。
- 事件如餐厅的公告板,记录“某人点了什么菜”(
- 核心功能:
- 记录操作:如记录转账、状态更新等,供链下工具(如 DApp)监听。
- 高效存储:比修改
storage
变量便宜,适合保存历史记录。 - 链下交互:通过
indexed
参数高效过滤日志(如按sender
查找事件)。
代码功能
- 事件
Log
:- 记录调用者地址(
sender
,indexed
)和消息(message
)。 indexed
参数sender
存储在日志的 topics 中,方便按地址过滤。message
存储在日志的 data 字段,成本低但过滤效率较低。
- 记录调用者地址(
- 事件
AnotherLog
:- 无参数,仅记录事件发生,适合简单通知(如“操作完成”)。
- 函数
test
:- 触发三次事件:两次
Log
(带不同消息)一次AnotherLog
。 - 模拟合约操作(如用户交互)并记录到区块链日志。
- 触发三次事件:两次
事件的注意事项
-
索引参数(
indexed
):- 最多 3 个
indexed
参数,存储在 topics,方便链下过滤(如按sender
查找)。 - 非索引参数存储在 data,成本低但过滤效率较低。
- 选择合适的
indexed
参数(如地址、ID),避免浪费 topics。
- 最多 3 个
-
Gas 成本:
- 触发事件约 375 Gas 每 topic(事件签名 + 索引参数),data 每字节约 8 Gas。
- 比
storage
修改(20,000+ Gas)便宜,适合记录历史数据。 - 复杂参数(如长字符串)增加 Gas,尽量精简
data
字段。
-
安全性:
-
事件仅记录日志,不影响合约逻辑,需确保触发条件正确。
-
结合错误处理(如require、revert)验证输入:
function deposit() public payable {require(msg.value > 0, "No value sent");emit Deposit(msg.sender, msg.value); }
-
使用修饰器(如onlyOwner)限制敏感事件触发(参考历史对话):
modifier onlyOwner() {require(msg.sender == owner, "Not owner");_; } function logAdminAction() public onlyOwner {emit Log(msg.sender, "Admin action"); }
-
-
链下交互:
- 事件不可在合约内直接读取,需链下工具(如 Web3.js、Ethers.js)处理。
- 确保事件参数清晰(如
indexed sender
便于过滤),便于 DApp 使用。