Solidity / payable ба ETH хүлээж авах

payable ба ETH хүлээж авах

Smart contract ETH хүлээн авахын тулд тусгай тэмдэглэгээ шаардагдана. Энэ хичээлд payable функц, msg.value, receive(), fallback() функцүүдийг судална.

payable — ETH хүлээн авах зөвшөөрөл

Функц ETH хүлээн авахын тулд payable тэмдэглэгээтэй байх ёстой. Тэмдэглэгээгүй функц руу ETH илгээхийг оролдвол гүйлгээ буцаагдана.

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

contract PiggyBank {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    // payable тэмдэглэгээтэй тул ETH хүлээн авна
    function deposit() public payable {
        require(msg.value > 0, "ETH илгээх шаардлагатай");
        // msg.value — илгээгдсэн ETH-ийн хэмжээ (wei-р)
    }

    // payable тэмдэглэгээгүй — ETH хүлээн авахгүй
    function getOwner() public view returns (address) {
        return owner;
    }
}

msg.value — илгээгдсэн ETH

msg.value нь тухайн гүйлгээнд илгээгдсэн ETH-ийн хэмжээг wei нэгжээр агуулна.

код
1 ETH = 1,000,000,000,000,000,000 wei (10^18)
1 ETH = 1000 finney
1 ETH = 1,000,000 szabo

Solidity-д нэгжийн тэмдэглэгээ:

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

contract UnitExample {
    function checkValue() public payable {
        // Нэгж тэмдэглэгээ ашиглаж болно
        require(msg.value >= 0.01 ether, "Хамгийн багадаа 0.01 ETH шаардлагатай");
        require(msg.value <= 10 ether, "Хамгийн ихдээ 10 ETH");
    }

    // Contract-ын ETH үлдэгдэл харах
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

payable address — ETH илгээх хаяг

Хаяг руу ETH илгээхийн тулд тухайн хаяг payable байх ёстой.

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

contract Wallet {
    address public owner;

    constructor() {
        owner = msg.sender;
    }

    function deposit() public payable {}

    function withdraw(uint256 amount) public {
        require(msg.sender == owner, "Зөвхөн эзэмшигч");
        require(address(this).balance >= amount, "Үлдэгдэл хүрэлцэхгүй");

        // msg.sender-ийг payable болгоно
        payable(msg.sender).transfer(amount);
    }

    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

receive() — ETH шууд хүлээн авах

receive() функц нь функцийн нэр заагаагүй ETH илгээлтийг хүлээн авна (өгөгдөлгүй гүйлгээ).

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

contract EtherReceiver {
    event Received(address sender, uint256 amount);

    // receive() нь external payable байх ёстой
    receive() external payable {
        emit Received(msg.sender, msg.value);
    }

    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

receive() функцгүй contract руу ETH шууд илгээхийг оролдвол гүйлгээ буцаагдана.

fallback() — бусад бүх дуудлага

fallback() функц нь тохирох функц олдоогүй үед ажиллана. ETH илгээлт болон өгөгдөлтэй дуудлага хоёуланг нь барьж авч болно.

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

contract FallbackExample {
    event FallbackCalled(address sender, uint256 value, bytes data);
    event Received(address sender, uint256 value);

    // Өгөгдөлгүй ETH илгээлт
    receive() external payable {
        emit Received(msg.sender, msg.value);
    }

    // Тохирох функц олдоогүй эсвэл өгөгдөлтэй ETH илгээлт
    fallback() external payable {
        emit FallbackCalled(msg.sender, msg.value, msg.data);
    }
}

receive() vs fallback() — дуудлагын дараалал

код
ETH илгээлт ирэв
        │
msg.data хоосон уу?
   ├── Тийм ──► receive() байна уу?
   │               ├── Тийм ──► receive() дуудна
   │               └── Үгүй ──► fallback() дуудна
   └── Үгүй ──► fallback() дуудна

Практик жишээ — Crowdfund

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

contract Crowdfund {
    address public creator;
    uint256 public goal;
    uint256 public deadline;
    mapping(address => uint256) public contributions;

    event Contributed(address contributor, uint256 amount);
    event GoalReached(uint256 totalAmount);

    constructor(uint256 _goalInEther, uint256 _durationDays) {
        creator = msg.sender;
        goal = _goalInEther * 1 ether;
        deadline = block.timestamp + (_durationDays * 1 days);
    }

    function contribute() public payable {
        require(block.timestamp < deadline, "Хугацаа дууссан");
        require(msg.value > 0, "ETH илгээх шаардлагатай");

        contributions[msg.sender] += msg.value;
        emit Contributed(msg.sender, msg.value);

        if (address(this).balance >= goal) {
            emit GoalReached(address(this).balance);
        }
    }

    function withdraw() public {
        require(msg.sender == creator, "Зөвхөн үүсгэгч");
        require(address(this).balance >= goal, "Зорилго хүрээгүй байна");
        require(block.timestamp >= deadline, "Хугацаа дуусаагүй байна");

        payable(creator).transfer(address(this).balance);
    }

    function refund() public {
        require(block.timestamp >= deadline, "Хугацаа дуусаагүй байна");
        require(address(this).balance < goal, "Зорилго хүрсэн — буцаалт боломжгүй");

        uint256 amount = contributions[msg.sender];
        require(amount > 0, "Оруулсан хөрөнгө байхгүй");

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

    receive() external payable {
        contribute();
    }
}

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

ETH илгээх гурван арга — send, transfer, call — тэдгээрийн ялгааг судална.