// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; import {Test, console} from "forge-std/Test.sol"; import {ArbitrageManager} from "../src/ArbitrageManager.sol"; import {IERC20} from "../src/IERC20.sol"; import {IUniswapV2Pair} from "../src/IUniswapV2Pair.sol"; contract ArbitrageTest is Test { ArbitrageManager public arbitrageManager; uint256 mainnetFork; address constant vitalik = 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045; IERC20 weth = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IUniswapV2Pair uniswapPair = IUniswapV2Pair(0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc); IUniswapV2Pair sushiswapPair = IUniswapV2Pair(0x397FF1542f962076d0BFE58eA045FfA2d347ACa0); function setUp() public { mainnetFork = vm.createFork("https://eth-mainnet.g.alchemy.com/v2/kkDMaLVYpWQA0GsCYNFvAODnAxCCiamv"); // TODO use an env variable vm.selectFork(mainnetFork); vm.rollFork(22_147_269); arbitrageManager = new ArbitrageManager(); } function test_canSelectFork() public view { assertEq(vm.activeFork(), mainnetFork); assertEq(block.number, 22_147_269); } function test_sqrt() public view { uint256 n = 344353442342354234324324324323; assertEq(arbitrageManager.sqrt(n**2), n); n = 115792089237316195423570985008687907853269984665640564039457584007913129639935; assertEq(340282366920938463463374607431768211456 - 1, arbitrageManager.sqrt(n)); // +-1 is an acceptable rounding error } function test_computeAmountIn() public { (uint256 X_A, uint256 Y_A, ) = uniswapPair.getReserves(); // (USDT, WETH) (uint256 X_B, uint256 Y_B, ) = sushiswapPair.getReserves(); // (USDT, WETH) console.log("Uniswap pair reserves", X_A, Y_A); console.log("Uniswap pair ratio", Y_A/X_A); console.log("Sushiswap pair ratio", Y_B/X_B); uint256 unbalance = Y_A / 5; console.log("Unbalance", unbalance); vm.prank(address(uniswapPair)); // it works only for the next call weth.transfer(address(0), unbalance); uniswapPair.sync(); console.log("Arbitrage opportunity created"); (X_A, Y_A, ) = uniswapPair.getReserves(); (X_B, Y_B, ) = sushiswapPair.getReserves(); console.log("Uniswap pair reserves", X_A, Y_A); console.log("Uniswap pair ratio", Y_A/X_A); console.log("Sushiswap pair ratio", Y_B/X_B); uint256 optimum = arbitrageManager.optimalIn(X_A, Y_A, X_B, Y_B); console.log("The optimum is", optimum); vm.prank(address(0)); // it works only for the next call weth.transfer(address(uniswapPair), optimum); uint256 amountOut = arbitrageManager.getAmountOut(optimum, Y_A, X_A); console.log("First swap's amountOut", amountOut); uniswapPair.swap(amountOut, 0, address(sushiswapPair), new bytes(0)); amountOut = arbitrageManager.getAmountOut(amountOut, X_B, Y_B); console.log("Second swap's amountOut", amountOut); sushiswapPair.swap(0, amountOut, address(this), new bytes(0)); (X_A, Y_A, ) = uniswapPair.getReserves(); (X_B, Y_B, ) = sushiswapPair.getReserves(); console.log("Uniswap pair reserves", X_A, Y_A); console.log("Uniswap pair ratio", Y_A/X_A); console.log("Sushiswap pair ratio", Y_B/X_B); } }