payable ба ETH хүлээж авах
Smart contract ETH хүлээн авахын тулд тусгай тэмдэглэгээ шаардагдана. Энэ хичээлд payable функц, msg.value, receive(), fallback() функцүүдийг судална.
payable — ETH хүлээн авах зөвшөөрөл
Функц ETH хүлээн авахын тулд payable тэмдэглэгээтэй байх ёстой. Тэмдэглэгээгүй функц руу ETH илгээхийг оролдвол гүйлгээ буцаагдана.
// 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-д нэгжийн тэмдэглэгээ:
// 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 байх ёстой.
// 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 илгээлтийг хүлээн авна (өгөгдөлгүй гүйлгээ).
// 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 илгээлт болон өгөгдөлтэй дуудлага хоёуланг нь барьж авч болно.
// 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
// 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 — тэдгээрийн ялгааг судална.