안녕하세요. 이번 포스팅은 스마트 컨트랙트의 fallback, receivce 함수를 알아보겠습니다.
fallback 함수
fallback은 '대비책'이라는 사전적 의미를 가집니다.
솔리디티에서도 fallback 함수는 '대비책' 함수라고 보시면 생각하시면 되는데요.
어떤 행동을 취하게 하는 함수라고 보시면 될 것 같습니다.
fallback 함수를 사용하는 이유는 아래와 같습니다.
1. 스마트 컨트랙트가 이더를 받을 수 있다.
2. 이더를 받고 난 이후 어떠한 행동을 취할 수 있다.
3. call 함수로 없는 함수가 불려질 때, 어떠한 행동을 취하게 할 수 있다.
fallback 함수는 솔리디티 0.6 이전과 이후로 의미가 다릅니다. 자세한 건 아래서 살펴보겠습니다.
우선 버전에 상관없이 공통적인 fallback 함수의 특징은 아래와 같습니다.
1. 무기명 함수입니다. 즉, 이름이 없는 함수입니다.
2. external 키워드가 필수입니다.
3. payable 키워드가 필수입니다.
3번에 대해 부연설명을 하자면 call 함수는 이더를 보낼 수도 있지만 외부 스마트 컨트랙트의 함수를 호출할 수 있습니다.
만약 A라는 컨트랙트에서 B라는 컨트랙트의 b_method라는 함수를 호출했고, B라는 컨트랙트에는 b_method 함수가 없다고 가정해보겠습니다.
A 컨트랙트는 B 컨트랙트 내 존재하지 않는 함수를 호출했기 때문에 이러한 경우에 B 컨트랙트의 fallback 함수가 호출됩니다.
솔리디티 0.6 이전의 fallback 함수
fallback 함수는 아래의 형태로 작성하시면 됩니다.
function() external payable {
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.7.0 < 0.9.0;
contract Bank {
event JustFallbackWithFunds(address _from, uint256 _value, string message);
// solidity ~ 0.6
function() external payable {
emit JustFallbackWithFunds(msg.sender, msg.value, "JustFallbackWithFunds is called");
}
}
contract You {
// receive()
function DepositWithSend(address payable _to) public payable {
bool success = _to.send(msg.value);
require(success, "Failed");
}
function DepositWithTranser(address payable _to) public payable {
_to.transfer(msg.value);
}
function DepositWithCall(address payable _to) public payable {
// ~ 0.7
(bool sent, ) = _to.call.value(msg.value)(""); // 이더 전송 call
require(sent, "Failed to send ether");
// 0.7 ~
(bool sent, ) = _to.call{value: msg.value}(""); // 이더 전송만 함(함수호출은 안함)
require(sent, "Failed");
}
// fallback()
function JustGiveMessage(address _to) public {
// ~ 0.7
(bool success, ) = _to.call("HI"); // Bank 컨트랙의 HI 함수 호출 -> Bank 컨트랙은 HI 함수가 없으므로 Bank 컨트랙의 fallback 함수가 호출된다.
require(success, "Failedto send ether");
// 0.7 ~
(bool success, ) = _to.call{value: msg.value}("HI"); // 함수 호출 및 이더 전송까지 함
require(success, "Failed");
}
}
솔리디티 0.6 이후의 fallback 함수
fallback 함수가 receivce, fallback으로 나뉩니다.
receivce는 순수하게 이더만 받을 때 작동합니다.
fallback은 함수를 실행하면서 이더를 보낼 때, 불려진 함수가 없는 경우에 작동합니다.
fallback 함수는 아래의 형태로 작성하시면 됩니다.
이더를 받는 receivce 함수가 따로 있으므로 payable 키워드를 사용하지 않아도 됩니다.
fallback() external {
}
receive() external payable {
}
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.7.0 < 0.9.0;
contract Bank {
event JustFallback(address _from, string message);
event ReceiveFallback(address _from, uint256 _value, string message);
event JustFallbackWithFunds(address _from, uint256 _value, string message);
// solidity ~ 0.6
//function() external payable {
// emit JustFallbackWithFunds(msg.sender, msg.value, "JustFallbackWithFunds is called");
//}
// solidity 0.6 ~
fallback() external {
emit JustFallback(msg.sender, "JustFallback is called");
}
receive() external payable {
emit ReceivceFallback(msg.sender, msg.value, "ReceivceFallback is called");
}
}
contract You {
// receive()
function DepositWithSend(address payable _to) public payable {
bool success = _to.send(msg.value);
require(success, "Failed");
}
function DepositWithTranser(address payable _to) public payable {
_to.transfer(msg.value);
}
function DepositWithCall(address payable _to) public payable {
// ~ 0.7
(bool sent, ) = _to.call.value(msg.value)(""); // 이더 전송 call
require(sent, "Failed to send ether");
// 0.7 ~
(bool sent, ) = _to.call{value: msg.value}("");
require(sent, "Failed");
}
// fallback()
function JustGiveMessage(address _to) public {
// ~ 0.7
(bool success, ) = _to.call("HI"); // Bank 컨트랙의 HI 함수 호출 -> Bank 컨트랙은 HI 함수가 없으므로 Bank 컨트랙의 fallback 함수가 호출된다.
require(success, "Failedto send ether");
// 0.7 ~
(bool success, ) = _to.call{value: msg.value}("HI"); // 이더 전송, 함수 호출 두 가지를 함
require(success, "Failed");
}
}
참고
https://www.youtube.com/watch?v=bS4yIW4CHo8&list=PLJQKWHLhBrxI43w0DU4uQrhWv4Pm1OFlx&index=35