Skip to content

Function Modifiers and Security Patterns

This example demonstrates common security patterns and function modifiers in Solidity.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract SecurityPatterns {
    // State variables
    address public owner;
    bool public paused;
    uint256 public lastActionTimestamp;
    mapping(address => uint256) public balances;

    // Constants
    uint256 public constant COOLDOWN_PERIOD = 1 days;
    uint256 public constant MAX_BALANCE = 1000 ether;

    // Events
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event Paused(address account);
    event Unpaused(address account);

    // Constructor
    constructor() {
        owner = msg.sender;
    }

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

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

    modifier withCooldown() {
        require(block.timestamp >= lastActionTimestamp + COOLDOWN_PERIOD, 
                "Action in cooldown");
        _;
        lastActionTimestamp = block.timestamp;
    }

    modifier validAmount(uint256 amount) {
        require(amount > 0, "Amount must be positive");
        require(amount <= MAX_BALANCE, "Amount exceeds maximum");
        _;
    }

    // Reentrancy Guard
    bool private locked;
    modifier noReentrant() {
        require(!locked, "No reentrancy");
        locked = true;
        _;
        locked = false;
    }

    // Functions
    function pause() public onlyOwner {
        paused = true;
        emit Paused(msg.sender);
    }

    function unpause() public onlyOwner {
        paused = false;
        emit Unpaused(msg.sender);
    }

    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "New owner is zero address");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }

    // Example of multiple modifiers
    function deposit() public payable 
        whenNotPaused 
        withCooldown 
        validAmount(msg.value) 
    {
        balances[msg.sender] += msg.value;
    }

    // Example of reentrancy protection
    function withdraw(uint256 amount) public 
        whenNotPaused 
        noReentrant 
        validAmount(amount) 
    {
        require(balances[msg.sender] >= amount, "Insufficient balance");

        balances[msg.sender] -= amount;

        // Potential reentrancy attack vector, but protected by noReentrant
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }

    // Emergency withdrawal
    function emergencyWithdraw() public onlyOwner {
        (bool success, ) = owner.call{value: address(this).balance}("");
        require(success, "Transfer failed");
    }
}

Key Security Patterns

  1. Access Control

    • Owner pattern
    • Role-based access
    • Function modifiers
  2. Circuit Breaker

    • Pause/Unpause functionality
    • Emergency stops
    • Controlled shutdown
  3. Rate Limiting

    • Cooldown periods
    • Maximum limits
    • Transaction throttling
  4. Reentrancy Protection

    • Checks-Effects-Interactions pattern
    • Reentrancy guard
    • State management
  5. Input Validation

    • Amount validation
    • Address validation
    • State checks

Best Practices

  1. Use multiple modifiers when needed
  2. Follow checks-effects-interactions pattern
  3. Implement emergency stops
  4. Validate all inputs
  5. Use events for important state changes