새소식

인기 검색어

Write Up/Ethernaut

[Ethernaut] Dex Two

  • -
반응형

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

import "openzeppelin-contracts-08/token/ERC20/IERC20.sol";
import "openzeppelin-contracts-08/token/ERC20/ERC20.sol";
import 'openzeppelin-contracts-08/access/Ownable.sol';

contract DexTwo is Ownable {
  address public token1;
  address public token2;
  constructor() {}

  function setTokens(address _token1, address _token2) public onlyOwner {
    token1 = _token1;
    token2 = _token2;
  }

  function add_liquidity(address token_address, uint amount) public onlyOwner {
    IERC20(token_address).transferFrom(msg.sender, address(this), amount);
  }
  
  function swap(address from, address to, uint amount) public {
    require(IERC20(from).balanceOf(msg.sender) >= amount, "Not enough to swap");
    uint swapAmount = getSwapAmount(from, to, amount);
    IERC20(from).transferFrom(msg.sender, address(this), amount);
    IERC20(to).approve(address(this), swapAmount);
    IERC20(to).transferFrom(address(this), msg.sender, swapAmount);
  } 

  function getSwapAmount(address from, address to, uint amount) public view returns(uint){
    return((amount * IERC20(to).balanceOf(address(this)))/IERC20(from).balanceOf(address(this)));
  }

  function approve(address spender, uint amount) public {
    SwappableTokenTwo(token1).approve(msg.sender, spender, amount);
    SwappableTokenTwo(token2).approve(msg.sender, spender, amount);
  }

  function balanceOf(address token, address account) public view returns (uint){
    return IERC20(token).balanceOf(account);
  }
}

contract SwappableTokenTwo is ERC20 {
  address private _dex;
  constructor(address dexInstance, string memory name, string memory symbol, uint initialSupply) ERC20(name, symbol) {
        _mint(msg.sender, initialSupply);
        _dex = dexInstance;
  }

  function approve(address owner, address spender, uint256 amount) public {
    require(owner != _dex, "InvalidApprover");
    super._approve(owner, spender, amount);
  }
}

우리의 mission은 token1, token2를 모두 추출해야 한다.

저번 dex랑 비슷하지만 swap부분에서 코드가 누락된 부분이 있다.

require((from == token1 && to == token2) || (from == token2 && to == token1), "Invalid tokens");

이 부분이 누락되었다. 즉 token을 우리가 새로 만들 수 있다는 말이다.

ERC20에서 우리가 토큰을 새로 추가해서 뺄 수도 있다.

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Attack is ERC20 {
    constructor(uint256 initialSupply) ERC20("AkToken", "ATK") {
        _mint(msg.sender, initialSupply);
        
    }
}

그래서 remix를 이용하여 개인 토큰을 하나 만든다. 그리고 DEX2 Contract에 토큰을 전송하고, approve를 하면 준비는 끝났다.

그럼 얼마나 보내고 얼마나 가지고 있어야 할까??

getSwapAmount() 함수의 공식을 보면 amount * to.balanceOf(this) / from.balanceOf(this)다. 여기서 DEX2에 있는 자원은 token1 100개, token2 100개이다.

DEX2 contract에 있는 ATK 코인이 n개, 내가 소유하고 있는 ATK코인이 k개라고 가정하면, 100 * 100/n = 100, (k-100) * 100/(n+100) = 100 2개의 연산이 나온다. 계산을 해보면 100/n = 1, n = 100 (k-100) * 0.5 = 100, k-100 = 200, k = 300

n=100, k=300이라는 결과가 나온다. 그래서 initialSupply를 400으로 하고 100을 DEX2 컨트랙트에 주고 300을 가지고 token을 훔치면 된다.

표로 나타내면 위 그림과 같이 진행된다. 실제로 저렇게 되는지 확인해 보자.

기본 세팅을 하고 시작하면 된다.

이렇게 token1한테 100을 주고 token2한테 200을 주면 DEX2의 잔고가 0이 되고 atk 토큰이 400이 된다.

반응형

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

[Ethernaut] Dex  (0) 2023.02.15
[Ethernaut] Shop  (0) 2023.02.13
[Ethernaut] Denial  (0) 2023.02.13
[Ethernaut] Alien Codex  (0) 2023.02.10
[Ethernaut] Recovery  (0) 2023.02.09
Contents

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

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