Fallback
Fallback
이번글에서는 Ethernaut의 ‘Fallback’이란 문제를 풀어보겠습니다.
0x01 Fallback Function
Fallback 함수는
- 존재하지 않는 함수가 호출된 경우
- Ether가 컨트랙트로 직접 전송되었지만
receive()가 없는 경우 msg.data가 비어있지 않은 경우
호출됩니다.
아래는 Fallback 함수의 동작 조건(2번, 3번)을 시각화한 것입니다.
1
2
3
4
5
6
7
8
9
10
11
send Ether
|
msg.data is empty?
/ \
yes no
| |
receive() exists? fallback()
/ \
yes no
| |
receive() fallback()
Reference | solidity-by-example
0x02 Features
- Fallback은 익명함수이다.
- 파라미터와 리턴 값이 없으며, 특별한 경우 데이터(bytes calldata)를 인자로 받아 처리할 수 있다.
- Fallback은 무조건
external로 선언되어야 한다. - Fallback 함수가
payable로 선언되면, 이더를 받을 때 실행 가능하다. - Fallback은 전송이나 전송을 통해 호출될 때 2300 가스 제한을 갖는다.
- Fallback은 직접 호출되지 않는다.
0x03 Examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract Fallback {
// event: 함수 이름과 남은 가스량을 로그로 기록
event Log(string func, uint256 gas);
// fallback()
// - 컨트랙트에 정의되지 않은 함수가 호출되거나, 데이터가 담긴 트랜잭션이 전송될 때 실행됨
// - 반드시 external 및 payable로 선언 (이더 수신 가능)
// - send/transfer: gas 2300만 전달, call: 모든 gas 전달
fallback() external payable {
emit Log("fallback", gasleft());
}
// receive()
// - msg.data(입력 데이터)가 비어있는 단순 이더 전송 시 호출됨
// - 반드시 external 및 payable로 선언 (이더 수신 가능)
receive() external payable {
emit Log("receive", gasleft());
}
// getBalance(): 이 컨트랙트의 현재 이더 잔고를 반환
function getBalance() public view returns (uint256) {
return address(this).balance;
}
}
contract SendToFallback {
// transferToFallback()
// - _to 주소로 transfer를 사용하여 이더를 보냄
// - transfer는 2300 가스만 전달 → 대상 컨트랙트의 receive 또는 fallback 함수가 실행됨(단, receive 함수가 있으면 우선)
function transferToFallback(address payable _to) public payable {
_to.transfer(msg.value);
}
// callFallback()
// - _to 주소로 call을 사용하여 이더를 보냄
// - call은 모든 가스를 전달하며, 데이터도 함께 보낼 수 있음
// - 함수 시그니처 등 데이터가 없으면 receive, 데이터가 있으면 fallback이 실행됨
// - 이더 전송 성공 여부 확인(실패시 revert)
function callFallback(address payable _to) public payable {
(bool sent,) = _to.call{value: msg.value}("");
require(sent, "Failed to send Ether");
}
}
Reference | solidity-by-example
위의 코드를 실행 시 생기는 CALLDATA는 msg.data 역할을 합니다.
즉, CALLDATA에 빈 값을 넣으면 CALLDATA(msg.data)가 비어있으므로 receive가, 빈 값이 아니라면 fallback이 호출됩니다.
This post is licensed under CC BY 4.0 by the author.