새소식

인기 검색어

Write Up/Ethernaut

[Ethernaut] Fallback

  • -
반응형

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

contract Fallback {

  mapping(address => uint) 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 (uint) {
    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;
  }
}

 

Fallback문제는 코드가 주어져있고 mission이 나와있다.

  1. contract의 주인이 되어라
  2. balance를 0으로 만들어라

이렇게 2가지의 mission이 주어진다.

먼저 코드를 같이 보자. 흐름을 하나씩 분석해 보면

  function contribute() public payable {
    require(msg.value < 0.001 ether);
    contributions[msg.sender] += msg.value;
    if(contributions[msg.sender] > contributions[owner]) {
      owner = msg.sender;
    }
  }

contribute() 함수는 msg.value가 0.001 ether일 때 다음 흐름을 가진다.

contributions[msg.sender]에 msg.value를 더하고 만약 contributions[msg.sender]가 contriutions[owner]보다 크면 owner는 msg.sender이다.

 

여기서 msg.sender = contract를 호출한 사람의 주소, msg.value = 입력한 ether가 된다.

  function getContribution() public view returns (uint) {
    return contributions[msg.sender];
  }

  function withdraw() public onlyOwner {
    payable(owner).transfer(address(this).balance);
  }

getContribution() 함수는 contributions[msg.sender]의 값을 return 한다.

그리고 withdraw() 함수는 owner에게 balance를 지불하는데 msg.value가 owner일 때만 가능하다.

  receive() external payable {
    require(msg.value > 0 && contributions[msg.sender] > 0);
    owner = msg.sender;
  }
}

외부에서 송금한 경우 receive() 함수가 실행되는데 msg.value가 > 0이고 contributions[msg.sender]가  > 0이면 owner는 msg.sender로 바뀐다.

현재 owner는 level이다. 그리고 owner의 contribute는 1000 ether를 가지고 시작한다.

일단 owner를 내 주소로 만들기 위해서는 contribute() 함수의 if문과 receive() 함수를 봐야 한다.

contribute() 함수로 owner를 내 주소로 만들기에는 너무 노가다다....

왜냐하면 0.001 ether 미만인 ether를  계속 보내면서 1000 ether를 초과해야 한다.

그러면 receive() 함수를 봐야 하는데 receive()의 조건은 msg.value > 0 && contribute[msg.sender] > 0이다.

첫 번째 contribute[msg.sender] > 0의 조건은 쉽다. 그냥 단순히 0.001 ether 미만의 ether를 보내면 된다.

contribute[msg.sender]에 0.0009 ether를 보냈다. 그리고 getContribution()을 호출하니까 정상적으로 0.0009 ether가 들어 간 것을 확인할 수 있다. 그러면 contribute[msg.sender] > 0의 조건은 만족했다.

두 번째 msg.value > 0은 내가 직접 instance에 ether를 주면 된다. 아까 말했듯이 receive() 함수는 외부에서 ether가 송금되었을 때 실행되는 함수이다.

MetaMask에서 입력한 ether가 0 초과만 하면 되니까 적은 금액을 넣고 송금을 하면 owner가 내 주소로 바뀌는 것을 확인할 수 있다.

첫 번째 contract의 ownership을 내 것으로 만드는 것은 성공했다.

그럼 owner의 balance를 0으로 만들어야 문제가 풀린다.

owner()가 나의 주소로 되어 있으니까 withdraw() 함수를 호출하면 잔액이 0이 된다.

두 가지 mission을 모두 해결했기 때문에 Submit instance를 누르면 level을 클리어했다고 나온다.

반응형

'Write Up > Ethernaut' 카테고리의 다른 글

[Ethernaut] Preservation  (0) 2023.02.01
[Ethernaut] Telephone  (0) 2023.01.30
[Ethernaut] CoinFlip  (0) 2023.01.30
[Ethernaut] Fallout  (0) 2023.01.29
[Ethernaut] Hello Ethernaut  (0) 2023.01.28
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.