Solidity
中的事件(event
)是EVM
上日志的抽象,它具有两个特点:
ether.js
)可以通过RPC
接口订阅和监听这些事件,并在前端做响应。EVM
上比较经济的存储数据的方式,每个大概消耗2,000-5,000 gas
不等。相比之下,存储一个新的变量至少需要20,000 gas
。事件的声明由event
关键字开头,然后跟事件名称,括号里面写好事件需要记录的变量类型和变量名。以ERC20
代币合约的Transfer
事件为例:
event Transfer(address indexed from, address indexed to, uint256 value);
每个事件最多有3个带indexed
的变量,Transfer
事件共记录了3个变量from
,to
和value
,分别对应代币的转账地址,接收地址和转账数量。同时from
和to
前面带着indexed
关键字,表示很重要,程序可以轻松的筛选出特定转账地址和接收地址的转账事件
- // 定义_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);
- }
继承(inheritance
),包括简单继承,多重继承,以及修饰器(modifier
)和构造函数(constructor
)的继承。
virtual
: 父合约中的函数,如果希望子合约重写,需要加上virtual
关键字
override
:子合约重写了父合约中的函数,需要加上override
关键。
- contract Yeye {
- event Log(string msg);
-
- // 定义3个function: hip(), pop(), man(),Log值为Yeye。
- function hip() public virtual{
- emit Log("Yeye");
- }
-
- function pop() public virtual{
- emit Log("Yeye");
- }
-
- function yeye() public virtual {
- emit Log("Yeye");
- }
- }
我们再定义一个合约Baba
- contract Baba is Yeye{
- // 继承两个function: hip()和pop(),输出改为Baba。
- function hip() public virtual override{
- emit Log("Baba");
- }
-
- function pop() public virtual override{
- emit Log("Baba");
- }
-
- function baba() public virtual{
- emit Log("Baba");
- }
- }
Erzi
合约,继承Yeye合约和Baba合约,那么就要写成contract Erzi is Yeye, Baba
,而不能写成contract Erzi is Baba, Yeye
,不然就会报错。hip()
和pop()
,在子合约里必须重写,不然会报错。override(Yeye, Baba)
。- contract Erzi is Yeye, Baba{
- // 继承两个function: hip()和pop(),输出值为Erzi。
- function hip() public virtual override(Yeye, Baba){
- emit Log("Erzi");
- }
-
- function pop() public virtual override(Yeye, Baba) {
- emit Log("Erzi");
- }
Solidity中的修饰器(Modifier)同样可以继承,用法与函数继承类似,在相应的地方加virtual
和override
关键字即可。
子合约有两种方法继承父合约的构造函数。举个简单的例子,父合约A
里面有一个状态变量a,并由构造函数的参数来确定:
- // 构造函数的继承
- abstract contract A {
- uint public a;
-
- constructor(uint _a) {
- a = _a;
- }
- }
contract B is A(1)
- contract C is A {
- constructor(uint _c) A(_c * _c) {}
- }
子合约有两种方式调用父合约的函数,直接调用和利用super关键字。
父合约名.函数名()
的方式来调用父合约函数,例如Yeye.pop()
。- function callParent() public{
- Yeye.pop();
- }
super
关键字:子合约可以利用super.函数名()
来调用最近的父合约函数。solidity
继承关系按声明时从右到左的顺序是:contract Erzi is Yeye, Baba
,那么Baba
是最近的父合约,super.pop()
将调用Baba.pop()
而不是Yeye.pop()
:- function callParentSuper() public{
- // 将调用最近的父合约函数,Baba.pop()
- super.pop();
- }