Solidity / Modifier

Modifier

modifier бол функц ажиллахаас өмнө эсвэл дараа автоматаар ажилладаг кодын хэсэг. require(msg.sender == owner) шалгалтыг функц бүрд давтаж бичихийн оронд нэг modifier-д нэгтгэж, олон функцэд хавсаргана.

Modifier тодорхойлох

solidity
modifier modifierName() {
    // өмнөх шалгалт
    require(..., "алдааны мессеж");
    _;   // ← функцийн бие энд ажиллана
    // дараах код (хэрэв байвал)
}

_; нь функцийн бие орох байрыг заана. Энэ тэмдэггүй бол modifier compile хийгдэхгүй.

onlyOwner загвар

Хамгийн түгээмэл modifier — зөвхөн owner үйлдэл хийж болно:

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

contract Ownable {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Зөвхөн эзэмшигч");
        _;
    }

    // modifier хавсаргасан функцүүд
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "Хүчингүй хаяг");
        owner = newOwner;
    }

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

    function pause() public onlyOwner {
        // зогсоох логик
    }
}

onlyOwner modifier байхгүй бол:

solidity
// ❌ давтагдсан код — засах хэцүү
function transferOwnership(address newOwner) public {
    require(msg.sender == owner, "Зөвхөн эзэмшигч");  // ← давтагдана
    owner = newOwner;
}

function withdraw() public {
    require(msg.sender == owner, "Зөвхөн эзэмшигч");  // ← давтагдана
    payable(owner).transfer(address(this).balance);
}

Параметртэй modifier

Modifier параметр авч болно — илүү уян хатан шалгалт хийх боломж олгодог:

solidity
contract TokenSale {
    mapping(address => uint256) public balances;
    uint256 public constant MAX_BUY = 1000;

    modifier minAmount(uint256 amount, uint256 minimum) {
        require(amount >= minimum, "Дүн хэт бага байна");
        _;
    }

    modifier maxAmount(uint256 amount, uint256 maximum) {
        require(amount <= maximum, "Дүн хэт их байна");
        _;
    }

    modifier validAddress(address addr) {
        require(addr != address(0), "Хүчингүй хаяг");
        require(addr != msg.sender, "Өөртөө илгээх боломжгүй");
        _;
    }

    function buy(uint256 amount)
        public
        payable
        minAmount(amount, 10)
        maxAmount(amount, MAX_BUY)
    {
        balances[msg.sender] += amount;
    }

    function transfer(address to, uint256 amount)
        public
        validAddress(to)
        minAmount(amount, 1)
    {
        require(balances[msg.sender] >= amount, "Үлдэгдэл хүрэлцэхгүй");
        balances[msg.sender] -= amount;
        balances[to]         += amount;
    }
}

Олон modifier хослуулах

Функцэд хэд хэдэн modifier хавсаргаж болно — зүүнээс баруун тийш дарааллаар ажиллана:

solidity
contract Auction {
    address public owner;
    bool    public isActive;
    uint256 public endTime;

    constructor() {
        owner    = msg.sender;
        isActive = false;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Зөвхөн эзэмшигч");
        _;
    }

    modifier whenActive() {
        require(isActive, "Дуусгавар биш байна");
        _;
    }

    modifier notEnded() {
        require(block.timestamp < endTime, "Хугацаа дууссан");
        _;
    }

    modifier whenNotActive() {
        require(!isActive, "Аль хэдийн идэвхтэй байна");
        _;
    }

    // modifier дараалал: onlyOwner → whenNotActive
    function start(uint256 duration) public onlyOwner whenNotActive {
        isActive = true;
        endTime  = block.timestamp + duration;
    }

    // modifier дараалал: whenActive → notEnded
    function bid() public payable whenActive notEnded {
        require(msg.value > 0, "ETH илгээх ёстой");
        // дуусгавар биш логик
    }

    // modifier дараалал: onlyOwner → whenActive
    function cancel() public onlyOwner whenActive {
        isActive = false;
    }
}

Modifier-ийн ажиллах дараалал:

код
bid() дуудагдахад:

1. whenActive шалгана  → isActive == true?
       ↓ тийм
2. notEnded шалгана   → timestamp < endTime?
       ↓ тийм
3. bid()-ийн бие ажиллана

Modifier-ийн _; байрлал

_; нь функцийн бие хаана ажиллах-г тодорхойлно. Өмнө эсвэл дараа тавьж болно:

solidity
// Өмнө шалгаад функц ажиллуулах (хамгийн түгээмэл)
modifier checkBefore() {
    require(condition, "Шалгалт амжилтгүй");
    _;   // функцийн бие энд
}

// Функц ажиллуулаад дараа нь шалгах / цэвэрлэх
modifier checkAfter() {
    _;   // функцийн бие энд
    require(result > 0, "Үр дүн хүчингүй");
}

// Өмнө тохируулж, функц ажиллуулж, дараа нь цэвэрлэх (reentrancy guard)
modifier nonReentrant() {
    require(!locked, "Reentrancy хориглогдсон");
    locked = true;
    _;           // функцийн бие энд
    locked = false;
}

Бодит жишээ: Crowdfund contract

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

contract Crowdfund {
    address public owner;
    uint256 public goal;
    uint256 public deadline;
    uint256 public raised;
    bool    public finalized;

    mapping(address => uint256) public contributions;

    event Contributed(address indexed contributor, uint256 amount);
    event Finalized(bool goalReached, uint256 totalRaised);

    constructor(uint256 _goalWei, uint256 _durationSeconds) {
        owner    = msg.sender;
        goal     = _goalWei;
        deadline = block.timestamp + _durationSeconds;
    }

    modifier onlyOwner() {
        require(msg.sender == owner, "Зөвхөн эзэмшигч");
        _;
    }

    modifier beforeDeadline() {
        require(block.timestamp < deadline, "Хугацаа дууссан");
        _;
    }

    modifier afterDeadline() {
        require(block.timestamp >= deadline, "Хугацаа дуусаагүй");
        _;
    }

    modifier notFinalized() {
        require(!finalized, "Аль хэдийн дуусгавар болсон");
        _;
    }

    function contribute() public payable beforeDeadline notFinalized {
        require(msg.value > 0, "ETH илгээх ёстой");
        contributions[msg.sender] += msg.value;
        raised += msg.value;
        emit Contributed(msg.sender, msg.value);
    }

    function finalize() public onlyOwner afterDeadline notFinalized {
        finalized = true;

        if (raised >= goal) {
            payable(owner).transfer(raised);
        }

        emit Finalized(raised >= goal, raised);
    }

    function refund() public afterDeadline {
        require(raised < goal,                   "Зорилго биелсэн — буцаалт байхгүй");
        require(contributions[msg.sender] > 0,   "Оруулсан хувь байхгүй");

        uint256 amount = contributions[msg.sender];
        contributions[msg.sender] = 0;
        payable(msg.sender).transfer(amount);
    }
}

Дараагийн хичээлд:

require, revert, assert — алдааг зохицуулах гурван механизм, тэдгээрийн ялгаа, хэзээ аль нэгийг ашиглахыг судална.