βœ…MyMevBot

The setup is there are two uni v2 pairs:

  • weth/usdc pair, price is balanced

  • weth/usdt pair, price is unbalanced <- we arbitrage from this pool

The weth/usdt pair is unbalanced beacause in test file a user 0xBeeb provided liquidity (10 weth, 3000000 usdt). This position is under 1 weth = 300000 usdt, which makes the price of weth goes a lot higher than market price.

We are given a flashloan service for usdc, so the arbitrage path is:

  1. Flashloan usdc

  2. Go to weth/usdc pool to swap for weth

  3. Go to weth/usdt pool to swap for usdt <- in this step we get a lot more usdt than usual

  4. Let router find a pair to swap usdt for usdc, then we pay back the flashloan and keep the profit.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "./interfaces/IERC20.sol";

/**
 *
 *  ARBITRAGE A POOL
 *
 * Given two pools where the token pair represents the same underlying; WETH/USDC and WETH/USDT (the formal has the correct price, while the latter doesnt).
 * The challenge is to flash borrow some USDC (>1000) from `flashLenderPool` to arbitrage the pool(s), then make profit by ensuring MyMevBot contract's USDC balance
 * is more than 0.
 *
 */
contract MyMevBot {
    IUniswapV3Pool public immutable flashLenderPool;
    IERC20 public immutable weth;
    IERC20 public immutable usdc;
    IERC20 public immutable usdt;
    IUniswapV2Router public immutable router;
    bool public flashLoaned;

    constructor(address _flashLenderPool, address _weth, address _usdc, address _usdt, address _router) {
        flashLenderPool = IUniswapV3Pool(_flashLenderPool);
        weth = IERC20(_weth);
        usdc = IERC20(_usdc);
        usdt = IERC20(_usdt);
        router = IUniswapV2Router(_router);
    }

    function performArbitrage() public {
        // Start with the minimum required amount
        flashLenderPool.flash(address(this), 1000e6, 0, "");
    }

    function uniswapV3FlashCallback(uint256 _fee0, uint256, bytes calldata data) external {
        callMeCallMe();

        uint256 usdcAmount = usdc.balanceOf(address(this));
        
        // Approve router to spend tokens
        usdc.approve(address(router), usdcAmount);
        weth.approve(address(router), type(uint256).max);
        usdt.approve(address(router), type(uint256).max);
        
        // Simple arbitrage: USDC -> WETH -> USDT -> USDC
        // Use the entire flash loan amount for arbitrage
        address[] memory path = new address[](4);
        path[0] = address(usdc);
        path[1] = address(weth);  
        path[2] = address(usdt);
        path[3] = address(usdc);
        
        router.swapExactTokensForTokens(
            usdcAmount, // Use full amount for arbitrage
            0,
            path,
            address(this),
            block.timestamp + 300
        );
        
        // Pay back the flash loan (borrowed amount + fee)
        uint256 repayAmount = usdcAmount + _fee0;
        usdc.transfer(address(flashLenderPool), repayAmount);
    }

    function callMeCallMe() private {
        uint256 usdcBal = usdc.balanceOf(address(this));
        require(msg.sender == address(flashLenderPool), "not callback");
        require(flashLoaned = usdcBal >= 1000e6, "FlashLoan less than 1,000 USDC.");
    }
}

interface IUniswapV3Pool {
    /**
     * recipient: the address which will receive the token0 and/or token1 amounts.
     * amount0: the amount of USDC to borrow.
     * amount1: the amount of WETH to borrow.
     * data: any data to be passed through to the callback.
     */
    function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external;
}

interface IUniswapV2Router {
    /**
     *     amountIn: the amount to use for swap.
     *     amountOutMin: the minimum amount of output tokens that must be received for the transaction not to revert.
     *     path: an array of token addresses. In our case, WETH and USDC.
     *     to: recipient address to receive the liquidity tokens.
     *     deadline: timestamp after which the transaction will revert.
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);
}

Last updated