Modifier
Reusable code blocks in Solidity that modify function behavior, commonly used for access control and input validation.
Modifiers in Solidity are reusable code blocks that can change or extend function behavior without duplicating code. They're most commonly used for access control, input validation, and reentrancy protection. The modifier code executes before and/or after the function body, with the special _; placeholder indicating where the modified function's code runs.
Basic Modifier Syntax
1contract Example {2 address public owner;34 modifier onlyOwner() {5 require(msg.sender == owner, "Not owner");6 _; // Function body executes here7 }89 function sensitiveAction() external onlyOwner {10 // Only owner can call this11 }12}
How Modifiers Execute
The _; placeholder determines when the function body runs:
1modifier beforeAndAfter() {2 // Code here runs BEFORE the function3 doSomethingFirst();45 _; // Function body executes67 // Code here runs AFTER the function8 doSomethingAfter();9}
Common Modifier Patterns
Access Control
1modifier onlyOwner() {2 require(msg.sender == owner, "Not owner");3 _;4}56modifier onlyRole(bytes32 role) {7 require(hasRole(role, msg.sender), "Missing role");8 _;9}1011modifier onlyWhitelisted() {12 require(whitelist[msg.sender], "Not whitelisted");13 _;14}
Input Validation
1modifier validAddress(address _addr) {2 require(_addr != address(0), "Zero address");3 _;4}56modifier nonZeroAmount(uint256 _amount) {7 require(_amount > 0, "Zero amount");8 _;9}1011modifier withinBounds(uint256 _value, uint256 _min, uint256 _max) {12 require(_value >= _min && _value <= _max, "Out of bounds");13 _;14}
State Checks
1modifier whenNotPaused() {2 require(!paused, "Contract paused");3 _;4}56modifier whenPaused() {7 require(paused, "Contract not paused");8 _;9}1011modifier inState(State _state) {12 require(state == _state, "Invalid state");13 _;14}
Reentrancy Protection
1modifier nonReentrant() {2 require(!locked, "Reentrant call");3 locked = true;4 _;5 locked = false;6}
OpenZeppelin's ReentrancyGuard is the standard implementation:
1import "@openzeppelin/contracts/security/ReentrancyGuard.sol";23contract Vault is ReentrancyGuard {4 function withdraw() external nonReentrant {5 // Protected from reentrancy6 }7}
Modifier with Parameters
Modifiers can accept arguments:
1modifier costs(uint256 _amount) {2 require(msg.value >= _amount, "Insufficient payment");3 _;4 // Refund excess5 if (msg.value > _amount) {6 payable(msg.sender).transfer(msg.value - _amount);7 }8}910function buyItem() external payable costs(1 ether) {11 // User must send at least 1 ETH12}
Multiple Modifiers
Functions can have multiple modifiers, executed left to right:
1function criticalAction()2 external3 onlyOwner4 whenNotPaused5 nonReentrant6{7 // All three conditions must pass8}910// Execution order:11// 1. onlyOwner check12// 2. whenNotPaused check13// 3. nonReentrant lock14// 4. Function body15// 5. nonReentrant unlock
Security Considerations
Modifier Execution Order
Order matters for security:
1// WRONG ORDER - reentrancy check after access control2function withdraw() external onlyOwner nonReentrant { }34// CORRECT - reentrancy protection should be first5function withdraw() external nonReentrant onlyOwner { }
State Changes in Modifiers
Be careful with state changes after _;:
1modifier countCalls() {2 _;3 callCount++; // Runs AFTER function - could be skipped if function reverts4}
Gas Considerations
Modifiers add gas overhead. Consider inlining for frequently-called functions:
1// Modifier version (more readable)2function transfer() external nonReentrant { }34// Inlined version (saves gas)5function transfer() external {6 require(!locked, "Reentrant");7 locked = true;8 // ... function logic ...9 locked = false;10}
Modifier vs Internal Function
| Aspect | Modifier | Internal Function |
|---|---|---|
| Syntax | Declarative | Imperative |
| Readability | Clear at function signature | Logic hidden in body |
| Reusability | High | High |
| Flexibility | Limited (before/after pattern) | Full control |
| Gas | Slightly higher | Slightly lower |
Audit Checklist
When auditing modifiers:
- Access control modifiers on all sensitive functions
- Correct modifier order on functions
- No state changes that could be bypassed
- Reentrancy protection where needed
- Proper validation of modifier parameters
- No circular dependencies between modifiers
Modifiers are a fundamental Solidity pattern for writing clean, secure, and maintainable code. Proper use of modifiers significantly reduces the risk of access control vulnerabilities and code duplication.
Related Terms
Access Control
Security mechanisms that restrict which addresses can call specific functions in a smart contract, preventing unauthorized actions.
Solidity
The primary programming language for writing smart contracts on Ethereum and EVM-compatible blockchains.
Ownable
A common smart contract pattern providing single-address ownership with transfer capabilities, typically used for administrative access control.
Reentrancy Attack
A vulnerability where external calls allow malicious contracts to recursively call back before state updates complete.
Need expert guidance on Modifier?
Our team at Zealynx has deep expertise in blockchain security and DeFi protocols. Whether you need an audit or consultation, we're here to help.
Get a Quote
