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中的函数重载概念。示例// ...