🏠 Home
Beginner
01 — Introduction to Solidity 02 — Setting Up Your Environment 03 — Your First Smart Contract 04 — Data Types & Variables 05 — Functions & Visibility 06 — Control Flow 07 — Arrays & Mappings 08 — Structs & Enums
Intermediate
09 — Events & Logging 10 — Modifiers 11 — Inheritance 12 — Interfaces & Abstract Contracts 13 — Error Handling 14 — Ether & Wei 15 — Payable Functions 16 — msg.sender & msg.value 17 — Storage vs Memory vs Stack
Advanced
18 — Gas Optimization 19 — ERC-20 Tokens 20 — ERC-721 NFT Standard 21 — Contract Security 22 — Reentrancy Attacks 23 — Oracles & Chainlink 24 — Upgradeable Contracts 25 — Deploying to Mainnet
SolidityMaster / Lesson 10
Lesson 10 of 25

Modifiers

Create reusable access control and validation logic with function modifiers.

Intermediate

What Are Modifiers?

Modifiers wrap function calls with pre-conditions (and post-conditions). They are the idiomatic Solidity way to implement access control, reentrancy guards, and input validation without repeating code. The _; placeholder marks where the function body executes.
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ModifierDemo {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    // Modifier definition
    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _; // function body executes here
    }

    // Apply modifier to a function
    function sensitiveAction() public onlyOwner {
        // Only the owner can reach this code
    }
}

Modifier with Parameters

Modifiers can accept parameters, making them more flexible.
Solidity
contract Parametric {
    mapping(address => uint256) public balances;

    modifier hasBalance(uint256 amount) {
        require(balances[msg.sender] >= amount, "Insufficient funds");
        _;
    }

    modifier validAddress(address addr) {
        require(addr != address(0), "Zero address");
        _;
    }

    function withdraw(uint256 amount) public hasBalance(amount) {
        balances[msg.sender] -= amount;
        payable(msg.sender).transfer(amount);
    }

    function send(address to, uint256 amount)
        public
        hasBalance(amount)
        validAddress(to)
    {
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}

Chaining Modifiers

Multiple modifiers can be applied to one function. They execute left-to-right.
Solidity
contract Chained {
    address public owner;
    bool    public paused;

    constructor() { owner = msg.sender; }

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    modifier notPaused() {
        require(!paused, "Contract is paused");
        _;
    }

    function setPaused(bool _p) public onlyOwner {
        paused = _p;
    }

    // Both modifiers run before the function body
    function criticalAction() public onlyOwner notPaused {
        // safe to execute
    }
}

Post-Condition Modifiers

Code after _; runs AFTER the function body — useful for post-condition checks.
Solidity
contract PostCondition {
    uint256 public balance = 1000;

    // Ensures balance never goes negative
    modifier balanceCheck() {
        _;                                // function runs first
        require(balance <= 10000, "Balance cap exceeded");
    }

    function deposit(uint256 amount) public balanceCheck {
        balance += amount;
        // modifier check runs after this line
    }
}