Dex

Drain all funds from the Decentralized Exchange (Dex) contract by exploiting flawed price calculations.

Vulnerable Code
Analyze the Solidity code below to find the vulnerability.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; // Assume SafeMath is used contract Dex { using SafeMath for uint; address public token1; address public token2; address owner; // Implicit Ownable constructor(address _token1, address _token2) { token1 = _token1; token2 = _token2; owner = msg.sender; // Assuming Ownable sets owner } // Function to allow depositing initial liquidity (only owner) function setTokens(address _token1, address _token2) public { // Placeholder - assumes tokens are set, often in constructor or via owner } // Approve Dex to spend user's tokens before swapping function approve(address spender, uint amount) public { require(ERC20(token1).approve(spender, amount), "Approve T1 failed"); require(ERC20(token2).approve(spender, amount), "Approve T2 failed"); } // Add liquidity - often restricted or handled initially function addLiquidity(uint amount) public { // Placeholder - assumes liquidity exists // Typically transfers 'amount' of both tokens from msg.sender ERC20(token1).transferFrom(msg.sender, address(this), amount); ERC20(token2).transferFrom(msg.sender, address(this), amount); } // Calculates swap price based on current balances // Vulnerable: uses direct balance ratio function getSwapPrice(address from, address to, uint amount) public view returns(uint){ // Example: price = (amount * balance_to) / balance_from return((amount * ERC20(to).balanceOf(address(this))).div(ERC20(from).balanceOf(address(this)))); } // Swaps tokens // Vulnerable: price calculation can be manipulated function swap(address from, address to, uint amount) public { require(ERC20(from).transferFrom(msg.sender, address(this), amount), "TransferFrom failed"); uint swapAmount = getSwapPrice(from, to, amount); require(ERC20(to).balanceOf(address(this)) >= swapAmount, "Insufficient balance"); require(ERC20(to).transfer(msg.sender, swapAmount), "Transfer failed"); } } // Note: ERC20 token contracts (Token1, Token2) are assumed to exist and be standard. // Initial balances in Dex are typically 100 of each for the Ethernaut level. // Player starts with 10 of each token.
Submit Explanation
Explain the vulnerability and how to exploit it.
Hints (5)
Just a little peak
Hint 1
Hint 2
Hint 3
Hint 4
Hint 5
Explanation
Discomfort = Learning