// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.15; import 'forge-std/Test.sol'; import {IPermit2} from 'permit2/src/interfaces/IPermit2.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; import {IUniswapV2Factory} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol'; import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; import {UniversalRouter} from '../../contracts/UniversalRouter.sol'; import {Payments} from '../../contracts/modules/Payments.sol'; import {ActionConstants} from '@uniswap/v4-periphery/src/libraries/ActionConstants.sol'; import {Commands} from '../../contracts/libraries/Commands.sol'; import {RouterParameters} from '../../contracts/types/RouterParameters.sol'; abstract contract UniswapV2Test is Test { address constant RECIPIENT = address(10); uint256 constant AMOUNT = 1 ether; uint256 constant BALANCE = 100000 ether; IUniswapV2Factory constant FACTORY = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); ERC20 constant WETH9 = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); IPermit2 constant PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3); address constant FROM = address(1234); UniversalRouter router; function setUp() public virtual { vm.createSelectFork(vm.envString('FORK_URL'), 20010000); setUpTokens(); RouterParameters memory params = RouterParameters({ permit2: address(PERMIT2), weth9: address(WETH9), v2Factory: address(FACTORY), v3Factory: address(0), pairInitCodeHash: bytes32(0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f), poolInitCodeHash: bytes32(0), v4PoolManager: address(0), v3NFTPositionManager: address(0), v4PositionManager: address(0) }); router = new UniversalRouter(params); // pair doesn't exist, make a mock one if (FACTORY.getPair(token0(), token1()) == address(0)) { address pair = FACTORY.createPair(token0(), token1()); deal(token0(), pair, 100 ether); deal(token1(), pair, 100 ether); IUniswapV2Pair(pair).sync(); } vm.startPrank(FROM); deal(FROM, BALANCE); deal(token0(), FROM, BALANCE); deal(token1(), FROM, BALANCE); ERC20(token0()).approve(address(PERMIT2), type(uint256).max); ERC20(token1()).approve(address(PERMIT2), type(uint256).max); PERMIT2.approve(token0(), address(router), type(uint160).max, type(uint48).max); PERMIT2.approve(token1(), address(router), type(uint160).max, type(uint48).max); } function testExactInput0For1() public { bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V2_SWAP_EXACT_IN))); address[] memory path = new address[](2); path[0] = token0(); path[1] = token1(); bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, 0, path, true); router.execute(commands, inputs); assertEq(ERC20(token0()).balanceOf(FROM), BALANCE - AMOUNT); assertGt(ERC20(token1()).balanceOf(FROM), BALANCE); } function testExactInput1For0() public { bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V2_SWAP_EXACT_IN))); address[] memory path = new address[](2); path[0] = token1(); path[1] = token0(); bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, 0, path, true); router.execute(commands, inputs); assertEq(ERC20(token1()).balanceOf(FROM), BALANCE - AMOUNT); assertGt(ERC20(token0()).balanceOf(FROM), BALANCE); } function testExactInput0For1FromRouter() public { bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V2_SWAP_EXACT_IN))); deal(token0(), address(router), AMOUNT); address[] memory path = new address[](2); path[0] = token0(); path[1] = token1(); bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, 0, path, false); router.execute(commands, inputs); assertGt(ERC20(token1()).balanceOf(FROM), BALANCE); } function testExactInput1For0FromRouter() public { bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V2_SWAP_EXACT_IN))); deal(token1(), address(router), AMOUNT); address[] memory path = new address[](2); path[0] = token1(); path[1] = token0(); bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, 0, path, false); router.execute(commands, inputs); assertGt(ERC20(token0()).balanceOf(FROM), BALANCE); } function testExactOutput0For1() public { bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V2_SWAP_EXACT_OUT))); address[] memory path = new address[](2); path[0] = token0(); path[1] = token1(); bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, type(uint256).max, path, true); router.execute(commands, inputs); assertLt(ERC20(token0()).balanceOf(FROM), BALANCE); assertGe(ERC20(token1()).balanceOf(FROM), BALANCE + AMOUNT); } function testExactOutput1For0() public { bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V2_SWAP_EXACT_OUT))); address[] memory path = new address[](2); path[0] = token1(); path[1] = token0(); bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, type(uint256).max, path, true); router.execute(commands, inputs); assertLt(ERC20(token1()).balanceOf(FROM), BALANCE); assertGe(ERC20(token0()).balanceOf(FROM), BALANCE + AMOUNT); } function testExactOutput0For1FromRouter() public { bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V2_SWAP_EXACT_OUT))); deal(token0(), address(router), BALANCE); address[] memory path = new address[](2); path[0] = token0(); path[1] = token1(); bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, type(uint256).max, path, false); router.execute(commands, inputs); assertGe(ERC20(token1()).balanceOf(FROM), BALANCE + AMOUNT); } function testExactOutput1For0FromRouter() public { bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V2_SWAP_EXACT_OUT))); deal(token1(), address(router), BALANCE); address[] memory path = new address[](2); path[0] = token1(); path[1] = token0(); bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encode(ActionConstants.MSG_SENDER, AMOUNT, type(uint256).max, path, false); router.execute(commands, inputs); assertGe(ERC20(token0()).balanceOf(FROM), BALANCE + AMOUNT); } function token0() internal virtual returns (address); function token1() internal virtual returns (address); function setUpTokens() internal virtual {} }