跳到主要内容

函数修改器(modifier)

修改器作用

函数修改器可以用来改变一个函数的行为,比如用于在函数执行前检查某种前置条件。

函数修改器使用关键字 modifier , 以下代码定义了一个 onlyOwner 函数修改器, 然后使用修改器 onlyOwner 修饰 transferOwner() 函数:

pragma solidity >=0.8.0;


contract owned {
function owned() public { owner = msg.sender; }
address owner;

modifier onlyOwner {
require(msg.sender == owner, "Only owner can call this function.");
_;
}


function transferOwner(address _newO) public onlyOwner {
owner = _newO;
}
}

查看 onlyOwner 修改器的代码,很容易理解其作用是限定交易的发送者只能是owner

但我们用 onlyOwner 去修饰其他的函数时,后者也需要修改器的条件,因此对于 transferOwner() 函数来说,只有 owner 才能成功调用transferOwner()

函数修改器的工作原理是这样的:

函数修改器一般是带有一个特殊符号 _; ; 修改器所修饰的函数的函数体会被插入到_;的位置。

因此函数 transferOwner扩展开后,就是:

function transferOwner(address _newO) public {
require(
msg.sender == owner,
"Only owner can call this function."
);
owner = _newO;
}

因此,可以把修改器当做一个语法糖,用更简介的方式来组织限定条件。

修改器可带参数

修改器可以接收参数,例如:

contract testModifty {

modifier over22(uint age) {
require (age >= 22, "too small age");
_;
}


function marry(uint age) public over22(age) {
// do something
}
}

以上marry()函数只有满足age >= 22才可以成功调用。

多修改器一起使用

多个修改器可以一起修饰某个函数,此时会根据定义函数修改器的顺序嵌套执行。

猜测一下,下面的例子执行test1() 后,a 的结果是什么?

在运行之前,先自己思考一下,这里涉及两个知识点,第一,函数修改器可以按顺序嵌套,第二,修改器或函数体中显式的return语句仅仅跳出当前的修改器和函数体,整个执行逻辑会从前一个修改器中的定义的 “_” 之后继续执行。

因此上面的智能合约在运行test1()之后,状态变量a的值是多少?是1、11、12还是13呢?答案是11,大家可以运行get_a获取a的值。我们来分析一下test1函数,它扩展之后是这样的:

uint c = b;
uint c = a;
a = 12;
return ;
_;
a = 13;
c = a;
a = 11;

这个时候就一目了然了,最后a为11。

修改器可继承

修改器也是可被继承的,同时还可被继承合约重写(Override)。例如:

contract mortal is owned {
// 只有在合约里保存的owner调用close函数,才会生效
function close() public onlyOwner {
selfdestruct(owner);
}
}

mortal合约从上面的owned继承了onlyOwner修饰符,并将其应用于close函数。

小结

函数修改器是一个语法糖,同来给修饰的函数添加一些额外的功能或检查。

常用于如:检查输入条件、权限控制、重入控制、防止重复初始化等场景。


DeCert.me 码一个未来,DeCert 让每一位开发者轻松构建自己的可信履历。

DeCert.me 由登链社区 @UpchainDAO 孵化,欢迎 Discord 频道 一起交流。

本教程来自贡献者 @Tiny熊