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