CryptoZombies第1课13章Solidity事件概念及代码解析求助
Solidity事件(Events)核心概念与示例代码逐段讲解
事件核心概念
Solidity的事件是EVM提供的合约与链下系统通信的原生能力,核心特性如下:
- 事件触发后会将携带的参数打包写入对应交易的链上日志,日志与合约地址永久绑定,可被链上节点校验,但合约自身无法读取历史日志
- 日志写入的gas成本远低于合约状态存储,适合做链上动作的通知载体
- 链下服务(DApp前端、链下索引程序、脚本)可以订阅指定合约的指定事件,事件触发后会实时收到推送,不需要持续轮询链上数据
- 事件参数可以加
indexed修饰符,支持链下服务按参数值过滤检索对应日志,本次示例为事件基础用法,未使用该修饰符
逐段代码拆解
示例完整代码如下:
pragma solidity >=0.5.0 <0.6.0; contract ZombieFactory { event NewZombie(uint zombieId, string name, uint dna); uint dnaDigits = 16; uint dnaModulus = 10 ** dnaDigits; struct Zombie { string name; uint dna; } Zombie[] public zombies; function _createZombie(string memory _name, uint _dna) private { uint id = zombies.push(Zombie(_name, _dna)) - 1; emit NewZombie(id, _name, _dna); } function _generateRandomDna(string memory _str) private view returns (uint) { uint rand = uint(keccak256(abi.encodePacked(_str))); return rand % dnaModulus; } function createRandomZombie(string memory _name) public { uint randDna = _generateRandomDna(_name); _createZombie(_name, randDna); } }
逐段逻辑说明:
pragma solidity >=0.5.0 <0.6.0;
编译器版本声明,限定合约只能使用0.5.x系列版本的Solidity编译器编译,避免不同版本语法不兼容导致的编译错误。contract ZombieFactory {
声明名为ZombieFactory的合约,所有合约逻辑都包裹在该合约的作用域内。event NewZombie(uint zombieId, string name, uint dna);
事件定义,语法和函数签名类似:指定事件名为NewZombie,触发时需要传入3个参数,分别为新僵尸的ID、名称、DNA值。事件定义必须放在合约顶层,不能嵌套在函数内部。uint dnaDigits = 16;、uint dnaModulus = 10 ** dnaDigits;
两个合约状态变量,dnaDigits定义僵尸DNA固定为16位长度,dnaModulus取值为10的16次方,后续生成随机DNA时取模用,保证生成值不超过16位。struct Zombie { string name; uint dna; }
自定义Zombie结构体,每个僵尸实例包含两个属性:字符串类型的名称、无符号整数类型的DNA值。Zombie[] public zombies;
声明Zombie类型的公开动态数组,用于存储所有生成的僵尸实例;public修饰符会自动为该数组生成同名查询函数,外部可传入索引值查询对应位置的僵尸数据。function _createZombie(string memory _name, uint _dna) private { ... }
私有内部函数,函数名以下划线开头是Solidity社区约定的私有函数命名规范,仅允许合约内部调用,外部无法直接访问:- 参数
_name、_dna用memory修饰,指定参数临时存储在内存中,不写入链上状态 uint id = zombies.push(Zombie(_name, _dna)) - 1;:向zombies数组末尾推入一个新的僵尸实例,Solidity 0.5版本中数组push方法返回数组更新后的长度,减1即为新插入僵尸在数组中的索引ID(数组索引从0开始计数)emit NewZombie(id, _name, _dna);:事件触发逻辑,使用emit关键字调用之前定义的NewZombie事件,传入新僵尸的ID、名称、DNA三个参数;该逻辑执行时会将参数打包写入交易日志,不会修改合约自身的状态变量,gas成本远低于状态写入。
- 参数
function _generateRandomDna(string memory _str) private view returns (uint) { ... }
私有视图函数,不修改链上状态,传入一个字符串返回无符号整数作为随机DNA:- 将传入的字符串通过
abi.encodePacked打包后,用keccak256算法计算得到256位哈希值,强转为无符号整数 - 将哈希值对
dnaModulus取模,得到16位长度的数值作为DNA返回
- 将传入的字符串通过
function createRandomZombie(string memory _name) public { ... }
公开函数,是外部用户调用创建僵尸的唯一入口:- 传入想要设置的僵尸名称,内部先调用
_generateRandomDna基于名称生成随机DNA - 再调用
_createZombie将新僵尸存入数组,同时触发NewZombie事件
- 传入想要设置的僵尸名称,内部先调用
示例中事件的完整执行链路
- 用户向合约地址发送交易,调用
createRandomZombie函数传入僵尸名称 - EVM执行合约逻辑,生成随机DNA、将新僵尸存入
zombies数组 - 执行到
emit语句时,将新僵尸的ID、名称、DNA写入交易日志 - 交易被打包确认上链后,所有订阅了该合约
NewZombie事件的链下服务会实时收到事件携带的参数,前端即可直接更新页面展示新生成的僵尸,不需要反复轮询链上查询状态变化
内容的提问来源于stack exchange,提问作者jerome richard A R
相关产品推荐
相关产品推荐

