169 lines
5.9 KiB
Solidity
169 lines
5.9 KiB
Solidity
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
pragma solidity ^0.8.24;
|
|
|
|
import {IUniversalRouter} from "../interfaces/IUniversalRouter.sol";
|
|
import {IPermit2} from "../lib/permit2/src/interfaces/IPermit2.sol";
|
|
import {Constants} from "./Constants.sol";
|
|
import {Actions} from "../lib/v4-periphery/src/libraries/Actions.sol";
|
|
import {PoolKey} from "../lib/v4-core/src/types/PoolKey.sol";
|
|
import {IV4Router} from "../lib/v4-periphery/src/interfaces/IV4Router.sol";
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import {Currency} from "../lib/v4-core/src/types/Currency.sol";
|
|
import {IHooks} from "../lib/v4-core/src/interfaces/IHooks.sol";
|
|
import "forge-std/Test.sol";
|
|
|
|
contract Commands {
|
|
uint256 constant V2_SWAP_EXACT_IN = 0x08;
|
|
uint256 constant V3_SWAP_EXACT_IN = 0x00;
|
|
uint256 constant V4_SWAP = 0x10;
|
|
}
|
|
|
|
// A gas test to compare the gas usage of the UniversalRouter with the TychoRouter
|
|
// The gas usage quoted from the TychoRouter is without any asserts after the swap
|
|
// The path executed on TychoRouter is the same as the path executed on UniversalRouter
|
|
contract GasTest is Commands, Test, Constants {
|
|
IUniversalRouter universalRouter = IUniversalRouter(UNIVERSAL_ROUTER);
|
|
IPermit2 permit2 = IPermit2(PERMIT2_ADDRESS);
|
|
|
|
function setUp() public {
|
|
uint256 forkBlock = 21817316;
|
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
|
}
|
|
|
|
// Gas usage: 248511
|
|
// TychoRouter:testSwapSimple costs 113647
|
|
function testUniversalRouterUniswapV2() public {
|
|
uint256 amountIn = 10 ** 18;
|
|
|
|
bytes memory commands =
|
|
abi.encodePacked(uint8(Commands.V2_SWAP_EXACT_IN));
|
|
|
|
address[] memory path = new address[](2);
|
|
path[0] = WETH_ADDR;
|
|
path[1] = DAI_ADDR;
|
|
|
|
bytes[] memory inputs = new bytes[](1);
|
|
inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, false);
|
|
|
|
deal(WETH_ADDR, address(universalRouter), amountIn);
|
|
universalRouter.execute(commands, inputs, block.timestamp + 1000);
|
|
}
|
|
|
|
// Gas usage: 296248
|
|
// TychoRouter:testSwapSimplePermit2 costs 184993
|
|
function testUniversalRouterUniswapV2Permit2() public {
|
|
uint256 amountIn = 10 ** 18;
|
|
|
|
bytes memory commands =
|
|
abi.encodePacked(uint8(Commands.V2_SWAP_EXACT_IN));
|
|
|
|
address[] memory path = new address[](2);
|
|
path[0] = WETH_ADDR;
|
|
path[1] = DAI_ADDR;
|
|
|
|
bytes[] memory inputs = new bytes[](1);
|
|
inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, true);
|
|
|
|
deal(WETH_ADDR, address(this), amountIn);
|
|
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, amountIn);
|
|
permit2.approve(
|
|
WETH_ADDR,
|
|
address(universalRouter),
|
|
uint160(amountIn),
|
|
uint48(block.timestamp + 1000)
|
|
);
|
|
universalRouter.execute(commands, inputs, block.timestamp + 1000);
|
|
}
|
|
|
|
// Gas usage: 252003
|
|
// TychoRouter:testSwapSingleUSV3 costs 126181
|
|
function testUniversalRouterUniswapV3() public {
|
|
uint256 amountIn = 10 ** 18;
|
|
|
|
bytes memory commands =
|
|
abi.encodePacked(uint8(Commands.V3_SWAP_EXACT_IN));
|
|
|
|
uint24 poolFee = 3000;
|
|
bytes memory path = abi.encodePacked(WETH_ADDR, poolFee, DAI_ADDR);
|
|
|
|
bytes[] memory inputs = new bytes[](1);
|
|
inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, false);
|
|
|
|
deal(WETH_ADDR, address(universalRouter), amountIn);
|
|
universalRouter.execute(commands, inputs, block.timestamp + 1000);
|
|
}
|
|
|
|
// Gas usage: 299036
|
|
// TychoRouter:testSwapSingleUSV3Permit2 costs 192780
|
|
function testUniversalRouterUniswapV3Permit2() public {
|
|
uint256 amountIn = 10 ** 18;
|
|
|
|
bytes memory commands =
|
|
abi.encodePacked(uint8(Commands.V3_SWAP_EXACT_IN));
|
|
|
|
uint24 poolFee = 3000;
|
|
bytes memory path = abi.encodePacked(WETH_ADDR, poolFee, DAI_ADDR);
|
|
|
|
bytes[] memory inputs = new bytes[](1);
|
|
inputs[0] = abi.encode(BOB, amountIn, uint256(0), path, true);
|
|
|
|
deal(WETH_ADDR, address(this), amountIn);
|
|
IERC20(WETH_ADDR).approve(PERMIT2_ADDRESS, amountIn);
|
|
permit2.approve(
|
|
WETH_ADDR,
|
|
address(universalRouter),
|
|
uint160(amountIn),
|
|
uint48(block.timestamp + 1000)
|
|
);
|
|
universalRouter.execute(commands, inputs, block.timestamp + 1000);
|
|
}
|
|
|
|
// Gas usage: 299523
|
|
// TychoRouter:testSwapSingleUSV4CallbackPermit2 costs 217751
|
|
function testUniversalRouterUniswapV4Permit2() public {
|
|
uint128 amountIn = uint128(100 ether);
|
|
uint128 amountOutMinimum = uint128(0);
|
|
uint256 deadline = block.timestamp + 1000;
|
|
|
|
bytes memory commands = abi.encodePacked(uint8(Commands.V4_SWAP));
|
|
|
|
bytes memory actions = abi.encodePacked(
|
|
uint8(Actions.SWAP_EXACT_IN_SINGLE),
|
|
uint8(Actions.SETTLE_ALL),
|
|
uint8(Actions.TAKE_ALL)
|
|
);
|
|
|
|
PoolKey memory key = PoolKey({
|
|
currency0: Currency.wrap(USDE_ADDR),
|
|
currency1: Currency.wrap(USDT_ADDR),
|
|
fee: 100,
|
|
tickSpacing: int24(1),
|
|
hooks: IHooks(address(0))
|
|
});
|
|
|
|
bytes[] memory params = new bytes[](3);
|
|
params[0] = abi.encode(
|
|
IV4Router.ExactInputSingleParams({
|
|
poolKey: key,
|
|
zeroForOne: true,
|
|
amountIn: amountIn,
|
|
amountOutMinimum: amountOutMinimum,
|
|
hookData: bytes("")
|
|
})
|
|
);
|
|
|
|
params[1] = abi.encode(key.currency0, amountIn);
|
|
params[2] = abi.encode(key.currency1, amountOutMinimum);
|
|
|
|
bytes[] memory inputs = new bytes[](1);
|
|
inputs[0] = abi.encode(actions, params);
|
|
|
|
deal(USDE_ADDR, address(this), amountIn);
|
|
IERC20(USDE_ADDR).approve(PERMIT2_ADDRESS, amountIn);
|
|
permit2.approve(
|
|
USDE_ADDR, address(universalRouter), amountIn, uint48(deadline)
|
|
);
|
|
universalRouter.execute(commands, inputs, deadline);
|
|
}
|
|
}
|