修饰器 #

一、修饰器概述 #

1.1 什么是修饰器 #

修饰器(modifier)是Solidity中用于修改函数行为的特殊声明,可以在函数执行前后添加检查逻辑。

solidity
contract ModifierDemo {
    address public owner;
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    function sensitiveOperation() public onlyOwner {
        // 只有owner可以执行
    }
}

1.2 修饰器的作用 #

作用 说明
访问控制 限制函数调用权限
条件检查 验证前置条件
代码复用 避免重复代码
逻辑分离 分离验证逻辑和业务逻辑

二、修饰器定义 #

2.1 基本语法 #

solidity
modifier modifierName() {
    // 前置检查
    _;  // 继续执行函数体
    // 后置检查
}

2.2 定义示例 #

solidity
contract ModifierDefinition {
    address public owner;
    bool public paused;
    
    // 基本修饰器
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    // 条件修饰器
    modifier whenNotPaused() {
        require(!paused, "Contract paused");
        _;
    }
    
    modifier whenPaused() {
        require(paused, "Contract not paused");
        _;
    }
}

2.3 占位符 _ #

solidity
contract PlaceholderDemo {
    uint256 public value;
    
    // _ 表示函数体的位置
    modifier logBeforeAfter() {
        // 前置逻辑
        emit Log("Before");
        
        _;  // 执行函数体
        
        // 后置逻辑
        emit Log("After");
    }
    
    event Log(string message);
    
    function setValue(uint256 _value) public logBeforeAfter {
        value = _value;
    }
}

三、修饰器使用 #

3.1 单个修饰器 #

solidity
contract SingleModifier {
    address public owner;
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    constructor() {
        owner = msg.sender;
    }
    
    // 使用单个修饰器
    function changeOwner(address newOwner) public onlyOwner {
        require(newOwner != address(0), "Invalid address");
        owner = newOwner;
    }
}

3.2 多个修饰器 #

solidity
contract MultipleModifiers {
    address public owner;
    bool public paused;
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    modifier whenNotPaused() {
        require(!paused, "Contract paused");
        _;
    }
    
    // 使用多个修饰器
    function sensitiveOperation() public onlyOwner whenNotPaused {
        // 只有owner且合约未暂停时才能执行
    }
    
    function setPaused(bool _paused) public onlyOwner {
        paused = _paused;
    }
}

3.3 修饰器执行顺序 #

solidity
contract ModifierOrder {
    modifier mod1() {
        emit Log("mod1 start");
        _;
        emit Log("mod1 end");
    }
    
    modifier mod2() {
        emit Log("mod2 start");
        _;
        emit Log("mod2 end");
    }
    
    event Log(string message);
    
    // 执行顺序:mod1 start -> mod2 start -> 函数体 -> mod2 end -> mod1 end
    function test() public mod1 mod2 {
        emit Log("function body");
    }
}

四、参数化修饰器 #

4.1 基本参数 #

solidity
contract ParameterizedModifier {
    modifier minAmount(uint256 amount) {
        require(msg.value >= amount, "Insufficient ETH");
        _;
    }
    
    modifier validAddress(address addr) {
        require(addr != address(0), "Invalid address");
        _;
    }
    
    // 使用参数化修饰器
    function deposit() public payable minAmount(1 ether) {
        // 至少存入1 ETH
    }
    
    function transfer(address to) public validAddress(to) {
        // 转账逻辑
    }
}

4.2 多个参数 #

solidity
contract MultipleParameters {
    modifier inRange(uint256 value, uint256 min, uint256 max) {
        require(value >= min && value <= max, "Out of range");
        _;
    }
    
    modifier validAddresses(address a, address b) {
        require(a != address(0) && b != address(0), "Invalid address");
        require(a != b, "Same address");
        _;
    }
    
    function setValue(uint256 value) public inRange(value, 1, 100) {
        // value必须在1-100之间
    }
    
    function transfer(address from, address to) public validAddresses(from, to) {
        // 转账逻辑
    }
}

五、常见修饰器模式 #

5.1 访问控制 #

solidity
contract AccessControl {
    address public owner;
    mapping(address => bool) public admins;
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    modifier onlyAdmin() {
        require(admins[msg.sender] || msg.sender == owner, "Not admin");
        _;
    }
    
    modifier onlyAuthorized() {
        require(admins[msg.sender] || msg.sender == owner, "Not authorized");
        _;
    }
    
    constructor() {
        owner = msg.sender;
    }
    
    function setAdmin(address user, bool status) public onlyOwner {
        admins[user] = status;
    }
    
    function adminOperation() public onlyAdmin {
        // 管理员操作
    }
}

5.2 状态检查 #

solidity
contract StateCheck {
    bool public paused;
    bool public initialized;
    
    modifier whenNotPaused() {
        require(!paused, "Contract paused");
        _;
    }
    
    modifier whenPaused() {
        require(paused, "Contract not paused");
        _;
    }
    
    modifier whenInitialized() {
        require(initialized, "Not initialized");
        _;
    }
    
    modifier whenNotInitialized() {
        require(!initialized, "Already initialized");
        _;
    }
    
    function initialize() public whenNotInitialized {
        initialized = true;
    }
    
    function pause() public whenInitialized whenNotPaused {
        paused = true;
    }
    
    function unpause() public whenInitialized whenPaused {
        paused = false;
    }
    
    function normalOperation() public whenNotPaused {
        // 正常操作
    }
}

5.3 输入验证 #

solidity
contract InputValidation {
    modifier nonZeroAddress(address addr) {
        require(addr != address(0), "Zero address");
        _;
    }
    
    modifier nonZeroAmount(uint256 amount) {
        require(amount > 0, "Zero amount");
        _;
    }
    
    modifier validRange(uint256 value, uint256 min, uint256 max) {
        require(value >= min && value <= max, "Out of range");
        _;
    }
    
    function transfer(
        address to,
        uint256 amount
    ) public nonZeroAddress(to) nonZeroAmount(amount) {
        // 转账逻辑
    }
    
    function setPercentage(uint256 percentage) public validRange(percentage, 0, 100) {
        // 设置百分比
    }
}

5.4 时间限制 #

solidity
contract TimeRestriction {
    uint256 public startTime;
    uint256 public endTime;
    
    modifier onlyDuringSale() {
        require(block.timestamp >= startTime, "Sale not started");
        require(block.timestamp <= endTime, "Sale ended");
        _;
    }
    
    modifier onlyBeforeSale() {
        require(block.timestamp < startTime, "Sale already started");
        _;
    }
    
    modifier onlyAfterSale() {
        require(block.timestamp > endTime, "Sale not ended");
        _;
    }
    
    function buy() public onlyDuringSale {
        // 购买逻辑
    }
    
    function setSalePeriod(uint256 _start, uint256 _end) public onlyBeforeSale {
        startTime = _start;
        endTime = _end;
    }
}

5.5 重入保护 #

solidity
contract ReentrancyGuard {
    bool private _locked;
    
    modifier nonReentrant() {
        require(!_locked, "Reentrant call");
        _locked = true;
        _;
        _locked = false;
    }
    
    mapping(address => uint256) public balances;
    
    function withdraw() public nonReentrant {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "No balance");
        
        balances[msg.sender] = 0;
        payable(msg.sender).transfer(amount);
    }
}

六、综合示例 #

6.1 完整的访问控制合约 #

solidity
contract FullAccessControl {
    address public owner;
    mapping(address => bool) public admins;
    bool public paused;
    
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event AdminSet(address indexed user, bool status);
    event Paused(address account);
    event Unpaused(address account);
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    modifier onlyAdmin() {
        require(admins[msg.sender] || msg.sender == owner, "Not admin");
        _;
    }
    
    modifier whenNotPaused() {
        require(!paused, "Contract paused");
        _;
    }
    
    modifier whenPaused() {
        require(paused, "Contract not paused");
        _;
    }
    
    constructor() {
        owner = msg.sender;
    }
    
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "Invalid new owner");
        
        address previousOwner = owner;
        owner = newOwner;
        
        emit OwnershipTransferred(previousOwner, newOwner);
    }
    
    function setAdmin(address user, bool status) public onlyOwner {
        admins[user] = status;
        emit AdminSet(user, status);
    }
    
    function pause() public onlyAdmin whenNotPaused {
        paused = true;
        emit Paused(msg.sender);
    }
    
    function unpause() public onlyAdmin whenPaused {
        paused = false;
        emit Unpaused(msg.sender);
    }
    
    function sensitiveOperation() public onlyAdmin whenNotPaused {
        // 敏感操作
    }
}

6.2 代币合约修饰器 #

solidity
contract TokenWithModifiers {
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;
    address public owner;
    bool public mintingFinished;
    
    mapping(address => uint256) public balanceOf;
    mapping(address => mapping(address => uint256)) public allowance;
    
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event Mint(address indexed to, uint256 amount);
    event MintFinished();
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    modifier canMint() {
        require(!mintingFinished, "Minting finished");
        _;
    }
    
    modifier validAddress(address addr) {
        require(addr != address(0), "Invalid address");
        _;
    }
    
    modifier validAmount(uint256 amount) {
        require(amount > 0, "Invalid amount");
        _;
    }
    
    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        uint256 _totalSupply
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        totalSupply = _totalSupply;
        owner = msg.sender;
        
        balanceOf[msg.sender] = _totalSupply;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }
    
    function mint(
        address to,
        uint256 amount
    ) public onlyOwner canMint validAddress(to) validAmount(amount) {
        totalSupply += amount;
        balanceOf[to] += amount;
        
        emit Mint(to, amount);
        emit Transfer(address(0), to, amount);
    }
    
    function finishMinting() public onlyOwner canMint {
        mintingFinished = true;
        emit MintFinished();
    }
    
    function transfer(
        address to,
        uint256 amount
    ) public validAddress(to) validAmount(amount) returns (bool) {
        require(balanceOf[msg.sender] >= amount, "Insufficient balance");
        
        balanceOf[msg.sender] -= amount;
        balanceOf[to] += amount;
        
        emit Transfer(msg.sender, to, amount);
        return true;
    }
}

七、最佳实践 #

7.1 命名约定 #

solidity
contract ModifierNaming {
    // 推荐:使用小驼峰命名
    modifier onlyOwner() { _; }
    modifier whenNotPaused() { _; }
    modifier nonReentrant() { _; }
    
    // 推荐:表达条件或限制
    modifier isValid(address addr) { _; }
    modifier hasBalance(uint256 amount) { _; }
    modifier canAccess() { _; }
}

7.2 保持简洁 #

solidity
contract KeepSimple {
    // 推荐:修饰器只做检查
    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }
    
    // 不推荐:修饰器包含复杂逻辑
    // modifier badModifier() {
    //     // 复杂的业务逻辑
    //     // ...
    //     _;
    // }
}

7.3 避免副作用 #

solidity
contract AvoidSideEffects {
    // 不推荐:修饰器修改状态
    // modifier badModifier() {
    //     counter++;
    //     _;
    // }
    
    // 推荐:修饰器只做检查
    modifier goodModifier() {
        require(counter > 0, "Counter is zero");
        _;
    }
    
    uint256 public counter;
}

八、总结 #

修饰器要点:

特性 说明
定义 modifier name() { ... _; ... }
使用 function foo() public modifierName {}
参数 支持参数化修饰器
组合 支持多个修饰器
执行 按声明顺序执行

使用建议:

  • 使用有意义的命名
  • 保持修饰器简洁
  • 避免副作用
  • 合理组合多个修饰器

接下来,让我们学习面向对象特性!

最后更新:2026-03-27