修饰器 #
一、修饰器概述 #
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