Solidity modifier 函数修改器
Solidity 中关键字 modifier 用于声明一个函数修改器。
我们可以将一些通用的操作提取出来,包装为函数修改器,来提高代码的复用性,改善编码效率。
函数修改器 modifier 的作用与 Java Spring 中的切面功能很相似,当它作用于一个函数上,可以在函数执行前或后预先执行 modifier 中的逻辑,以增强其功能。
函数修改器 modifier 常用于在函数执行前检查某种前置条件。
函数修改器 modifier 是一种合约属性,可被继承,同时还可被派生的合约重写(override)。
1. 基本函数修改器
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract ModifierTest { bool public paused; uint public counter; function setPaused(bool _paused) external { paused = _paused; } modifier isNotPaused() { require(!paused); // 检查前置条件:判断paused是否被设置,如果paused为true,那么终止执行。 _; // 执行被isNotPaused修饰的函数 } function add() external isNotPaused{ counter++; } }
add函数被修改器isNotPaused修饰,所以先执行 require(!paused),检查前置条件,然后再执行add函数的代码。
2. _ 的作用
函数修改器中有一行代码只有下划线 _ ,我们认为下划线 _ 代表了被修饰函数的代码。
也就是说,下划线实际上帮我们标记了被 modifier 修饰函数的执行位置。如上例中:
modifier isNotPaused() { require(!paused); // 检查前置条件:判断paused是否被设置,如果paused为true,那么终止执行。 _; // 执行被isNotPaused修饰的函数 }
下划线 _在 require(!paused) 后面,则被修饰函数 add 在此判断条件之后执行。
3. 带参数的函数修改器
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract ModifierTest { bool public paused; uint public counter; function setPaused(bool _paused) external { paused = _paused; } modifier isNotPaused(uint x) { require(x > 10 ); // 检查前置条件:判断x是否大于10,如果x大于或者等于10,那么终止执行。 _; // 执行被isNotPaused修饰的函数 } function add(uint x) external isNotPaused(x){ counter++; } }
4. 复杂示例
我们来看一个函数修改器经典的应用 OpenZeppelin 库中的 Ownable 合约,下面是其中关键的代码:
/// Ownable 可以判断合约的调用者是否为当前合约的owner, /// 从而避免其他人随意的调用一些合约的关键操作。 /// 同时,owner 可以指定任何其他人为此合约新的 owner, /// 显然,只有当前owner才能指定其他人为新的owner。 contract Ownable { // 变量 owner 指定此合约的owner address public owner; // 发布事件 - 此合约owner已经换人(此逻辑与modifier无关,可以忽略) event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // 构造函数 - 创建合约自动执行,初始化合约所有人为合约创建者 function Ownable() public { owner = msg.sender; } // 定义一个函数修改器 modifier onlyOwner() { // 判断此函数调用者是否为owner require(msg.sender == owner); _; } // owner可以用此函数将owner所有权转换给其他人,显然次函数只有owner才能调用 // 函数末尾加上onlyOwner声明,onlyOwner正是上面定义的modifier function transferOwnership(address newOwner) public onlyOwner { require(newOwner != address(0)); OwnershipTransferred(owner, newOwner); owner = newOwner; } }
上述合约的 transferOwnership 函数用于 owner 将所有权转让给其他人,于是在末尾声明 onlyOwner 修改器,onlyOwner 将在 transferOwnership 执行前,先执行
require(msg.sender == owner);
以保证此函数的调用者为 owner ,如果不是 owner 则抛出异常。
下一章:Solidity 函数重载
Solidity的函数重载,是指同一个作用域内,相同函数名可以定义多个函数。这些函数的参数(参数类型或参数数量)必须不一样。仅仅是返回值不一样是不被允许。下面的例子展示了Solidity中的函数重载概念。示例// ...