Solidity Cheatsheet

Modifiers

Use this Solidity reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Modifier Syntax

modifier <name>(<params>) {
    // pre-condition code
    _;          // placeholder for the modified function body
    // post-condition code (optional)
}
modifier onlyOwner() {
    require(msg.sender == owner, "Not owner");
    _; // function body runs here
}

function withdraw() external onlyOwner {
    payable(owner).transfer(address(this).balance);
}

Modifier with Parameters

modifier costs(uint256 price) {
    require(msg.value >= price, "Insufficient payment");
    _;
    // refund excess
    if (msg.value > price) {
        payable(msg.sender).transfer(msg.value - price);
    }
}

function mint() external payable costs(0.05 ether) {
    _mintToken(msg.sender);
}

Chaining Modifiers

Modifiers execute left to right, each wrapping the next.

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

modifier whenNotPaused() {
    require(!paused, "Paused");
    _;
}

modifier nonReentrant() {
    require(!_entered, "Reentrant");
    _entered = true;
    _;
    _entered = false;
}

// Execution order: onlyOwner → whenNotPaused → nonReentrant → function body
function criticalAction() external onlyOwner whenNotPaused nonReentrant {
    // ...
}

_; Placement

// Pre-check only (most common)
modifier onlyAdmin() {
    require(isAdmin[msg.sender]);
    _;
}

// Post-check (rare — runs after function body)
modifier checkBalance() {
    _;
    require(address(this).balance >= reserve, "Below reserve");
}

// Wrapping — both pre and post
modifier timed() {
    uint start = block.timestamp;
    _;
    emit Elapsed(block.timestamp - start);
}

// Can place _; multiple times (function body runs each time)
modifier twice() {
    _;
    _;
}

Common Modifier Patterns

Access Control

address public owner;

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

function transferOwnership(address newOwner) external onlyOwner {
    require(newOwner != address(0), "Zero address");
    owner = newOwner;
}

Pausable

bool public paused;

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

modifier whenPaused() {
    require(paused, "Not paused");
    _;
}

function pause()   external onlyOwner whenNotPaused { paused = true; }
function unpause() external onlyOwner whenPaused    { paused = false; }

Reentrancy Guard

uint256 private _status = 1; // use 1/2 instead of false/true to avoid cold SSTORE

modifier nonReentrant() {
    require(_status != 2, "ReentrancyGuard: reentrant call");
    _status = 2;
    _;
    _status = 1;
}

Since Solidity 0.8.24, you can use transient storage for reentrancy locks — cheaper (EIP-1153):

uint256 transient private _entered;

modifier nonReentrant() {
    require(_entered == 0);
    _entered = 1;
    _;
    // auto-cleared at end of transaction
}

Rate Limiting / Cooldown

mapping(address => uint256) public lastCall;

modifier cooldown(uint256 period) {
    require(block.timestamp >= lastCall[msg.sender] + period, "Cooldown active");
    lastCall[msg.sender] = block.timestamp;
    _;
}

function claim() external cooldown(1 days) {
    // ...
}

Validation

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

modifier validAmount(uint256 amount) {
    require(amount > 0, "Amount must be > 0");
    require(amount <= maxAmount, "Amount too large");
    _;
}

function send(address to, uint256 amount)
    external
    validAddress(to)
    validAmount(amount)
{
    _transfer(to, amount);
}

Modifiers in Inheritance

contract Ownable {
    address public owner;

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

contract MultiSig is Ownable {
    // Override to require multisig instead
    modifier onlyOwner() override {
        require(isApproved(msg.sig), "Not approved");
        _;
    }
}

Custom Errors vs require in Modifiers

// Using custom errors (cheaper — no string storage)
error Unauthorized(address caller);
error ContractPaused();

modifier onlyOwner() {
    if (msg.sender != owner) revert Unauthorized(msg.sender);
    _;
}

modifier whenNotPaused() {
    if (paused) revert ContractPaused();
    _;
}

Gotchas

  • Modifiers cannot return values — they can only gate execution or emit events.
  • The function's return values pass through the modifier stack unchanged.
  • Avoid complex logic in modifiers — keep them readable; put logic in internal functions called from the modifier.
  • Modifiers are inlined by the compiler — each use adds bytecode.
  • A modifier with _; after a return in the pre-check: the function body still runs (the return in a modifier only exits the modifier's preamble block, not the function).
// WRONG mental model
modifier earlyReturn() {
    if (someFlag) return; // exits modifier but function body still runs!
    _;
}

// CORRECT pattern
modifier earlyReturn() {
    if (!someFlag) _; // only run function body when flag is false
}