? fallback func ?
호출한 함수가 컨트렉트 내에서 조회되지 않을 경우
외부에서 특정 컨트렉트를 호출했을 때, 해당 호출 주소(function identifier)가 확인되지 않으면 디폴트로 fallback 함수가 실행된다.
이더(ETH, ether)를 보낼 때 자동으로 실행
fallback 함수 선언 방식
fallback () external [payable] {
///....
}
fallback (bytes calldata _input) external [payable] returns (bytes memory _output)
///...
fallback 특징
calldata가 없는 상황에서는 receive.
calldata가 있으면, fallback이 호출됨.
만약, receive만 존재하는데, calldata가 존재하면, ERROR가 발생.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Fallback {
mapping(address => uint256) public contributions;
address public owner;
constructor() {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}
modifier onlyOwner() {
require(msg.sender == owner, "caller is not the owner");
_;
}
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] += msg.value;
if (contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
function getContribution() public view returns (uint256) {
return contributions[msg.sender];
}
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
}
object 1) 주인이 되어라
object 2) 잔고를 0원으로 만들어라
[object 1] 주인이 되어라
코드를 살펴 보면,
constructor() {
owner = msg.sender;
contributions[msg.sender] = 1000 * (1 ether);
}
배포자가 owner로 지정되게 되며, contribution[msg.sender]는 1000 ether로 지정된다.
배포자가 owner이므로, 내가 owner가 되는 조건을 찾아야한다.
function contribute() public payable {
require(msg.value < 0.001 ether);
contributions[msg.sender] += msg.value;
if (contributions[msg.sender] > contributions[owner]) {
owner = msg.sender;
}
}
해당 함수를 실행하면, 내가 owner로 지정될 수 있다.
해당 조건을 만족하면, owner로 변할 수 있다.
1. require(msg.value < 0.001 ether)
msg의 값이 0.001 ether 보다 작아야한다.
2. contribution[msg.sender] > contributions[owner]
msg.sender의 contribution이 onwer의 contribution보다 커야한다.
(help)

1을 만족시키는 것은 다음과 같다.
await contract.contribution.sendTransaction({value:to Wei("0.0001")})
sendTransaction을 통해 전송하는데, 여기서 ether를 전송하는 것은 toWei를 사용하며
여기서 Wei란? ether를 전송하는데 사용되는 단위
이렇게하여, 전송하면, 0.0001보다 작으므로, 1의 조건에 만족하게 된다.
require을 만족하여, contributions[msg.sender] += msg.value가 실행된다.
처음 호출 하게 된 것이라면, msg.sender = msg.value(0.0001)가 contribution에 저장되게 된다.
그러나, 처음에 선언 할 때, owner의 contribution을 1000 ether라고 지정하였기 때문에
이를 만족하기 위해서는 해당 작업을 1,000,000번 수행해야한다... (진땀)
이 방법 외에 더 좋은 방법이 있는지 조회를 해보면
receive() external payable {
require(msg.value > 0 && contributions[msg.sender] > 0);
owner = msg.sender;
}
receive()를 통해, msg.value가 0보다 크고, contribution[msg.sender]가 0보다 크면, owner가 될 수 있음을 확인했다.
위에서 말했다시피, calldata가 없으면 receive가 호출되고, 이게 fallback 함수에 의해 작동되는 것이기 때문에 문제의 이름이 fallback임을 유추할 수 있다.
앞에서 했던 작업들에 의해 require이 만족되어, owner가 되었음을 console창을 통해 확인할 수 있었다.

[object 2]
잔액을 0원으로 만들어야한다고 한다.
그럼, 인출을 해야한다.
인출에 관련된 부분을 확인해보면,
function withdraw() public onlyOwner {
payable(owner).transfer(address(this).balance);
}
withdraw라는 함수를 통해, 작동되는 것을 확인할 수 있었다.
또한, 해당 함수 내에서, onlyOwner가 적혀있는데 이를 "함수제어자"라고 부르며
이에 대한 내용 또한 위의 코드에서 확인할 수 있었다.
modifier onlyOwner() {
require(msg.sender == owner, "caller is not the owner");
_;
}
msg.sender가 owner여야 하는데, 이미 우리는 owner의 권한을 획득하였으므로
withdraw를 수행할 수 있게 되었다.
await contract.withdraw()
위의 명령문을 통해, 인출을 진행하여 0원으로 만들 수 있었다.