From e3bf4ea1beb141917b7dac9dfed0e99282da7698 Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Sat, 6 May 2023 00:12:07 -0700 Subject: [PATCH 01/10] Refactor `_v2Swap` to resolve stack too deep error saving 100~200 gas --- .gas-snapshot | 41 ++++++++++ contracts/modules/uniswap/v2/V2SwapRouter.sol | 23 +++--- .../__snapshots__/NFTX.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 12 +-- .../SeaportV1_4.gas.test.ts.snap | 2 +- .../SeaportV1_5.gas.test.ts.snap | 2 +- .../__snapshots__/Sudoswap.gas.test.ts.snap | 4 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 78 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 8 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++--- .../__snapshots__/X2Y2.gas.test.ts.snap | 4 +- 11 files changed, 120 insertions(+), 76 deletions(-) create mode 100644 .gas-snapshot diff --git a/.gas-snapshot b/.gas-snapshot new file mode 100644 index 00000000..585ff775 --- /dev/null +++ b/.gas-snapshot @@ -0,0 +1,41 @@ +UniversalRouterTest:testCallModule() (gas: 6639) +UniversalRouterTest:testSupportsInterface() (gas: 9045) +UniversalRouterTest:testSweepERC1155() (gas: 70732) +UniversalRouterTest:testSweepERC1155InsufficientOutput() (gas: 60170) +UniversalRouterTest:testSweepERC1155NotFullAmount() (gas: 70713) +UniversalRouterTest:testSweepETH() (gas: 58089) +UniversalRouterTest:testSweepETHInsufficientOutput() (gas: 76030) +UniversalRouterTest:testSweepToken() (gas: 82345) +UniversalRouterTest:testSweepTokenInsufficientOutput() (gas: 82056) +V2DaiWeth:testExactInput0For1() (gas: 108328) +V2DaiWeth:testExactInput0For1FromRouter() (gas: 250570) +V2DaiWeth:testExactInput1For0() (gas: 108400) +V2DaiWeth:testExactInput1For0FromRouter() (gas: 250732) +V2DaiWeth:testExactOutput0For1() (gas: 109216) +V2DaiWeth:testExactOutput0For1FromRouter() (gas: 251648) +V2DaiWeth:testExactOutput1For0() (gas: 109640) +V2DaiWeth:testExactOutput1For0FromRouter() (gas: 251890) +V2MockMock:testExactInput0For1() (gas: 107669) +V2MockMock:testExactInput0For1FromRouter() (gas: 250324) +V2MockMock:testExactInput1For0() (gas: 107628) +V2MockMock:testExactInput1For0FromRouter() (gas: 250432) +V2MockMock:testExactOutput0For1() (gas: 107833) +V2MockMock:testExactOutput0For1FromRouter() (gas: 250835) +V2MockMock:testExactOutput1For0() (gas: 108008) +V2MockMock:testExactOutput1For0FromRouter() (gas: 250922) +V2MockWeth:testExactInput0For1() (gas: 102499) +V2MockWeth:testExactInput0For1FromRouter() (gas: 245915) +V2MockWeth:testExactInput1For0() (gas: 102545) +V2MockWeth:testExactInput1For0FromRouter() (gas: 246368) +V2MockWeth:testExactOutput0For1() (gas: 102666) +V2MockWeth:testExactOutput0For1FromRouter() (gas: 246425) +V2MockWeth:testExactOutput1For0() (gas: 103798) +V2MockWeth:testExactOutput1For0FromRouter() (gas: 247535) +V2WethApe:testExactInput0For1() (gas: 113107) +V2WethApe:testExactInput0For1FromRouter() (gas: 250272) +V2WethApe:testExactInput1For0() (gas: 107925) +V2WethApe:testExactInput1For0FromRouter() (gas: 250388) +V2WethApe:testExactOutput0For1() (gas: 114127) +V2WethApe:testExactOutput0For1FromRouter() (gas: 251456) +V2WethApe:testExactOutput1For0() (gas: 109113) +V2WethApe:testExactOutput1For0FromRouter() (gas: 251513) \ No newline at end of file diff --git a/contracts/modules/uniswap/v2/V2SwapRouter.sol b/contracts/modules/uniswap/v2/V2SwapRouter.sol index 1ac55c7e..2d843e8a 100644 --- a/contracts/modules/uniswap/v2/V2SwapRouter.sol +++ b/contracts/modules/uniswap/v2/V2SwapRouter.sol @@ -23,19 +23,22 @@ abstract contract V2SwapRouter is RouterImmutables, Permit2Payments { (address token0,) = UniswapV2Library.sortTokens(path[0], path[1]); uint256 finalPairIndex = path.length - 1; uint256 penultimatePairIndex = finalPairIndex - 1; - for (uint256 i; i < finalPairIndex; i++) { - (address input, address output) = (path[i], path[i + 1]); - (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves(); - (uint256 reserveInput, uint256 reserveOutput) = - input == token0 ? (reserve0, reserve1) : (reserve1, reserve0); - uint256 amountInput = ERC20(input).balanceOf(pair) - reserveInput; - uint256 amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput); - (uint256 amount0Out, uint256 amount1Out) = - input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0)); + for (uint256 i; i < finalPairIndex; ++i) { + address input = path[i]; + uint256 amount0Out; + uint256 amount1Out; + { + (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves(); + (uint256 reserveInput, uint256 reserveOutput) = + input == token0 ? (reserve0, reserve1) : (reserve1, reserve0); + uint256 amountInput = ERC20(input).balanceOf(pair) - reserveInput; + uint256 amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput); + (amount0Out, amount1Out) = input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0)); + } address nextPair; (nextPair, token0) = i < penultimatePairIndex ? UniswapV2Library.pairAndToken0For( - UNISWAP_V2_FACTORY, UNISWAP_V2_PAIR_INIT_CODE_HASH, output, path[i + 2] + UNISWAP_V2_FACTORY, UNISWAP_V2_PAIR_INIT_CODE_HASH, path[i + 1], path[i + 2] ) : (recipient, address(0)); IUniswapV2Pair(pair).swap(amount0Out, amount1Out, nextPair, new bytes(0)); diff --git a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap index 61efee2a..7202d1a2 100644 --- a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`NFTX Gas Tests gas: buyAndRedeem w/ specific selection 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 574870, + "gasUsed": 574873, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 611d0a42..8835d99b 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -10,42 +10,42 @@ Object { exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39235, + "gasUsed": 39247, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67925, + "gasUsed": 67949, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38248, + "gasUsed": 38260, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33821, + "gasUsed": 33833, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46765, + "gasUsed": 46768, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 53127, + "gasUsed": 53154, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap index ab59cc35..992ad7dc 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.4 Gas Tests ERC20 -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2532, - "gasUsed": 237135, + "gasUsed": 237147, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap index 0cb15f1c..6df4f12c 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152097, + "gasUsed": 152100, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap index a244721d..5e57a015 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap @@ -3,14 +3,14 @@ exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases NFTs with FRAX ERC20 token already approved 1`] = ` Object { "calldataByteLength": 1380, - "gasUsed": 289920, + "gasUsed": 289932, } `; exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases tokens 2402, 2509 of Based Ghoul with FRAX ERC20 token 1`] = ` Object { "calldataByteLength": 1508, - "gasUsed": 311549, + "gasUsed": 311561, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index ea83a9a4..314e3e80 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,105 +3,105 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 273152, + "gasUsed": 272999, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 248874, + "gasUsed": 248721, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 248874, + "gasUsed": 248721, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 273152, + "gasUsed": 272999, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 196828, + "gasUsed": 196686, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 189624, + "gasUsed": 189482, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit 1`] = ` Object { "calldataByteLength": 1540, - "gasUsed": 305306, + "gasUsed": 305098, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = ` Object { "calldataByteLength": 1220, - "gasUsed": 313366, + "gasUsed": 313170, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from batch 1`] = ` Object { "calldataByteLength": 1284, - "gasUsed": 315444, + "gasUsed": 315224, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, without explicit permit 1`] = ` Object { "calldataByteLength": 900, - "gasUsed": 309818, + "gasUsed": 309598, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 179794, + "gasUsed": 179664, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 179569, + "gasUsed": 179439, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 198235, + "gasUsed": 198074, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 187631, + "gasUsed": 187492, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 1124, - "gasUsed": 200312, + "gasUsed": 200182, } `; @@ -143,98 +143,98 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn trade, where an output fee is taken 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 128824, + "gasUsed": 128729, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 109042, + "gasUsed": 108923, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 246952, + "gasUsed": 246851, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops, no deadline 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 246694, + "gasUsed": 246593, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179686, + "gasUsed": 179576, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops, MSG_SENDER flag 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179686, + "gasUsed": 179576, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 109016, + "gasUsed": 108897, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 252135, + "gasUsed": 252034, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 182300, + "gasUsed": 182190, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 125258, + "gasUsed": 125142, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 130417, + "gasUsed": 130313, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 138406, + "gasUsed": 138314, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 108953, + "gasUsed": 108834, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127616, + "gasUsed": 127500, } `; @@ -283,69 +283,69 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 117144, + "gasUsed": 117121, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 276366, + "gasUsed": 276297, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 192426, + "gasUsed": 192380, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 118771, + "gasUsed": 118726, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 421314, + "gasUsed": 421548, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 315013, + "gasUsed": 315107, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 133408, + "gasUsed": 133388, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 135107, + "gasUsed": 135065, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 242391, + "gasUsed": 242368, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 129590, + "gasUsed": 129549, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index eaf4b768..92cf0870 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,24 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `18307`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17913`; exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2468, - "gasUsed": 252703, + "gasUsed": 252590, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152097, + "gasUsed": 152100, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: WETH --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2308, - "gasUsed": 186861, + "gasUsed": 186879, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index 5c3725a0..f0b55d1d 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1162636`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1162168`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1197018`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1196550`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3240424`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3239139`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3393845`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3392560`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4330328`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4328813`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4534102`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4532587`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `533020`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `532809`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `533338`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `533127`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `310739`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `310693`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `310675`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `310629`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; diff --git a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap index 3ad231a8..724d5967 100644 --- a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`X2Y2 ERC-721 purchase gas: purchases 1 ERC-721 on X2Y2 1`] = ` Object { "calldataByteLength": 2212, - "gasUsed": 210135, + "gasUsed": 210138, } `; exports[`X2Y2 ERC-1155 purchase gas: purchases 1 ERC-1155 on X2Y2 1`] = ` Object { "calldataByteLength": 2276, - "gasUsed": 211192, + "gasUsed": 211195, } `; From 2b2ea49c8ce7e818e80301ac9236bde502528c38 Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Sun, 7 May 2023 02:09:34 -0700 Subject: [PATCH 02/10] Compute pair address using inline assembly in `UniswapV2Library.pairForPreSorted` and clean the upper bits explicitly saving 500~1000 gas --- .gas-snapshot | 64 +++++++-------- .../modules/uniswap/v2/UniswapV2Library.sol | 25 +++--- contracts/modules/uniswap/v3/BytesLib.sol | 3 +- .../__snapshots__/CryptoPunk.gas.test.ts.snap | 2 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 78 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 4 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++--- 7 files changed, 101 insertions(+), 95 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 585ff775..d3f593b4 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -7,35 +7,35 @@ UniversalRouterTest:testSweepETH() (gas: 58089) UniversalRouterTest:testSweepETHInsufficientOutput() (gas: 76030) UniversalRouterTest:testSweepToken() (gas: 82345) UniversalRouterTest:testSweepTokenInsufficientOutput() (gas: 82056) -V2DaiWeth:testExactInput0For1() (gas: 108328) -V2DaiWeth:testExactInput0For1FromRouter() (gas: 250570) -V2DaiWeth:testExactInput1For0() (gas: 108400) -V2DaiWeth:testExactInput1For0FromRouter() (gas: 250732) -V2DaiWeth:testExactOutput0For1() (gas: 109216) -V2DaiWeth:testExactOutput0For1FromRouter() (gas: 251648) -V2DaiWeth:testExactOutput1For0() (gas: 109640) -V2DaiWeth:testExactOutput1For0FromRouter() (gas: 251890) -V2MockMock:testExactInput0For1() (gas: 107669) -V2MockMock:testExactInput0For1FromRouter() (gas: 250324) -V2MockMock:testExactInput1For0() (gas: 107628) -V2MockMock:testExactInput1For0FromRouter() (gas: 250432) -V2MockMock:testExactOutput0For1() (gas: 107833) -V2MockMock:testExactOutput0For1FromRouter() (gas: 250835) -V2MockMock:testExactOutput1For0() (gas: 108008) -V2MockMock:testExactOutput1For0FromRouter() (gas: 250922) -V2MockWeth:testExactInput0For1() (gas: 102499) -V2MockWeth:testExactInput0For1FromRouter() (gas: 245915) -V2MockWeth:testExactInput1For0() (gas: 102545) -V2MockWeth:testExactInput1For0FromRouter() (gas: 246368) -V2MockWeth:testExactOutput0For1() (gas: 102666) -V2MockWeth:testExactOutput0For1FromRouter() (gas: 246425) -V2MockWeth:testExactOutput1For0() (gas: 103798) -V2MockWeth:testExactOutput1For0FromRouter() (gas: 247535) -V2WethApe:testExactInput0For1() (gas: 113107) -V2WethApe:testExactInput0For1FromRouter() (gas: 250272) -V2WethApe:testExactInput1For0() (gas: 107925) -V2WethApe:testExactInput1For0FromRouter() (gas: 250388) -V2WethApe:testExactOutput0For1() (gas: 114127) -V2WethApe:testExactOutput0For1FromRouter() (gas: 251456) -V2WethApe:testExactOutput1For0() (gas: 109113) -V2WethApe:testExactOutput1For0FromRouter() (gas: 251513) \ No newline at end of file +V2DaiWeth:testExactInput0For1() (gas: 107336) +V2DaiWeth:testExactInput0For1FromRouter() (gas: 249776) +V2DaiWeth:testExactInput1For0() (gas: 107408) +V2DaiWeth:testExactInput1For0FromRouter() (gas: 249939) +V2DaiWeth:testExactOutput0For1() (gas: 108235) +V2DaiWeth:testExactOutput0For1FromRouter() (gas: 250864) +V2DaiWeth:testExactOutput1For0() (gas: 108659) +V2DaiWeth:testExactOutput1For0FromRouter() (gas: 251105) +V2MockMock:testExactInput0For1() (gas: 106677) +V2MockMock:testExactInput0For1FromRouter() (gas: 249531) +V2MockMock:testExactInput1For0() (gas: 106636) +V2MockMock:testExactInput1For0FromRouter() (gas: 249639) +V2MockMock:testExactOutput0For1() (gas: 106852) +V2MockMock:testExactOutput0For1FromRouter() (gas: 250050) +V2MockMock:testExactOutput1For0() (gas: 107027) +V2MockMock:testExactOutput1For0FromRouter() (gas: 250137) +V2MockWeth:testExactInput0For1() (gas: 101507) +V2MockWeth:testExactInput0For1FromRouter() (gas: 245121) +V2MockWeth:testExactInput1For0() (gas: 101553) +V2MockWeth:testExactInput1For0FromRouter() (gas: 245575) +V2MockWeth:testExactOutput0For1() (gas: 101685) +V2MockWeth:testExactOutput0For1FromRouter() (gas: 245640) +V2MockWeth:testExactOutput1For0() (gas: 102817) +V2MockWeth:testExactOutput1For0FromRouter() (gas: 246750) +V2WethApe:testExactInput0For1() (gas: 112115) +V2WethApe:testExactInput0For1FromRouter() (gas: 249479) +V2WethApe:testExactInput1For0() (gas: 106933) +V2WethApe:testExactInput1For0FromRouter() (gas: 249595) +V2WethApe:testExactOutput0For1() (gas: 113146) +V2WethApe:testExactOutput0For1FromRouter() (gas: 250672) +V2WethApe:testExactOutput1For0() (gas: 108132) +V2WethApe:testExactOutput1For0FromRouter() (gas: 250728) \ No newline at end of file diff --git a/contracts/modules/uniswap/v2/UniswapV2Library.sol b/contracts/modules/uniswap/v2/UniswapV2Library.sol index 936d7cd1..9cc68071 100644 --- a/contracts/modules/uniswap/v2/UniswapV2Library.sol +++ b/contracts/modules/uniswap/v2/UniswapV2Library.sol @@ -52,15 +52,22 @@ library UniswapV2Library { pure returns (address pair) { - pair = address( - uint160( - uint256( - keccak256( - abi.encodePacked(hex'ff', factory, keccak256(abi.encodePacked(token0, token1)), initCodeHash) - ) - ) - ) - ); + assembly ("memory-safe") { + // Get the free memory pointer. + let fmp := mload(0x40) + // keccak256(abi.encodePacked(token0, token1)) + mstore(add(fmp, 0x14), token1) + mstore(fmp, token0) + let pairHash := keccak256(add(fmp, 0x0c), 0x28) + // abi.encodePacked(hex'ff', factory, pairHash, initCodeHash) + mstore(fmp, factory) + fmp := add(fmp, 0x0b) + mstore8(fmp, 0xff) + mstore(add(fmp, 0x15), pairHash) + mstore(add(fmp, 0x35), initCodeHash) + // Compute the CREATE2 pair address and clean the upper bits. + pair := and(keccak256(fmp, 0x55), 0xffffffffffffffffffffffffffffffffffffffff) + } } /// @notice Calculates the v2 address for a pair and fetches the reserves for each token diff --git a/contracts/modules/uniswap/v3/BytesLib.sol b/contracts/modules/uniswap/v3/BytesLib.sol index 06520ff6..cb5a5a16 100644 --- a/contracts/modules/uniswap/v3/BytesLib.sol +++ b/contracts/modules/uniswap/v3/BytesLib.sol @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-3.0-or-later - -/// @title Library for Bytes Manipulation pragma solidity ^0.8.0; import {Constants} from '../../../libraries/Constants.sol'; +/// @title Library for Bytes Manipulation library BytesLib { error SliceOutOfBounds(); diff --git a/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap index bd5c70ec..00882e9e 100644 --- a/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`Cryptopunks purchases 1 cryptopunk gas 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 109587, + "gasUsed": 109576, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 314e3e80..dddf95e6 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,105 +3,105 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 272999, + "gasUsed": 272500, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 248721, + "gasUsed": 248222, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 248721, + "gasUsed": 248222, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 272999, + "gasUsed": 272500, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 196686, + "gasUsed": 196278, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 189482, + "gasUsed": 189074, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit 1`] = ` Object { "calldataByteLength": 1540, - "gasUsed": 305098, + "gasUsed": 303823, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = ` Object { "calldataByteLength": 1220, - "gasUsed": 313170, + "gasUsed": 311897, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from batch 1`] = ` Object { "calldataByteLength": 1284, - "gasUsed": 315224, + "gasUsed": 313950, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, without explicit permit 1`] = ` Object { "calldataByteLength": 900, - "gasUsed": 309598, + "gasUsed": 308325, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 179664, + "gasUsed": 179255, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 179439, + "gasUsed": 179030, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 198074, + "gasUsed": 197666, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 187492, + "gasUsed": 187083, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 1124, - "gasUsed": 200182, + "gasUsed": 199773, } `; @@ -143,98 +143,98 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn trade, where an output fee is taken 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 128729, + "gasUsed": 128411, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108923, + "gasUsed": 108605, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 246851, + "gasUsed": 245897, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops, no deadline 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 246593, + "gasUsed": 245639, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179576, + "gasUsed": 178941, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops, MSG_SENDER flag 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 179576, + "gasUsed": 178941, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108897, + "gasUsed": 108580, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 252034, + "gasUsed": 250441, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 182190, + "gasUsed": 181236, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 125142, + "gasUsed": 124824, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 130313, + "gasUsed": 129996, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 138314, + "gasUsed": 137997, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 108834, + "gasUsed": 108516, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127500, + "gasUsed": 127183, } `; @@ -283,69 +283,69 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 117121, + "gasUsed": 117031, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 276297, + "gasUsed": 276027, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 192380, + "gasUsed": 192200, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 118726, + "gasUsed": 118636, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 421548, + "gasUsed": 421278, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 315107, + "gasUsed": 314927, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 133388, + "gasUsed": 133298, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 135065, + "gasUsed": 134975, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 242368, + "gasUsed": 242278, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 129549, + "gasUsed": 129459, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 92cf0870..4f362ba9 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,11 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17913`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17663`; exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2468, - "gasUsed": 252590, + "gasUsed": 252271, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index f0b55d1d..737b233a 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1162168`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1160628`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1196550`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1195010`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3239139`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3234839`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3392560`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3388260`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4328813`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4323613`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4532587`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4527387`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `532809`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `532129`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `533127`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `532447`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `310693`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `310513`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `310629`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `310449`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; From 8deab37ce68069491e5ee4b83be0b4035c60963b Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Sun, 7 May 2023 02:30:35 -0700 Subject: [PATCH 03/10] Rewrite `V3SwapRouter.computePoolAddress` using inline assembly and clean the upper bits at the same gas --- contracts/modules/uniswap/v3/V3SwapRouter.sol | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index 44966bdf..5b07b3f4 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -158,20 +158,28 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S } function computePoolAddress(address tokenA, address tokenB, uint24 fee) private view returns (address pool) { + address factory = UNISWAP_V3_FACTORY; + bytes32 initCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH; if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA); - pool = address( - uint160( - uint256( - keccak256( - abi.encodePacked( - hex'ff', - UNISWAP_V3_FACTORY, - keccak256(abi.encode(tokenA, tokenB, fee)), - UNISWAP_V3_POOL_INIT_CODE_HASH - ) - ) - ) + assembly ("memory-safe") { + // Get the free memory pointer. + let fmp := mload(0x40) + // Hash the pool key. + mstore(fmp, tokenA) + mstore(add(fmp, 0x20), tokenB) + mstore(add(fmp, 0x40), fee) + let poolHash := keccak256(fmp, 0x60) + // abi.encodePacked(hex'ff', factory, poolHash, initCodeHash) + mstore(fmp, factory) + fmp := add(fmp, 0x0b) + mstore8(fmp, 0xff) + mstore(add(fmp, 0x15), poolHash) + mstore(add(fmp, 0x35), initCodeHash) + // Compute the CREATE2 pool address and clean the upper bits. + pool := and( + keccak256(fmp, 0x55), + 0xffffffffffffffffffffffffffffffffffffffff ) - ); + } } } From d16a6d9c74e51c44fc4df083334c94bc3015dc8a Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Sun, 7 May 2023 04:51:01 -0700 Subject: [PATCH 04/10] Run Hardhat gas snapshot to demonstrate `computePoolAddress` optimization since there weren't V3 Foundry tests Rewritten `computePoolAddress` saves ~500 gas --- .gas-snapshot | 82 +++++++++---------- .../__snapshots__/Uniswap.gas.test.ts.snap | 42 +++++----- .../UniversalRouter.gas.test.ts.snap | 2 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++--- 4 files changed, 73 insertions(+), 73 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index d3f593b4..07756090 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,41 +1,41 @@ -UniversalRouterTest:testCallModule() (gas: 6639) -UniversalRouterTest:testSupportsInterface() (gas: 9045) -UniversalRouterTest:testSweepERC1155() (gas: 70732) -UniversalRouterTest:testSweepERC1155InsufficientOutput() (gas: 60170) -UniversalRouterTest:testSweepERC1155NotFullAmount() (gas: 70713) -UniversalRouterTest:testSweepETH() (gas: 58089) -UniversalRouterTest:testSweepETHInsufficientOutput() (gas: 76030) -UniversalRouterTest:testSweepToken() (gas: 82345) -UniversalRouterTest:testSweepTokenInsufficientOutput() (gas: 82056) -V2DaiWeth:testExactInput0For1() (gas: 107336) -V2DaiWeth:testExactInput0For1FromRouter() (gas: 249776) -V2DaiWeth:testExactInput1For0() (gas: 107408) -V2DaiWeth:testExactInput1For0FromRouter() (gas: 249939) -V2DaiWeth:testExactOutput0For1() (gas: 108235) -V2DaiWeth:testExactOutput0For1FromRouter() (gas: 250864) -V2DaiWeth:testExactOutput1For0() (gas: 108659) -V2DaiWeth:testExactOutput1For0FromRouter() (gas: 251105) -V2MockMock:testExactInput0For1() (gas: 106677) -V2MockMock:testExactInput0For1FromRouter() (gas: 249531) -V2MockMock:testExactInput1For0() (gas: 106636) -V2MockMock:testExactInput1For0FromRouter() (gas: 249639) -V2MockMock:testExactOutput0For1() (gas: 106852) -V2MockMock:testExactOutput0For1FromRouter() (gas: 250050) -V2MockMock:testExactOutput1For0() (gas: 107027) -V2MockMock:testExactOutput1For0FromRouter() (gas: 250137) -V2MockWeth:testExactInput0For1() (gas: 101507) -V2MockWeth:testExactInput0For1FromRouter() (gas: 245121) -V2MockWeth:testExactInput1For0() (gas: 101553) -V2MockWeth:testExactInput1For0FromRouter() (gas: 245575) -V2MockWeth:testExactOutput0For1() (gas: 101685) -V2MockWeth:testExactOutput0For1FromRouter() (gas: 245640) -V2MockWeth:testExactOutput1For0() (gas: 102817) -V2MockWeth:testExactOutput1For0FromRouter() (gas: 246750) -V2WethApe:testExactInput0For1() (gas: 112115) -V2WethApe:testExactInput0For1FromRouter() (gas: 249479) -V2WethApe:testExactInput1For0() (gas: 106933) -V2WethApe:testExactInput1For0FromRouter() (gas: 249595) -V2WethApe:testExactOutput0For1() (gas: 113146) -V2WethApe:testExactOutput0For1FromRouter() (gas: 250672) -V2WethApe:testExactOutput1For0() (gas: 108132) -V2WethApe:testExactOutput1For0FromRouter() (gas: 250728) \ No newline at end of file +UniversalRouterTest:testCallModule() (gas: 6140) +UniversalRouterTest:testSupportsInterface() (gas: 8798) +UniversalRouterTest:testSweepERC1155() (gas: 62829) +UniversalRouterTest:testSweepERC1155InsufficientOutput() (gas: 52550) +UniversalRouterTest:testSweepERC1155NotFullAmount() (gas: 62840) +UniversalRouterTest:testSweepETH() (gas: 52960) +UniversalRouterTest:testSweepETHInsufficientOutput() (gas: 69123) +UniversalRouterTest:testSweepToken() (gas: 73822) +UniversalRouterTest:testSweepTokenInsufficientOutput() (gas: 74095) +V2DaiWeth:testExactInput0For1() (gas: 99957) +V2DaiWeth:testExactInput0For1FromRouter() (gas: 218758) +V2DaiWeth:testExactInput1For0() (gas: 100008) +V2DaiWeth:testExactInput1For0FromRouter() (gas: 218832) +V2DaiWeth:testExactOutput0For1() (gas: 99112) +V2DaiWeth:testExactOutput0For1FromRouter() (gas: 218182) +V2DaiWeth:testExactOutput1For0() (gas: 99321) +V2DaiWeth:testExactOutput1For0FromRouter() (gas: 218322) +V2MockMock:testExactInput0For1() (gas: 95197) +V2MockMock:testExactInput0For1FromRouter() (gas: 214974) +V2MockMock:testExactInput1For0() (gas: 95135) +V2MockMock:testExactInput1For0FromRouter() (gas: 214994) +V2MockMock:testExactOutput0For1() (gas: 94103) +V2MockMock:testExactOutput0For1FromRouter() (gas: 214190) +V2MockMock:testExactOutput1For0() (gas: 94052) +V2MockMock:testExactOutput1For0FromRouter() (gas: 214167) +V2MockWeth:testExactInput0For1() (gas: 92036) +V2MockWeth:testExactInput0For1FromRouter() (gas: 212419) +V2MockWeth:testExactInput1For0() (gas: 92156) +V2MockWeth:testExactInput1For0FromRouter() (gas: 212613) +V2MockWeth:testExactOutput0For1() (gas: 90942) +V2MockWeth:testExactOutput0For1FromRouter() (gas: 211635) +V2MockWeth:testExactOutput1For0() (gas: 91469) +V2MockWeth:testExactOutput1For0FromRouter() (gas: 212103) +V2WethApe:testExactInput0For1() (gas: 104715) +V2WethApe:testExactInput0For1FromRouter() (gas: 218444) +V2WethApe:testExactInput1For0() (gas: 99554) +V2WethApe:testExactInput1For0FromRouter() (gas: 218505) +V2WethApe:testExactOutput0For1() (gas: 103996) +V2WethApe:testExactOutput0For1FromRouter() (gas: 217968) +V2WethApe:testExactOutput1For0() (gas: 98821) +V2WethApe:testExactOutput1For0FromRouter() (gas: 217958) \ No newline at end of file diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index dddf95e6..3625e860 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,42 +3,42 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 272500, + "gasUsed": 271482, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 248222, + "gasUsed": 247204, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 248222, + "gasUsed": 247204, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 272500, + "gasUsed": 271482, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 196278, + "gasUsed": 195769, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 189074, + "gasUsed": 188565, } `; @@ -73,35 +73,35 @@ Object { exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 179255, + "gasUsed": 178746, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 179030, + "gasUsed": 178521, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 197666, + "gasUsed": 197145, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 187083, + "gasUsed": 186574, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 1124, - "gasUsed": 199773, + "gasUsed": 199252, } `; @@ -283,69 +283,69 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 117031, + "gasUsed": 116522, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 276027, + "gasUsed": 274486, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 192200, + "gasUsed": 191170, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 118636, + "gasUsed": 118115, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 421278, + "gasUsed": 419724, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 314927, + "gasUsed": 313884, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 133298, + "gasUsed": 132789, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 134975, + "gasUsed": 134454, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 242278, + "gasUsed": 241757, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 129459, + "gasUsed": 128950, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 4f362ba9..c68da95f 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17663`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17545`; exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` Object { diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index 737b233a..d460be3d 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1160628`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1155468`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1195010`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1189850`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3234839`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3219364`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3388260`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3372785`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4323613`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4302988`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4527387`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4506762`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `532129`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `530064`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `532447`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530382`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `310513`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309483`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `310449`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309419`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; From b3bfe3211588155edbecf0b092f84801a0739ca0 Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Sun, 7 May 2023 04:53:59 -0700 Subject: [PATCH 05/10] Add Foundry test `UniswapV3Test` for more granular gas comparison --- .gas-snapshot | 6 +- test/foundry-tests/UniswapV2.t.sol | 3 - test/foundry-tests/UniswapV3.t.sol | 149 ++++++++++++++++++ .../uniswapTokens/v3MockMock.t.sol | 24 +++ 4 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 test/foundry-tests/UniswapV3.t.sol create mode 100644 test/foundry-tests/uniswapTokens/v3MockMock.t.sol diff --git a/.gas-snapshot b/.gas-snapshot index 07756090..6fb50d61 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -38,4 +38,8 @@ V2WethApe:testExactInput1For0FromRouter() (gas: 218505) V2WethApe:testExactOutput0For1() (gas: 103996) V2WethApe:testExactOutput0For1FromRouter() (gas: 217968) V2WethApe:testExactOutput1For0() (gas: 98821) -V2WethApe:testExactOutput1For0FromRouter() (gas: 217958) \ No newline at end of file +V2WethApe:testExactOutput1For0FromRouter() (gas: 217958) +V3MockMock:testExactInput0For1() (gas: 123400) +V3MockMock:testExactInput1For0() (gas: 130552) +V3MockMock:testExactOutput0For1() (gas: 125721) +V3MockMock:testExactOutput1For0() (gas: 131894) \ No newline at end of file diff --git a/test/foundry-tests/UniswapV2.t.sol b/test/foundry-tests/UniswapV2.t.sol index c925ea85..97935e56 100644 --- a/test/foundry-tests/UniswapV2.t.sol +++ b/test/foundry-tests/UniswapV2.t.sol @@ -12,9 +12,6 @@ import {Constants} from '../../contracts/libraries/Constants.sol'; import {Commands} from '../../contracts/libraries/Commands.sol'; import {RouterParameters} from '../../contracts/base/RouterImmutables.sol'; -import '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol'; -import '@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol'; - abstract contract UniswapV2Test is Test { address constant RECIPIENT = address(10); uint256 constant AMOUNT = 1 ether; diff --git a/test/foundry-tests/UniswapV3.t.sol b/test/foundry-tests/UniswapV3.t.sol new file mode 100644 index 00000000..e6cc87ce --- /dev/null +++ b/test/foundry-tests/UniswapV3.t.sol @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.15; + +import 'forge-std/Test.sol'; +import {Permit2} from 'permit2/src/Permit2.sol'; +import {ERC20, SafeTransferLib} from 'solmate/src/utils/SafeTransferLib.sol'; +import {IUniswapV3Factory} from '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol'; +import {IUniswapV3Pool} from '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; +import {IUniswapV3MintCallback} from '@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol'; +import {UniversalRouter} from '../../contracts/UniversalRouter.sol'; +import {Payments} from '../../contracts/modules/Payments.sol'; +import {Constants} from '../../contracts/libraries/Constants.sol'; +import {Commands} from '../../contracts/libraries/Commands.sol'; +import {RouterParameters} from '../../contracts/base/RouterImmutables.sol'; + +abstract contract UniswapV3Test is Test, IUniswapV3MintCallback { + using SafeTransferLib for ERC20; + + /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 + int24 internal constant MIN_TICK = -887272; + /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 + int24 internal constant MAX_TICK = -MIN_TICK; + uint24 internal constant fee = 3000; + + address constant RECIPIENT = address(10); + uint256 constant AMOUNT = 1 ether; + uint256 constant BALANCE = 100000 ether; + IUniswapV3Factory constant FACTORY = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); + ERC20 constant WETH9 = ERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + Permit2 constant PERMIT2 = Permit2(0x000000000022D473030F116dDEE9F6B43aC78BA3); + address constant FROM = address(1234); + + UniversalRouter router; + address pool; + + function setUp() public virtual { + vm.createSelectFork(vm.envString('FORK_URL'), 16000000); + setUpTokens(); + + RouterParameters memory params = RouterParameters({ + permit2: address(PERMIT2), + weth9: address(WETH9), + seaportV1_5: address(0), + seaportV1_4: address(0), + openseaConduit: address(0), + nftxZap: address(0), + x2y2: address(0), + foundation: address(0), + sudoswap: address(0), + elementMarket: address(0), + nft20Zap: address(0), + cryptopunks: address(0), + looksRareV2: address(0), + routerRewardsDistributor: address(0), + looksRareRewardsDistributor: address(0), + looksRareToken: address(0), + v2Factory: address(0), + v3Factory: address(FACTORY), + pairInitCodeHash: bytes32(0), + poolInitCodeHash: bytes32(0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54) + }); + router = new UniversalRouter(params); + + // pool doesn't exist, make a mock one + if (FACTORY.getPool(token0(), token1(), fee) == address(0)) { + pool = FACTORY.createPool(token0(), token1(), fee); + IUniswapV3Pool(pool).initialize(1 << 96); + int24 tickSpacing = IUniswapV3Pool(pool).tickSpacing(); + mint( + address(this), + MIN_TICK / tickSpacing * tickSpacing, + MAX_TICK / tickSpacing * tickSpacing, + uint128(AMOUNT * 10) + ); + } + vm.label(pool, 'pool'); + vm.label(token0(), 'token0'); + vm.label(token1(), 'token1'); + + 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); + } + + /// @dev Pay pool to finish minting + function uniswapV3MintCallback(uint256 amount0Owed, uint256 amount1Owed, bytes calldata) external { + if (amount0Owed > 0) deal(token0(), msg.sender, amount0Owed); + if (amount1Owed > 0) deal(token1(), msg.sender, amount1Owed); + } + + /// @dev Make a direct pool mint + function mint(address recipient, int24 tickLower, int24 tickUpper, uint128 liquidity) internal { + IUniswapV3Pool(pool).mint(recipient, tickLower, tickUpper, liquidity, new bytes(0)); + } + + function testExactInput0For1() public { + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V3_SWAP_EXACT_IN))); + bytes memory path = abi.encodePacked(token0(), fee, token1()); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(Constants.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.V3_SWAP_EXACT_IN))); + bytes memory path = abi.encodePacked(token1(), fee, token0()); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(Constants.MSG_SENDER, AMOUNT, 0, path, true); + + router.execute(commands, inputs); + assertEq(ERC20(token1()).balanceOf(FROM), BALANCE - AMOUNT); + assertGt(ERC20(token0()).balanceOf(FROM), BALANCE); + } + + function testExactOutput0For1() public { + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V3_SWAP_EXACT_OUT))); + bytes memory path = abi.encodePacked(token1(), fee, token0()); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(Constants.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.V3_SWAP_EXACT_OUT))); + bytes memory path = abi.encodePacked(token0(), fee, token1()); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(Constants.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 token0() internal virtual returns (address); + function token1() internal virtual returns (address); + + function setUpTokens() internal virtual; +} diff --git a/test/foundry-tests/uniswapTokens/v3MockMock.t.sol b/test/foundry-tests/uniswapTokens/v3MockMock.t.sol new file mode 100644 index 00000000..2616ceac --- /dev/null +++ b/test/foundry-tests/uniswapTokens/v3MockMock.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.15; + +import 'forge-std/Test.sol'; +import {MockERC20} from '../mock/MockERC20.sol'; +import {UniswapV3Test} from '../UniswapV3.t.sol'; + +contract V3MockMock is UniswapV3Test { + MockERC20 mockA; + MockERC20 mockB; + + function setUpTokens() internal override { + mockA = new MockERC20(); + mockB = new MockERC20(); + } + + function token0() internal view override returns (address) { + return address(mockA); + } + + function token1() internal view override returns (address) { + return address(mockB); + } +} From bade8444361b9f87cbfabb11446f8ac9c8d605c5 Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Sun, 7 May 2023 06:16:35 -0700 Subject: [PATCH 06/10] Replace conditional statements with bitwise operations --- .gas-snapshot | 72 ++++++++--------- contracts/modules/uniswap/v2/TernaryLib.sol | 42 ++++++++++ .../modules/uniswap/v2/UniswapV2Library.sol | 16 +--- contracts/modules/uniswap/v2/V2SwapRouter.sol | 7 +- contracts/modules/uniswap/v3/V3SwapRouter.sol | 68 +++++++++++----- .../CheckOwnership.gas.test.ts.snap | 10 +-- .../__snapshots__/CryptoPunk.gas.test.ts.snap | 2 +- .../__snapshots__/Element.gas.test.ts.snap | 2 +- .../__snapshots__/Foundation.gas.test.ts.snap | 2 +- .../__snapshots__/NFT20.gas.test.ts.snap | 2 +- .../__snapshots__/NFTX.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 16 ++-- .../SeaportV1_4.gas.test.ts.snap | 4 +- .../SeaportV1_5.gas.test.ts.snap | 4 +- .../__snapshots__/Sudoswap.gas.test.ts.snap | 6 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 78 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 8 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++--- .../__snapshots__/X2Y2.gas.test.ts.snap | 4 +- 19 files changed, 214 insertions(+), 151 deletions(-) create mode 100644 contracts/modules/uniswap/v2/TernaryLib.sol diff --git a/.gas-snapshot b/.gas-snapshot index 6fb50d61..4ea7134f 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -7,39 +7,39 @@ UniversalRouterTest:testSweepETH() (gas: 52960) UniversalRouterTest:testSweepETHInsufficientOutput() (gas: 69123) UniversalRouterTest:testSweepToken() (gas: 73822) UniversalRouterTest:testSweepTokenInsufficientOutput() (gas: 74095) -V2DaiWeth:testExactInput0For1() (gas: 99957) -V2DaiWeth:testExactInput0For1FromRouter() (gas: 218758) -V2DaiWeth:testExactInput1For0() (gas: 100008) -V2DaiWeth:testExactInput1For0FromRouter() (gas: 218832) -V2DaiWeth:testExactOutput0For1() (gas: 99112) -V2DaiWeth:testExactOutput0For1FromRouter() (gas: 218182) -V2DaiWeth:testExactOutput1For0() (gas: 99321) -V2DaiWeth:testExactOutput1For0FromRouter() (gas: 218322) -V2MockMock:testExactInput0For1() (gas: 95197) -V2MockMock:testExactInput0For1FromRouter() (gas: 214974) -V2MockMock:testExactInput1For0() (gas: 95135) -V2MockMock:testExactInput1For0FromRouter() (gas: 214994) -V2MockMock:testExactOutput0For1() (gas: 94103) -V2MockMock:testExactOutput0For1FromRouter() (gas: 214190) -V2MockMock:testExactOutput1For0() (gas: 94052) -V2MockMock:testExactOutput1For0FromRouter() (gas: 214167) -V2MockWeth:testExactInput0For1() (gas: 92036) -V2MockWeth:testExactInput0For1FromRouter() (gas: 212419) -V2MockWeth:testExactInput1For0() (gas: 92156) -V2MockWeth:testExactInput1For0FromRouter() (gas: 212613) -V2MockWeth:testExactOutput0For1() (gas: 90942) -V2MockWeth:testExactOutput0For1FromRouter() (gas: 211635) -V2MockWeth:testExactOutput1For0() (gas: 91469) -V2MockWeth:testExactOutput1For0FromRouter() (gas: 212103) -V2WethApe:testExactInput0For1() (gas: 104715) -V2WethApe:testExactInput0For1FromRouter() (gas: 218444) -V2WethApe:testExactInput1For0() (gas: 99554) -V2WethApe:testExactInput1For0FromRouter() (gas: 218505) -V2WethApe:testExactOutput0For1() (gas: 103996) -V2WethApe:testExactOutput0For1FromRouter() (gas: 217968) -V2WethApe:testExactOutput1For0() (gas: 98821) -V2WethApe:testExactOutput1For0FromRouter() (gas: 217958) -V3MockMock:testExactInput0For1() (gas: 123400) -V3MockMock:testExactInput1For0() (gas: 130552) -V3MockMock:testExactOutput0For1() (gas: 125721) -V3MockMock:testExactOutput1For0() (gas: 131894) \ No newline at end of file +V2DaiWeth:testExactInput0For1() (gas: 99728) +V2DaiWeth:testExactInput0For1FromRouter() (gas: 218575) +V2DaiWeth:testExactInput1For0() (gas: 99819) +V2DaiWeth:testExactInput1For0FromRouter() (gas: 218681) +V2DaiWeth:testExactOutput0For1() (gas: 98863) +V2DaiWeth:testExactOutput0For1FromRouter() (gas: 217983) +V2DaiWeth:testExactOutput1For0() (gas: 99122) +V2DaiWeth:testExactOutput1For0FromRouter() (gas: 218163) +V2MockMock:testExactInput0For1() (gas: 94968) +V2MockMock:testExactInput0For1FromRouter() (gas: 214791) +V2MockMock:testExactInput1For0() (gas: 94946) +V2MockMock:testExactInput1For0FromRouter() (gas: 214843) +V2MockMock:testExactOutput0For1() (gas: 93854) +V2MockMock:testExactOutput0For1FromRouter() (gas: 213991) +V2MockMock:testExactOutput1For0() (gas: 93853) +V2MockMock:testExactOutput1For0FromRouter() (gas: 214008) +V2MockWeth:testExactInput0For1() (gas: 91807) +V2MockWeth:testExactInput0For1FromRouter() (gas: 212236) +V2MockWeth:testExactInput1For0() (gas: 91967) +V2MockWeth:testExactInput1For0FromRouter() (gas: 212462) +V2MockWeth:testExactOutput0For1() (gas: 90693) +V2MockWeth:testExactOutput0For1FromRouter() (gas: 211436) +V2MockWeth:testExactOutput1For0() (gas: 91270) +V2MockWeth:testExactOutput1For0FromRouter() (gas: 211944) +V2WethApe:testExactInput0For1() (gas: 104526) +V2WethApe:testExactInput0For1FromRouter() (gas: 218292) +V2WethApe:testExactInput1For0() (gas: 99325) +V2WethApe:testExactInput1For0FromRouter() (gas: 218322) +V2WethApe:testExactOutput0For1() (gas: 103797) +V2WethApe:testExactOutput0For1FromRouter() (gas: 217809) +V2WethApe:testExactOutput1For0() (gas: 98572) +V2WethApe:testExactOutput1For0FromRouter() (gas: 217759) +V3MockMock:testExactInput0For1() (gas: 123143) +V3MockMock:testExactInput1For0() (gas: 130334) +V3MockMock:testExactOutput0For1() (gas: 125474) +V3MockMock:testExactOutput1For0() (gas: 131651) \ No newline at end of file diff --git a/contracts/modules/uniswap/v2/TernaryLib.sol b/contracts/modules/uniswap/v2/TernaryLib.sol new file mode 100644 index 00000000..991eef91 --- /dev/null +++ b/contracts/modules/uniswap/v2/TernaryLib.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.5.0; + +/// @title Library for replacing ternary operator with efficient bitwise operations +library TernaryLib { + /// @notice Equivalent to the ternary operator: `condition ? a : b` + function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256 res) { + assembly { + res := xor(b, mul(xor(a, b), condition)) + } + } + + /// @notice Sorts two uint256 in ascending order + /// @dev Equivalent to: `a < b ? (a, b) : (b, a)` + function sort2(uint256 a, uint256 b) internal pure returns (uint256, uint256) { + return swapIf(b < a, a, b); + } + + /// @notice Sorts two tokens to return token0 and token1 + /// @param tokenA The first token to sort + /// @param tokenB The other token to sort + /// @return token0 The smaller token by address value + /// @return token1 The larger token by address value + function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { + assembly { + let diff := mul(xor(tokenA, tokenB), lt(tokenB, tokenA)) + token0 := xor(tokenA, diff) + token1 := xor(tokenB, diff) + } + } + + /// @notice Swaps two uint256 if `condition` is true + /// @dev Equivalent to: `condition ? (b, a) : (a, b)` + function swapIf(bool condition, uint256 a, uint256 b) internal pure returns (uint256, uint256) { + assembly { + let diff := mul(xor(a, b), condition) + a := xor(a, diff) + b := xor(b, diff) + } + return (a, b); + } +} diff --git a/contracts/modules/uniswap/v2/UniswapV2Library.sol b/contracts/modules/uniswap/v2/UniswapV2Library.sol index 9cc68071..5162364b 100644 --- a/contracts/modules/uniswap/v2/UniswapV2Library.sol +++ b/contracts/modules/uniswap/v2/UniswapV2Library.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; +import {TernaryLib} from './TernaryLib.sol'; /// @title Uniswap v2 Helper Library /// @notice Calculates the recipient address for a command @@ -20,7 +21,7 @@ library UniswapV2Library { pure returns (address pair) { - (address token0, address token1) = sortTokens(tokenA, tokenB); + (address token0, address token1) = TernaryLib.sortTokens(tokenA, tokenB); pair = pairForPreSorted(factory, initCodeHash, token0, token1); } @@ -37,7 +38,7 @@ library UniswapV2Library { returns (address pair, address token0) { address token1; - (token0, token1) = sortTokens(tokenA, tokenB); + (token0, token1) = TernaryLib.sortTokens(tokenA, tokenB); pair = pairForPreSorted(factory, initCodeHash, token0, token1); } @@ -86,7 +87,7 @@ library UniswapV2Library { address token0; (pair, token0) = pairAndToken0For(factory, initCodeHash, tokenA, tokenB); (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves(); - (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0); + (reserveA, reserveB) = TernaryLib.swapIf(tokenA == token0, reserve1, reserve0); } /// @notice Given an input asset amount returns the maximum output amount of the other asset @@ -144,13 +145,4 @@ library UniswapV2Library { amount = getAmountIn(amount, reserveIn, reserveOut); } } - - /// @notice Sorts two tokens to return token0 and token1 - /// @param tokenA The first token to sort - /// @param tokenB The other token to sort - /// @return token0 The smaller token by address value - /// @return token1 The larger token by address value - function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { - (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); - } } diff --git a/contracts/modules/uniswap/v2/V2SwapRouter.sol b/contracts/modules/uniswap/v2/V2SwapRouter.sol index 2d843e8a..8a9e51e0 100644 --- a/contracts/modules/uniswap/v2/V2SwapRouter.sol +++ b/contracts/modules/uniswap/v2/V2SwapRouter.sol @@ -8,6 +8,7 @@ import {Payments} from '../../Payments.sol'; import {Permit2Payments} from '../../Permit2Payments.sol'; import {Constants} from '../../../libraries/Constants.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {TernaryLib} from './TernaryLib.sol'; /// @title Router for Uniswap v2 Trades abstract contract V2SwapRouter is RouterImmutables, Permit2Payments { @@ -20,7 +21,7 @@ abstract contract V2SwapRouter is RouterImmutables, Permit2Payments { if (path.length < 2) revert V2InvalidPath(); // cached to save on duplicate operations - (address token0,) = UniswapV2Library.sortTokens(path[0], path[1]); + (address token0,) = TernaryLib.sortTokens(path[0], path[1]); uint256 finalPairIndex = path.length - 1; uint256 penultimatePairIndex = finalPairIndex - 1; for (uint256 i; i < finalPairIndex; ++i) { @@ -30,10 +31,10 @@ abstract contract V2SwapRouter is RouterImmutables, Permit2Payments { { (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves(); (uint256 reserveInput, uint256 reserveOutput) = - input == token0 ? (reserve0, reserve1) : (reserve1, reserve0); + TernaryLib.swapIf(input == token0, reserve1, reserve0); uint256 amountInput = ERC20(input).balanceOf(pair) - reserveInput; uint256 amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput); - (amount0Out, amount1Out) = input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0)); + (amount0Out, amount1Out) = TernaryLib.swapIf(input == token0, amountOutput, 0); } address nextPair; (nextPair, token0) = i < penultimatePairIndex diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index 5b07b3f4..df7dd176 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -90,16 +90,26 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S while (true) { bool hasMultiplePools = path.hasMultiplePools(); + // for intermediate swaps, this contract custodies + address _recipient; + assembly { + // hasMultiplePools ? address(this) : recipient + _recipient := xor(recipient, mul(xor(address(), recipient), hasMultiplePools)) + } // the outputs of prior swaps become the inputs to subsequent ones (int256 amount0Delta, int256 amount1Delta, bool zeroForOne) = _swap( amountIn.toInt256(), - hasMultiplePools ? address(this) : recipient, // for intermediate swaps, this contract custodies + _recipient, path.getFirstPool(), // only the first pool is needed payer, // for intermediate swaps, this contract custodies true ); - amountIn = uint256(-(zeroForOne ? amount1Delta : amount0Delta)); + // amountIn = uint256(-(zeroForOne ? amount1Delta : amount0Delta)) + assembly { + // no need to check for underflow here as it will be caught in `toInt256()` + amountIn := sub(0, xor(amount0Delta, mul(xor(amount1Delta, amount0Delta), zeroForOne))) + } // decide whether to continue or terminate if (hasMultiplePools) { @@ -131,7 +141,12 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S (int256 amount0Delta, int256 amount1Delta, bool zeroForOne) = _swap(-amountOut.toInt256(), recipient, path, payer, false); - uint256 amountOutReceived = zeroForOne ? uint256(-amount1Delta) : uint256(-amount0Delta); + uint256 amountOutReceived; + assembly { + // amountOutReceived = uint256(-(zeroForOne ? amount1Delta : amount0Delta)) + // no need to check for underflow + amountOutReceived := sub(0, xor(amount0Delta, mul(xor(amount1Delta, amount0Delta), zeroForOne))) + } if (amountOutReceived != amountOut) revert V3InvalidAmountOut(); @@ -144,29 +159,45 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S private returns (int256 amount0Delta, int256 amount1Delta, bool zeroForOne) { - (address tokenIn, uint24 fee, address tokenOut) = path.decodeFirstPool(); - - zeroForOne = isExactIn ? tokenIn < tokenOut : tokenOut < tokenIn; + address pool; + { + (address tokenIn, uint24 fee, address tokenOut) = path.decodeFirstPool(); + pool = computePoolAddress(tokenIn, tokenOut, fee); + // When isExactIn == 1, zeroForOne = tokenIn < tokenOut = !(tokenOut < tokenIn) = 1 ^ (tokenOut < tokenIn) + // When isExactIn == 0, zeroForOne = tokenOut < tokenIn = 0 ^ (tokenOut < tokenIn) + assembly { + zeroForOne := xor(isExactIn, lt(tokenOut, tokenIn)) + } + } - (amount0Delta, amount1Delta) = IUniswapV3Pool(computePoolAddress(tokenIn, tokenOut, fee)).swap( - recipient, - zeroForOne, - amount, - (zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1), - abi.encode(path, payer) - ); + uint160 sqrtPriceLimitX96; + // Equivalent to `sqrtPriceLimitX96 = zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1` + assembly { + sqrtPriceLimitX96 := + xor( + 0xfffd8963efd1fc6a506488495d951d5263988d25, // MAX_SQRT_RATIO - 1 + mul( + 0xfffd8963efd1fc6a506488495d951d53639afb81, // MAX_SQRT_RATIO - 1 ^ MIN_SQRT_RATIO + 1 + zeroForOne + ) + ) + } + (amount0Delta, amount1Delta) = + IUniswapV3Pool(pool).swap(recipient, zeroForOne, amount, sqrtPriceLimitX96, abi.encode(path, payer)); } function computePoolAddress(address tokenA, address tokenB, uint24 fee) private view returns (address pool) { address factory = UNISWAP_V3_FACTORY; bytes32 initCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH; - if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA); assembly ("memory-safe") { // Get the free memory pointer. let fmp := mload(0x40) // Hash the pool key. - mstore(fmp, tokenA) - mstore(add(fmp, 0x20), tokenB) + // Equivalent to `if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA)` + let diff := mul(xor(tokenA, tokenB), lt(tokenB, tokenA)) + // poolHash = abi.encode(tokenA, tokenB, fee) + mstore(fmp, xor(tokenA, diff)) + mstore(add(fmp, 0x20), xor(tokenB, diff)) mstore(add(fmp, 0x40), fee) let poolHash := keccak256(fmp, 0x60) // abi.encodePacked(hex'ff', factory, poolHash, initCodeHash) @@ -176,10 +207,7 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S mstore(add(fmp, 0x15), poolHash) mstore(add(fmp, 0x35), initCodeHash) // Compute the CREATE2 pool address and clean the upper bits. - pool := and( - keccak256(fmp, 0x55), - 0xffffffffffffffffffffffffffffffffffffffff - ) + pool := and(keccak256(fmp, 0x55), 0xffffffffffffffffffffffffffffffffffffffff) } } } diff --git a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap index 6e029571..6f8e8da6 100644 --- a/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/CheckOwnership.gas.test.ts.snap @@ -3,34 +3,34 @@ exports[`Check Ownership Gas gas: balance check ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39837, + "gasUsed": 39813, } `; exports[`Check Ownership Gas gas: checks ownership after a seaport trade, one NFT 1`] = ` Object { "calldataByteLength": 2180, - "gasUsed": 185340, + "gasUsed": 185298, } `; exports[`Check Ownership Gas gas: checks ownership after a seaport trade, two NFTs 1`] = ` Object { "calldataByteLength": 5028, - "gasUsed": 294767, + "gasUsed": 294713, } `; exports[`Check Ownership Gas gas: does not check ownership after a seaport trade, one NFT 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 182447, + "gasUsed": 182423, } `; exports[`Check Ownership Gas gas: just ownership check 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 32519, + "gasUsed": 32501, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap index 00882e9e..38dcfd92 100644 --- a/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/CryptoPunk.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`Cryptopunks purchases 1 cryptopunk gas 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 109576, + "gasUsed": 109562, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Element.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Element.gas.test.ts.snap index e7e41b73..dd6e375b 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Element.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Element.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`Element Market gas tests gas: purchases open order 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 123947, + "gasUsed": 123929, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Foundation.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Foundation.gas.test.ts.snap index 43748aee..3092237f 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Foundation.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Foundation.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`Foundation Gas Tests Buy a mental worlds NFT from Foundation gas: token id 32 of mental worlds 1`] = ` Object { "calldataByteLength": 612, - "gasUsed": 182987, + "gasUsed": 182972, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/NFT20.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFT20.gas.test.ts.snap index 7854cf4a..a442474b 100644 --- a/test/integration-tests/gas-tests/__snapshots__/NFT20.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/NFT20.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`NFT20 Buy 3 alphabetties from NFT20 gas: purchases token ids 129, 193, 278 of Alphabetties 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 392225, + "gasUsed": 392207, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap index 7202d1a2..12905902 100644 --- a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`NFTX Gas Tests gas: buyAndRedeem w/ specific selection 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 574873, + "gasUsed": 574855, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 8835d99b..4cbb3552 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -3,55 +3,55 @@ exports[`Payments Gas Tests Individual Command Tests gas: APPROVE_ERC20 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 53851, + "gasUsed": 53827, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39247, + "gasUsed": 39223, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67949, + "gasUsed": 67901, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38260, + "gasUsed": 38236, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33833, + "gasUsed": 33806, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46768, + "gasUsed": 46744, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 53154, + "gasUsed": 53082, } `; exports[`Payments Gas Tests Individual Command Tests gas: WRAP_ETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 57425, + "gasUsed": 57406, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap index 992ad7dc..fdf6e45e 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`Seaport v1.4 Gas Tests ERC20 -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2532, - "gasUsed": 237147, + "gasUsed": 237051, } `; exports[`Seaport v1.4 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 182447, + "gasUsed": 182423, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap index 6df4f12c..285a5112 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152100, + "gasUsed": 152082, } `; exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAvailableAdvancedOrders 2 orders 1`] = ` Object { "calldataByteLength": 4644, - "gasUsed": 288379, + "gasUsed": 288361, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap index 5e57a015..b02fe878 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap @@ -3,20 +3,20 @@ exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases NFTs with FRAX ERC20 token already approved 1`] = ` Object { "calldataByteLength": 1380, - "gasUsed": 289932, + "gasUsed": 289866, } `; exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases tokens 2402, 2509 of Based Ghoul with FRAX ERC20 token 1`] = ` Object { "calldataByteLength": 1508, - "gasUsed": 311561, + "gasUsed": 311471, } `; exports[`Sudoswap Gas Tests ETH -> NFT gas: purchases token ids 173, 239, 240 of Sudolets 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 201657, + "gasUsed": 201639, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 3625e860..76bfe021 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,105 +3,105 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271482, + "gasUsed": 271297, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247204, + "gasUsed": 247009, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247204, + "gasUsed": 247009, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271482, + "gasUsed": 271297, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 195769, + "gasUsed": 195646, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 188565, + "gasUsed": 188442, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit 1`] = ` Object { "calldataByteLength": 1540, - "gasUsed": 303823, + "gasUsed": 303583, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = ` Object { "calldataByteLength": 1220, - "gasUsed": 311897, + "gasUsed": 311665, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from batch 1`] = ` Object { "calldataByteLength": 1284, - "gasUsed": 313950, + "gasUsed": 313742, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, without explicit permit 1`] = ` Object { "calldataByteLength": 900, - "gasUsed": 308325, + "gasUsed": 308141, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178746, + "gasUsed": 178600, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178521, + "gasUsed": 178375, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 197145, + "gasUsed": 197006, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 186574, + "gasUsed": 186428, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 1124, - "gasUsed": 199252, + "gasUsed": 199004, } `; @@ -143,98 +143,98 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn trade, where an output fee is taken 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 128411, + "gasUsed": 128298, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108605, + "gasUsed": 108540, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 245897, + "gasUsed": 245778, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops, no deadline 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 245639, + "gasUsed": 245520, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 178941, + "gasUsed": 178863, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops, MSG_SENDER flag 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 178941, + "gasUsed": 178863, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108580, + "gasUsed": 108551, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 250441, + "gasUsed": 250378, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 181236, + "gasUsed": 181168, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 124824, + "gasUsed": 124735, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 129996, + "gasUsed": 129963, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 137997, + "gasUsed": 137940, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 108516, + "gasUsed": 108395, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127183, + "gasUsed": 127106, } `; @@ -283,69 +283,69 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 116522, + "gasUsed": 116464, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 274486, + "gasUsed": 274334, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 191170, + "gasUsed": 191042, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 118115, + "gasUsed": 117985, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 419724, + "gasUsed": 419511, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 313884, + "gasUsed": 313710, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 132789, + "gasUsed": 132707, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 134454, + "gasUsed": 134300, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 241757, + "gasUsed": 241629, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 128950, + "gasUsed": 128777, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index c68da95f..59c6e53b 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,24 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17545`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17416`; exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2468, - "gasUsed": 252271, + "gasUsed": 252244, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152100, + "gasUsed": 152082, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: WETH --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2308, - "gasUsed": 186879, + "gasUsed": 186814, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index d460be3d..40c77650 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1155468`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1154612`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1189850`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1188946`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3219364`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3216940`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3372785`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3370145`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4302988`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4299876`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4506762`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4503362`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `530064`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529688`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530382`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530006`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309483`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309331`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309419`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309267`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; diff --git a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap index 724d5967..4f364da0 100644 --- a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`X2Y2 ERC-721 purchase gas: purchases 1 ERC-721 on X2Y2 1`] = ` Object { "calldataByteLength": 2212, - "gasUsed": 210138, + "gasUsed": 210123, } `; exports[`X2Y2 ERC-1155 purchase gas: purchases 1 ERC-1155 on X2Y2 1`] = ` Object { "calldataByteLength": 2276, - "gasUsed": 211195, + "gasUsed": 211180, } `; From bc568acecc6680d7f8c0179f1db97f64f9d03428 Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Sun, 7 May 2023 06:39:14 -0700 Subject: [PATCH 07/10] Uncheck loop index --- .gas-snapshot | 45 ------------- contracts/modules/Permit2Payments.sol | 5 +- .../modules/uniswap/v2/UniswapV2Library.sol | 12 ++-- .../__snapshots__/NFTX.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 12 ++-- .../SeaportV1_4.gas.test.ts.snap | 2 +- .../SeaportV1_5.gas.test.ts.snap | 2 +- .../__snapshots__/Sudoswap.gas.test.ts.snap | 4 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 64 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 8 +-- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 +++--- .../__snapshots__/X2Y2.gas.test.ts.snap | 4 +- 12 files changed, 70 insertions(+), 110 deletions(-) delete mode 100644 .gas-snapshot diff --git a/.gas-snapshot b/.gas-snapshot deleted file mode 100644 index 4ea7134f..00000000 --- a/.gas-snapshot +++ /dev/null @@ -1,45 +0,0 @@ -UniversalRouterTest:testCallModule() (gas: 6140) -UniversalRouterTest:testSupportsInterface() (gas: 8798) -UniversalRouterTest:testSweepERC1155() (gas: 62829) -UniversalRouterTest:testSweepERC1155InsufficientOutput() (gas: 52550) -UniversalRouterTest:testSweepERC1155NotFullAmount() (gas: 62840) -UniversalRouterTest:testSweepETH() (gas: 52960) -UniversalRouterTest:testSweepETHInsufficientOutput() (gas: 69123) -UniversalRouterTest:testSweepToken() (gas: 73822) -UniversalRouterTest:testSweepTokenInsufficientOutput() (gas: 74095) -V2DaiWeth:testExactInput0For1() (gas: 99728) -V2DaiWeth:testExactInput0For1FromRouter() (gas: 218575) -V2DaiWeth:testExactInput1For0() (gas: 99819) -V2DaiWeth:testExactInput1For0FromRouter() (gas: 218681) -V2DaiWeth:testExactOutput0For1() (gas: 98863) -V2DaiWeth:testExactOutput0For1FromRouter() (gas: 217983) -V2DaiWeth:testExactOutput1For0() (gas: 99122) -V2DaiWeth:testExactOutput1For0FromRouter() (gas: 218163) -V2MockMock:testExactInput0For1() (gas: 94968) -V2MockMock:testExactInput0For1FromRouter() (gas: 214791) -V2MockMock:testExactInput1For0() (gas: 94946) -V2MockMock:testExactInput1For0FromRouter() (gas: 214843) -V2MockMock:testExactOutput0For1() (gas: 93854) -V2MockMock:testExactOutput0For1FromRouter() (gas: 213991) -V2MockMock:testExactOutput1For0() (gas: 93853) -V2MockMock:testExactOutput1For0FromRouter() (gas: 214008) -V2MockWeth:testExactInput0For1() (gas: 91807) -V2MockWeth:testExactInput0For1FromRouter() (gas: 212236) -V2MockWeth:testExactInput1For0() (gas: 91967) -V2MockWeth:testExactInput1For0FromRouter() (gas: 212462) -V2MockWeth:testExactOutput0For1() (gas: 90693) -V2MockWeth:testExactOutput0For1FromRouter() (gas: 211436) -V2MockWeth:testExactOutput1For0() (gas: 91270) -V2MockWeth:testExactOutput1For0FromRouter() (gas: 211944) -V2WethApe:testExactInput0For1() (gas: 104526) -V2WethApe:testExactInput0For1FromRouter() (gas: 218292) -V2WethApe:testExactInput1For0() (gas: 99325) -V2WethApe:testExactInput1For0FromRouter() (gas: 218322) -V2WethApe:testExactOutput0For1() (gas: 103797) -V2WethApe:testExactOutput0For1FromRouter() (gas: 217809) -V2WethApe:testExactOutput1For0() (gas: 98572) -V2WethApe:testExactOutput1For0FromRouter() (gas: 217759) -V3MockMock:testExactInput0For1() (gas: 123143) -V3MockMock:testExactInput1For0() (gas: 130334) -V3MockMock:testExactOutput0For1() (gas: 125474) -V3MockMock:testExactOutput1For0() (gas: 131651) \ No newline at end of file diff --git a/contracts/modules/Permit2Payments.sol b/contracts/modules/Permit2Payments.sol index c547909f..cc5dfe40 100644 --- a/contracts/modules/Permit2Payments.sol +++ b/contracts/modules/Permit2Payments.sol @@ -28,8 +28,11 @@ abstract contract Permit2Payments is Payments { internal { uint256 batchLength = batchDetails.length; - for (uint256 i = 0; i < batchLength; ++i) { + for (uint256 i = 0; i < batchLength;) { if (batchDetails[i].from != owner) revert FromAddressIsNotOwner(); + unchecked { + ++i; + } } PERMIT2.transferFrom(batchDetails); } diff --git a/contracts/modules/uniswap/v2/UniswapV2Library.sol b/contracts/modules/uniswap/v2/UniswapV2Library.sol index 5162364b..225d6b2c 100644 --- a/contracts/modules/uniswap/v2/UniswapV2Library.sol +++ b/contracts/modules/uniswap/v2/UniswapV2Library.sol @@ -137,12 +137,14 @@ library UniswapV2Library { { if (path.length < 2) revert InvalidPath(); amount = amountOut; - for (uint256 i = path.length - 1; i > 0; i--) { - uint256 reserveIn; - uint256 reserveOut; + unchecked { + for (uint256 i = path.length - 1; i > 0; --i) { + uint256 reserveIn; + uint256 reserveOut; - (pair, reserveIn, reserveOut) = pairAndReservesFor(factory, initCodeHash, path[i - 1], path[i]); - amount = getAmountIn(amount, reserveIn, reserveOut); + (pair, reserveIn, reserveOut) = pairAndReservesFor(factory, initCodeHash, path[i - 1], path[i]); + amount = getAmountIn(amount, reserveIn, reserveOut); + } } } } diff --git a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap index 12905902..4b7b2e52 100644 --- a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`NFTX Gas Tests gas: buyAndRedeem w/ specific selection 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 574855, + "gasUsed": 574852, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 4cbb3552..a4ebba62 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -10,42 +10,42 @@ Object { exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39223, + "gasUsed": 39211, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67901, + "gasUsed": 67877, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38236, + "gasUsed": 38224, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33806, + "gasUsed": 33794, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46744, + "gasUsed": 46741, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 53082, + "gasUsed": 53055, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap index fdf6e45e..2e8c01cd 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.4 Gas Tests ERC20 -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2532, - "gasUsed": 237051, + "gasUsed": 237039, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap index 285a5112..d885ccdb 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152082, + "gasUsed": 152079, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap index b02fe878..c8a195e1 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap @@ -3,14 +3,14 @@ exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases NFTs with FRAX ERC20 token already approved 1`] = ` Object { "calldataByteLength": 1380, - "gasUsed": 289866, + "gasUsed": 289854, } `; exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases tokens 2402, 2509 of Based Ghoul with FRAX ERC20 token 1`] = ` Object { "calldataByteLength": 1508, - "gasUsed": 311471, + "gasUsed": 311459, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 76bfe021..d4be706f 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,63 +3,63 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271297, + "gasUsed": 271325, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247009, + "gasUsed": 247037, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247009, + "gasUsed": 247037, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271297, + "gasUsed": 271325, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 195646, + "gasUsed": 195666, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 188442, + "gasUsed": 188462, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit 1`] = ` Object { "calldataByteLength": 1540, - "gasUsed": 303583, + "gasUsed": 303571, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = ` Object { "calldataByteLength": 1220, - "gasUsed": 311665, + "gasUsed": 311641, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from batch 1`] = ` Object { "calldataByteLength": 1284, - "gasUsed": 313742, + "gasUsed": 313698, } `; @@ -73,35 +73,35 @@ Object { exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178600, + "gasUsed": 178608, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178375, + "gasUsed": 178383, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 197006, + "gasUsed": 196989, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 186428, + "gasUsed": 186445, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 1124, - "gasUsed": 199004, + "gasUsed": 199012, } `; @@ -143,7 +143,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn trade, where an output fee is taken 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 128298, + "gasUsed": 128274, } `; @@ -185,42 +185,42 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108551, + "gasUsed": 108476, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 250378, + "gasUsed": 250203, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 181168, + "gasUsed": 181043, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 124735, + "gasUsed": 124732, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 129963, + "gasUsed": 129873, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 137940, + "gasUsed": 137838, } `; @@ -234,7 +234,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127106, + "gasUsed": 127028, } `; @@ -283,69 +283,69 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 116464, + "gasUsed": 116484, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 274334, + "gasUsed": 274394, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 191042, + "gasUsed": 191082, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 117985, + "gasUsed": 118046, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 419511, + "gasUsed": 419298, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 313710, + "gasUsed": 313634, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 132707, + "gasUsed": 132724, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 134300, + "gasUsed": 134358, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 241629, + "gasUsed": 241649, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 128777, + "gasUsed": 128835, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index 59c6e53b..a11eb4c6 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,24 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17416`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17725`; exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2468, - "gasUsed": 252244, + "gasUsed": 252163, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152082, + "gasUsed": 152079, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: WETH --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2308, - "gasUsed": 186814, + "gasUsed": 186796, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index 40c77650..f0b27d27 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1154612`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1154812`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1188946`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1189146`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3216940`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3217540`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3370145`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3370745`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4299876`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4300676`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4503362`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4504162`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529688`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529768`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530006`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530086`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309331`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309371`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309267`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309307`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; diff --git a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap index 4f364da0..7fead112 100644 --- a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`X2Y2 ERC-721 purchase gas: purchases 1 ERC-721 on X2Y2 1`] = ` Object { "calldataByteLength": 2212, - "gasUsed": 210123, + "gasUsed": 210120, } `; exports[`X2Y2 ERC-1155 purchase gas: purchases 1 ERC-1155 on X2Y2 1`] = ` Object { "calldataByteLength": 2276, - "gasUsed": 211180, + "gasUsed": 211177, } `; From 65cad3d069a7553e967cb153ac610a47669761d5 Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Mon, 8 May 2023 19:48:55 -0700 Subject: [PATCH 08/10] Refactor to address comments --- .../modules/uniswap/{v2 => }/TernaryLib.sol | 20 +++++-- .../modules/uniswap/v2/UniswapV2Library.sol | 8 ++- contracts/modules/uniswap/v2/V2SwapRouter.sol | 6 +- contracts/modules/uniswap/v3/V3SwapRouter.sol | 45 ++++++--------- .../__snapshots__/NFTX.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 12 ++-- .../SeaportV1_4.gas.test.ts.snap | 2 +- .../SeaportV1_5.gas.test.ts.snap | 2 +- .../__snapshots__/Sudoswap.gas.test.ts.snap | 4 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 56 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 8 +-- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 +++---- .../__snapshots__/X2Y2.gas.test.ts.snap | 4 +- 13 files changed, 95 insertions(+), 94 deletions(-) rename contracts/modules/uniswap/{v2 => }/TernaryLib.sol (64%) diff --git a/contracts/modules/uniswap/v2/TernaryLib.sol b/contracts/modules/uniswap/TernaryLib.sol similarity index 64% rename from contracts/modules/uniswap/v2/TernaryLib.sol rename to contracts/modules/uniswap/TernaryLib.sol index 991eef91..1fb7eb80 100644 --- a/contracts/modules/uniswap/v2/TernaryLib.sol +++ b/contracts/modules/uniswap/TernaryLib.sol @@ -10,10 +10,18 @@ library TernaryLib { } } - /// @notice Sorts two uint256 in ascending order - /// @dev Equivalent to: `a < b ? (a, b) : (b, a)` - function sort2(uint256 a, uint256 b) internal pure returns (uint256, uint256) { - return swapIf(b < a, a, b); + /// @notice Equivalent to the ternary operator: `condition ? a : b` + function ternary(bool condition, int256 a, int256 b) internal pure returns (int256 res) { + assembly { + res := xor(b, mul(xor(a, b), condition)) + } + } + + /// @notice Equivalent to the ternary operator: `condition ? a : b` + function ternary(bool condition, address a, address b) internal pure returns (address res) { + assembly { + res := xor(b, mul(xor(a, b), condition)) + } } /// @notice Sorts two tokens to return token0 and token1 @@ -29,9 +37,9 @@ library TernaryLib { } } - /// @notice Swaps two uint256 if `condition` is true + /// @notice Switches two uint256 if `condition` is true /// @dev Equivalent to: `condition ? (b, a) : (a, b)` - function swapIf(bool condition, uint256 a, uint256 b) internal pure returns (uint256, uint256) { + function switchIf(bool condition, uint256 a, uint256 b) internal pure returns (uint256, uint256) { assembly { let diff := mul(xor(a, b), condition) a := xor(a, diff) diff --git a/contracts/modules/uniswap/v2/UniswapV2Library.sol b/contracts/modules/uniswap/v2/UniswapV2Library.sol index 225d6b2c..318e5a4b 100644 --- a/contracts/modules/uniswap/v2/UniswapV2Library.sol +++ b/contracts/modules/uniswap/v2/UniswapV2Library.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.0; import {IUniswapV2Pair} from '@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol'; -import {TernaryLib} from './TernaryLib.sol'; +import {TernaryLib} from '../TernaryLib.sol'; /// @title Uniswap v2 Helper Library /// @notice Calculates the recipient address for a command @@ -53,10 +53,12 @@ library UniswapV2Library { pure returns (address pair) { + // accomplishes the following: + // address(keccak256(abi.encodePacked(hex'ff', factory, keccak256(abi.encodePacked(token0, token1)), initCodeHash))) assembly ("memory-safe") { // Get the free memory pointer. let fmp := mload(0x40) - // keccak256(abi.encodePacked(token0, token1)) + // pairHash = keccak256(abi.encodePacked(token0, token1)) mstore(add(fmp, 0x14), token1) mstore(fmp, token0) let pairHash := keccak256(add(fmp, 0x0c), 0x28) @@ -87,7 +89,7 @@ library UniswapV2Library { address token0; (pair, token0) = pairAndToken0For(factory, initCodeHash, tokenA, tokenB); (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves(); - (reserveA, reserveB) = TernaryLib.swapIf(tokenA == token0, reserve1, reserve0); + (reserveA, reserveB) = TernaryLib.switchIf(tokenA == token0, reserve1, reserve0); } /// @notice Given an input asset amount returns the maximum output amount of the other asset diff --git a/contracts/modules/uniswap/v2/V2SwapRouter.sol b/contracts/modules/uniswap/v2/V2SwapRouter.sol index 8a9e51e0..e36f489d 100644 --- a/contracts/modules/uniswap/v2/V2SwapRouter.sol +++ b/contracts/modules/uniswap/v2/V2SwapRouter.sol @@ -8,7 +8,7 @@ import {Payments} from '../../Payments.sol'; import {Permit2Payments} from '../../Permit2Payments.sol'; import {Constants} from '../../../libraries/Constants.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; -import {TernaryLib} from './TernaryLib.sol'; +import {TernaryLib} from '../TernaryLib.sol'; /// @title Router for Uniswap v2 Trades abstract contract V2SwapRouter is RouterImmutables, Permit2Payments { @@ -31,10 +31,10 @@ abstract contract V2SwapRouter is RouterImmutables, Permit2Payments { { (uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pair).getReserves(); (uint256 reserveInput, uint256 reserveOutput) = - TernaryLib.swapIf(input == token0, reserve1, reserve0); + TernaryLib.switchIf(input == token0, reserve1, reserve0); uint256 amountInput = ERC20(input).balanceOf(pair) - reserveInput; uint256 amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput); - (amount0Out, amount1Out) = TernaryLib.swapIf(input == token0, amountOutput, 0); + (amount0Out, amount1Out) = TernaryLib.switchIf(input == token0, amountOutput, 0); } address nextPair; (nextPair, token0) = i < penultimatePairIndex diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index df7dd176..643ba942 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -11,12 +11,14 @@ import {RouterImmutables} from '../../../base/RouterImmutables.sol'; import {Permit2Payments} from '../../Permit2Payments.sol'; import {Constants} from '../../../libraries/Constants.sol'; import {ERC20} from 'solmate/src/tokens/ERC20.sol'; +import {TernaryLib} from '../TernaryLib.sol'; /// @title Router for Uniswap v3 Trades abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3SwapCallback { using V3Path for bytes; using BytesLib for bytes; using SafeCast for uint256; + using TernaryLib for bool; error V3InvalidSwap(); error V3TooLittleReceived(); @@ -37,6 +39,12 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; + /// @dev Literal numbers used in sqrtPriceLimitX96 = zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1 + /// = (MAX_SQRT_RATIO - 1) ^ ((MIN_SQRT_RATIO + 1 ^ MAX_SQRT_RATIO - 1) * zeroForOne) + uint160 internal constant MAX_SQRT_RATIO_LESS_ONE = 1461446703485210103287273052203988822378723970341; + /// @dev MIN_SQRT_RATIO + 1 ^ MAX_SQRT_RATIO - 1 + uint160 internal constant XOR_SQRT_RATIO = 1461446703485210103287273052203988822383019096961; + function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external { if (amount0Delta <= 0 && amount1Delta <= 0) revert V3InvalidSwap(); // swaps entirely within 0-liquidity regions are not supported (, address payer) = abi.decode(data, (bytes, address)); @@ -90,25 +98,18 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S while (true) { bool hasMultiplePools = path.hasMultiplePools(); - // for intermediate swaps, this contract custodies - address _recipient; - assembly { - // hasMultiplePools ? address(this) : recipient - _recipient := xor(recipient, mul(xor(address(), recipient), hasMultiplePools)) - } // the outputs of prior swaps become the inputs to subsequent ones (int256 amount0Delta, int256 amount1Delta, bool zeroForOne) = _swap( amountIn.toInt256(), - _recipient, + hasMultiplePools.ternary(address(this), recipient), // for intermediate swaps, this contract custodies path.getFirstPool(), // only the first pool is needed payer, // for intermediate swaps, this contract custodies true ); - // amountIn = uint256(-(zeroForOne ? amount1Delta : amount0Delta)) - assembly { - // no need to check for underflow here as it will be caught in `toInt256()` - amountIn := sub(0, xor(amount0Delta, mul(xor(amount1Delta, amount0Delta), zeroForOne))) + unchecked { + // no need to check for overflow here as it will be caught in `toInt256()` + amountIn = uint256(-zeroForOne.ternary(amount1Delta, amount0Delta)); } // decide whether to continue or terminate @@ -141,15 +142,12 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S (int256 amount0Delta, int256 amount1Delta, bool zeroForOne) = _swap(-amountOut.toInt256(), recipient, path, payer, false); - uint256 amountOutReceived; - assembly { - // amountOutReceived = uint256(-(zeroForOne ? amount1Delta : amount0Delta)) - // no need to check for underflow - amountOutReceived := sub(0, xor(amount0Delta, mul(xor(amount1Delta, amount0Delta), zeroForOne))) + unchecked { + // no need to check for overflow + uint256 amountOutReceived = uint256(-zeroForOne.ternary(amount1Delta, amount0Delta)); + if (amountOutReceived != amountOut) revert V3InvalidAmountOut(); } - if (amountOutReceived != amountOut) revert V3InvalidAmountOut(); - maxAmountInCached = DEFAULT_MAX_AMOUNT_IN; } @@ -173,14 +171,7 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S uint160 sqrtPriceLimitX96; // Equivalent to `sqrtPriceLimitX96 = zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1` assembly { - sqrtPriceLimitX96 := - xor( - 0xfffd8963efd1fc6a506488495d951d5263988d25, // MAX_SQRT_RATIO - 1 - mul( - 0xfffd8963efd1fc6a506488495d951d53639afb81, // MAX_SQRT_RATIO - 1 ^ MIN_SQRT_RATIO + 1 - zeroForOne - ) - ) + sqrtPriceLimitX96 := xor(MAX_SQRT_RATIO_LESS_ONE, mul(XOR_SQRT_RATIO, zeroForOne)) } (amount0Delta, amount1Delta) = IUniswapV3Pool(pool).swap(recipient, zeroForOne, amount, sqrtPriceLimitX96, abi.encode(path, payer)); @@ -195,7 +186,7 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S // Hash the pool key. // Equivalent to `if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA)` let diff := mul(xor(tokenA, tokenB), lt(tokenB, tokenA)) - // poolHash = abi.encode(tokenA, tokenB, fee) + // poolHash = keccak256(abi.encode(tokenA, tokenB, fee)) mstore(fmp, xor(tokenA, diff)) mstore(add(fmp, 0x20), xor(tokenB, diff)) mstore(add(fmp, 0x40), fee) diff --git a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap index 4b7b2e52..12905902 100644 --- a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`NFTX Gas Tests gas: buyAndRedeem w/ specific selection 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 574852, + "gasUsed": 574855, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index a4ebba62..4cbb3552 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -10,42 +10,42 @@ Object { exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39211, + "gasUsed": 39223, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67877, + "gasUsed": 67901, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38224, + "gasUsed": 38236, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33794, + "gasUsed": 33806, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46741, + "gasUsed": 46744, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 53055, + "gasUsed": 53082, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap index 2e8c01cd..fdf6e45e 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.4 Gas Tests ERC20 -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2532, - "gasUsed": 237039, + "gasUsed": 237051, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap index d885ccdb..285a5112 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152079, + "gasUsed": 152082, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap index c8a195e1..b02fe878 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap @@ -3,14 +3,14 @@ exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases NFTs with FRAX ERC20 token already approved 1`] = ` Object { "calldataByteLength": 1380, - "gasUsed": 289854, + "gasUsed": 289866, } `; exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases tokens 2402, 2509 of Based Ghoul with FRAX ERC20 token 1`] = ` Object { "calldataByteLength": 1508, - "gasUsed": 311459, + "gasUsed": 311471, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index d4be706f..5172e60c 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,56 +3,56 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271325, + "gasUsed": 271297, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247037, + "gasUsed": 247009, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247037, + "gasUsed": 247009, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271325, + "gasUsed": 271297, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 195666, + "gasUsed": 195646, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 188462, + "gasUsed": 188442, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit 1`] = ` Object { "calldataByteLength": 1540, - "gasUsed": 303571, + "gasUsed": 303583, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = ` Object { "calldataByteLength": 1220, - "gasUsed": 311641, + "gasUsed": 311665, } `; @@ -73,35 +73,35 @@ Object { exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178608, + "gasUsed": 178600, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178383, + "gasUsed": 178375, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 196989, + "gasUsed": 196931, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 186445, + "gasUsed": 186428, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 1124, - "gasUsed": 199012, + "gasUsed": 199004, } `; @@ -143,7 +143,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn trade, where an output fee is taken 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 128274, + "gasUsed": 128298, } `; @@ -206,21 +206,21 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 124732, + "gasUsed": 124735, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 129873, + "gasUsed": 129888, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 137838, + "gasUsed": 137865, } `; @@ -234,7 +234,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127028, + "gasUsed": 127031, } `; @@ -283,69 +283,69 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 116484, + "gasUsed": 116464, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 274394, + "gasUsed": 274334, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 191082, + "gasUsed": 191042, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 118046, + "gasUsed": 117985, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 419298, + "gasUsed": 419511, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 313634, + "gasUsed": 313710, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 132724, + "gasUsed": 132707, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 134358, + "gasUsed": 134300, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 241649, + "gasUsed": 241629, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 128835, + "gasUsed": 128777, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index a11eb4c6..eb14e353 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,24 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17725`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17353`; exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2468, - "gasUsed": 252163, + "gasUsed": 252169, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152079, + "gasUsed": 152082, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: WETH --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2308, - "gasUsed": 186796, + "gasUsed": 186814, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index f0b27d27..40c77650 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1154812`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1154612`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1189146`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1188946`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3217540`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3216940`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3370745`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3370145`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4300676`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4299876`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4504162`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4503362`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529768`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529688`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530086`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530006`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309371`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309331`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309307`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309267`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; diff --git a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap index 7fead112..4f364da0 100644 --- a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`X2Y2 ERC-721 purchase gas: purchases 1 ERC-721 on X2Y2 1`] = ` Object { "calldataByteLength": 2212, - "gasUsed": 210120, + "gasUsed": 210123, } `; exports[`X2Y2 ERC-1155 purchase gas: purchases 1 ERC-1155 on X2Y2 1`] = ` Object { "calldataByteLength": 2276, - "gasUsed": 211177, + "gasUsed": 211180, } `; From 68baa9c823e76cfc6e7e68f2797a9a8b68a7bedf Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Wed, 10 May 2023 14:27:16 -0700 Subject: [PATCH 09/10] Improve readability --- contracts/modules/uniswap/v3/V3SwapRouter.sol | 22 +++----- .../__snapshots__/NFTX.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 12 ++-- .../SeaportV1_4.gas.test.ts.snap | 2 +- .../SeaportV1_5.gas.test.ts.snap | 2 +- .../__snapshots__/Sudoswap.gas.test.ts.snap | 4 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 56 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 8 +-- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 +++---- .../__snapshots__/X2Y2.gas.test.ts.snap | 4 +- 10 files changed, 64 insertions(+), 68 deletions(-) diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index 643ba942..9c7ebd5f 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -39,12 +39,6 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; - /// @dev Literal numbers used in sqrtPriceLimitX96 = zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1 - /// = (MAX_SQRT_RATIO - 1) ^ ((MIN_SQRT_RATIO + 1 ^ MAX_SQRT_RATIO - 1) * zeroForOne) - uint160 internal constant MAX_SQRT_RATIO_LESS_ONE = 1461446703485210103287273052203988822378723970341; - /// @dev MIN_SQRT_RATIO + 1 ^ MAX_SQRT_RATIO - 1 - uint160 internal constant XOR_SQRT_RATIO = 1461446703485210103287273052203988822383019096961; - function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external { if (amount0Delta <= 0 && amount1Delta <= 0) revert V3InvalidSwap(); // swaps entirely within 0-liquidity regions are not supported (, address payer) = abi.decode(data, (bytes, address)); @@ -168,18 +162,20 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S } } - uint160 sqrtPriceLimitX96; - // Equivalent to `sqrtPriceLimitX96 = zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1` - assembly { - sqrtPriceLimitX96 := xor(MAX_SQRT_RATIO_LESS_ONE, mul(XOR_SQRT_RATIO, zeroForOne)) - } - (amount0Delta, amount1Delta) = - IUniswapV3Pool(pool).swap(recipient, zeroForOne, amount, sqrtPriceLimitX96, abi.encode(path, payer)); + (amount0Delta, amount1Delta) = IUniswapV3Pool(pool).swap( + recipient, + zeroForOne, + amount, + uint160(zeroForOne.ternary(MIN_SQRT_RATIO + 1, MAX_SQRT_RATIO - 1)), + abi.encode(path, payer) + ); } function computePoolAddress(address tokenA, address tokenB, uint24 fee) private view returns (address pool) { address factory = UNISWAP_V3_FACTORY; bytes32 initCodeHash = UNISWAP_V3_POOL_INIT_CODE_HASH; + // accomplishes the following: + // address(keccak256(abi.encodePacked(hex'ff', factory, keccak256(abi.encode(tokenA, tokenB, fee)), initCodeHash))) assembly ("memory-safe") { // Get the free memory pointer. let fmp := mload(0x40) diff --git a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap index 12905902..4b7b2e52 100644 --- a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`NFTX Gas Tests gas: buyAndRedeem w/ specific selection 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 574855, + "gasUsed": 574852, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index 4cbb3552..a4ebba62 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -10,42 +10,42 @@ Object { exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39223, + "gasUsed": 39211, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67901, + "gasUsed": 67877, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38236, + "gasUsed": 38224, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33806, + "gasUsed": 33794, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46744, + "gasUsed": 46741, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 53082, + "gasUsed": 53055, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap index fdf6e45e..2e8c01cd 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.4 Gas Tests ERC20 -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2532, - "gasUsed": 237051, + "gasUsed": 237039, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap index 285a5112..d885ccdb 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152082, + "gasUsed": 152079, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap index b02fe878..c8a195e1 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap @@ -3,14 +3,14 @@ exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases NFTs with FRAX ERC20 token already approved 1`] = ` Object { "calldataByteLength": 1380, - "gasUsed": 289866, + "gasUsed": 289854, } `; exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases tokens 2402, 2509 of Based Ghoul with FRAX ERC20 token 1`] = ` Object { "calldataByteLength": 1508, - "gasUsed": 311471, + "gasUsed": 311459, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index 5172e60c..d4be706f 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,56 +3,56 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271297, + "gasUsed": 271325, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247009, + "gasUsed": 247037, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247009, + "gasUsed": 247037, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271297, + "gasUsed": 271325, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 195646, + "gasUsed": 195666, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 188442, + "gasUsed": 188462, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit 1`] = ` Object { "calldataByteLength": 1540, - "gasUsed": 303583, + "gasUsed": 303571, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = ` Object { "calldataByteLength": 1220, - "gasUsed": 311665, + "gasUsed": 311641, } `; @@ -73,35 +73,35 @@ Object { exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178600, + "gasUsed": 178608, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178375, + "gasUsed": 178383, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 196931, + "gasUsed": 196989, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 186428, + "gasUsed": 186445, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 1124, - "gasUsed": 199004, + "gasUsed": 199012, } `; @@ -143,7 +143,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn trade, where an output fee is taken 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 128298, + "gasUsed": 128274, } `; @@ -206,21 +206,21 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 124735, + "gasUsed": 124732, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 129888, + "gasUsed": 129873, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 137865, + "gasUsed": 137838, } `; @@ -234,7 +234,7 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127031, + "gasUsed": 127028, } `; @@ -283,69 +283,69 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 116464, + "gasUsed": 116484, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 274334, + "gasUsed": 274394, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 191042, + "gasUsed": 191082, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 117985, + "gasUsed": 118046, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 419511, + "gasUsed": 419298, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 313710, + "gasUsed": 313634, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 132707, + "gasUsed": 132724, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 134300, + "gasUsed": 134358, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 241629, + "gasUsed": 241649, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 128777, + "gasUsed": 128835, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index eb14e353..a11eb4c6 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,24 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17353`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17725`; exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2468, - "gasUsed": 252169, + "gasUsed": 252163, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152082, + "gasUsed": 152079, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: WETH --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2308, - "gasUsed": 186814, + "gasUsed": 186796, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index 40c77650..f0b27d27 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1154612`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1154812`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1188946`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1189146`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3216940`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3217540`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3370145`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3370745`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4299876`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4300676`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4503362`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4504162`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529688`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529768`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530006`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530086`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309331`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309371`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309267`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309307`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; diff --git a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap index 4f364da0..7fead112 100644 --- a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`X2Y2 ERC-721 purchase gas: purchases 1 ERC-721 on X2Y2 1`] = ` Object { "calldataByteLength": 2212, - "gasUsed": 210123, + "gasUsed": 210120, } `; exports[`X2Y2 ERC-1155 purchase gas: purchases 1 ERC-1155 on X2Y2 1`] = ` Object { "calldataByteLength": 2276, - "gasUsed": 211180, + "gasUsed": 211177, } `; From 1ff67ff62d79c5d75f749d87b35f905d26db64e2 Mon Sep 17 00:00:00 2001 From: Shuhui Luo Date: Mon, 5 Jun 2023 01:58:50 -0700 Subject: [PATCH 10/10] Further optimize pool address calculation --- .../modules/uniswap/v2/UniswapV2Library.sol | 21 ++--- contracts/modules/uniswap/v3/V3SwapRouter.sol | 23 +++--- .../LooksRareV2.gas.test.ts.snap | 4 +- .../__snapshots__/NFTX.gas.test.ts.snap | 2 +- .../__snapshots__/Payments.gas.test.ts.snap | 12 +-- .../SeaportV1_4.gas.test.ts.snap | 2 +- .../SeaportV1_5.gas.test.ts.snap | 2 +- .../__snapshots__/Sudoswap.gas.test.ts.snap | 4 +- .../__snapshots__/Uniswap.gas.test.ts.snap | 78 +++++++++---------- .../UniversalRouter.gas.test.ts.snap | 8 +- .../UniversalVSSwapRouter.gas.test.ts.snap | 20 ++--- .../__snapshots__/X2Y2.gas.test.ts.snap | 4 +- 12 files changed, 91 insertions(+), 89 deletions(-) diff --git a/contracts/modules/uniswap/v2/UniswapV2Library.sol b/contracts/modules/uniswap/v2/UniswapV2Library.sol index 318e5a4b..1247caf3 100644 --- a/contracts/modules/uniswap/v2/UniswapV2Library.sol +++ b/contracts/modules/uniswap/v2/UniswapV2Library.sol @@ -56,20 +56,21 @@ library UniswapV2Library { // accomplishes the following: // address(keccak256(abi.encodePacked(hex'ff', factory, keccak256(abi.encodePacked(token0, token1)), initCodeHash))) assembly ("memory-safe") { - // Get the free memory pointer. + // Cache the free memory pointer. let fmp := mload(0x40) // pairHash = keccak256(abi.encodePacked(token0, token1)) - mstore(add(fmp, 0x14), token1) - mstore(fmp, token0) - let pairHash := keccak256(add(fmp, 0x0c), 0x28) + mstore(0x14, token1) + mstore(0, token0) + let pairHash := keccak256(0x0c, 0x28) // abi.encodePacked(hex'ff', factory, pairHash, initCodeHash) - mstore(fmp, factory) - fmp := add(fmp, 0x0b) - mstore8(fmp, 0xff) - mstore(add(fmp, 0x15), pairHash) - mstore(add(fmp, 0x35), initCodeHash) + // Prefix the factory address with 0xff. + mstore(0, or(factory, 0xff0000000000000000000000000000000000000000)) + mstore(0x20, pairHash) + mstore(0x40, initCodeHash) // Compute the CREATE2 pair address and clean the upper bits. - pair := and(keccak256(fmp, 0x55), 0xffffffffffffffffffffffffffffffffffffffff) + pair := and(keccak256(0x0b, 0x55), 0xffffffffffffffffffffffffffffffffffffffff) + // Restore the free memory pointer. + mstore(0x40, fmp) } } diff --git a/contracts/modules/uniswap/v3/V3SwapRouter.sol b/contracts/modules/uniswap/v3/V3SwapRouter.sol index 9c7ebd5f..7bec53dd 100644 --- a/contracts/modules/uniswap/v3/V3SwapRouter.sol +++ b/contracts/modules/uniswap/v3/V3SwapRouter.sol @@ -177,24 +177,25 @@ abstract contract V3SwapRouter is RouterImmutables, Permit2Payments, IUniswapV3S // accomplishes the following: // address(keccak256(abi.encodePacked(hex'ff', factory, keccak256(abi.encode(tokenA, tokenB, fee)), initCodeHash))) assembly ("memory-safe") { - // Get the free memory pointer. + // Cache the free memory pointer. let fmp := mload(0x40) // Hash the pool key. // Equivalent to `if (tokenA > tokenB) (tokenA, tokenB) = (tokenB, tokenA)` let diff := mul(xor(tokenA, tokenB), lt(tokenB, tokenA)) // poolHash = keccak256(abi.encode(tokenA, tokenB, fee)) - mstore(fmp, xor(tokenA, diff)) - mstore(add(fmp, 0x20), xor(tokenB, diff)) - mstore(add(fmp, 0x40), fee) - let poolHash := keccak256(fmp, 0x60) + mstore(0, xor(tokenA, diff)) + mstore(0x20, xor(tokenB, diff)) + mstore(0x40, fee) + let poolHash := keccak256(0, 0x60) // abi.encodePacked(hex'ff', factory, poolHash, initCodeHash) - mstore(fmp, factory) - fmp := add(fmp, 0x0b) - mstore8(fmp, 0xff) - mstore(add(fmp, 0x15), poolHash) - mstore(add(fmp, 0x35), initCodeHash) + // Prefix the factory address with 0xff. + mstore(0, or(factory, 0xff0000000000000000000000000000000000000000)) + mstore(0x20, poolHash) + mstore(0x40, initCodeHash) // Compute the CREATE2 pool address and clean the upper bits. - pool := and(keccak256(fmp, 0x55), 0xffffffffffffffffffffffffffffffffffffffff) + pool := and(keccak256(0x0b, 0x55), 0xffffffffffffffffffffffffffffffffffffffff) + // Restore the free memory pointer. + mstore(0x40, fmp) } } } diff --git a/test/integration-tests/gas-tests/__snapshots__/LooksRareV2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/LooksRareV2.gas.test.ts.snap index f000a1f9..2d0fb131 100644 --- a/test/integration-tests/gas-tests/__snapshots__/LooksRareV2.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/LooksRareV2.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`LooksRareV2 Gas Test Bulk Buy Buys 2 721s 1`] = ` Object { "calldataByteLength": 2884, - "gasUsed": 335502, + "gasUsed": 335484, } `; exports[`LooksRareV2 Gas Test Single Buy Buy a 721 1`] = ` Object { "calldataByteLength": 1508, - "gasUsed": 189890, + "gasUsed": 189872, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap index 4b7b2e52..12905902 100644 --- a/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/NFTX.gas.test.ts.snap @@ -3,6 +3,6 @@ exports[`NFTX Gas Tests gas: buyAndRedeem w/ specific selection 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 574852, + "gasUsed": 574855, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap index a4ebba62..4cbb3552 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Payments.gas.test.ts.snap @@ -10,42 +10,42 @@ Object { exports[`Payments Gas Tests Individual Command Tests gas: SWEEP with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 39211, + "gasUsed": 39223, } `; exports[`Payments Gas Tests Individual Command Tests gas: SWEEP_WITH_FEE 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 67877, + "gasUsed": 67901, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ERC20 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 38224, + "gasUsed": 38236, } `; exports[`Payments Gas Tests Individual Command Tests gas: TRANSFER with ETH 1`] = ` Object { "calldataByteLength": 356, - "gasUsed": 33794, + "gasUsed": 33806, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH 1`] = ` Object { "calldataByteLength": 324, - "gasUsed": 46741, + "gasUsed": 46744, } `; exports[`Payments Gas Tests Individual Command Tests gas: UNWRAP_WETH_WITH_FEE 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 53055, + "gasUsed": 53082, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap index 2e8c01cd..fdf6e45e 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_4.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.4 Gas Tests ERC20 -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2532, - "gasUsed": 237039, + "gasUsed": 237051, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap index d885ccdb..285a5112 100644 --- a/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/SeaportV1_5.gas.test.ts.snap @@ -3,7 +3,7 @@ exports[`Seaport v1.5 Gas Tests ETH -> NFT gas: fulfillAdvancedOrder 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152079, + "gasUsed": 152082, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap index c8a195e1..b02fe878 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Sudoswap.gas.test.ts.snap @@ -3,14 +3,14 @@ exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases NFTs with FRAX ERC20 token already approved 1`] = ` Object { "calldataByteLength": 1380, - "gasUsed": 289854, + "gasUsed": 289866, } `; exports[`Sudoswap Gas Tests ERC20 -> NFT gas: purchases tokens 2402, 2509 of Based Ghoul with FRAX ERC20 token 1`] = ` Object { "calldataByteLength": 1508, - "gasUsed": 311459, + "gasUsed": 311471, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap index d4be706f..cac8b9c0 100644 --- a/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/Uniswap.gas.test.ts.snap @@ -3,105 +3,105 @@ exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, both fail but the transaction succeeds 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271325, + "gasUsed": 271117, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, neither fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247037, + "gasUsed": 246829, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, second sub plan fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 247037, + "gasUsed": 246829, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Batch reverts gas: 2 sub-plans, the first fails 1`] = ` Object { "calldataByteLength": 1764, - "gasUsed": 271325, + "gasUsed": 271117, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V2, then V3 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 195666, + "gasUsed": 195538, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Interleaving routes gas: V3, then V2 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 188462, + "gasUsed": 188334, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, different input tokens, each two hop, with batch permit 1`] = ` Object { "calldataByteLength": 1540, - "gasUsed": 303571, + "gasUsed": 303445, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit 1`] = ` Object { "calldataByteLength": 1220, - "gasUsed": 311641, + "gasUsed": 311527, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, with explicit permit transfer from batch 1`] = ` Object { "calldataByteLength": 1284, - "gasUsed": 313698, + "gasUsed": 313560, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V2 different routes, each two hop, without explicit permit 1`] = ` Object { "calldataByteLength": 900, - "gasUsed": 308141, + "gasUsed": 308003, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178608, + "gasUsed": 178492, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ERC20 split V2 and V3, one hop, ADDRESS_THIS flag 1`] = ` Object { "calldataByteLength": 996, - "gasUsed": 178383, + "gasUsed": 178267, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, exactOut, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 196989, + "gasUsed": 196826, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ERC20 --> ETH split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 186445, + "gasUsed": 186320, } `; exports[`Uniswap Gas Tests Mixing V2 and V3 with Universal Router. Split routes gas: ETH --> ERC20 split V2 and V3, one hop 1`] = ` Object { "calldataByteLength": 1124, - "gasUsed": 199012, + "gasUsed": 198896, } `; @@ -143,98 +143,98 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn trade, where an output fee is taken 1`] = ` Object { "calldataByteLength": 836, - "gasUsed": 128274, + "gasUsed": 128262, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108540, + "gasUsed": 108504, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 245778, + "gasUsed": 245676, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops, no deadline 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 245520, + "gasUsed": 245418, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 178863, + "gasUsed": 178794, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops, MSG_SENDER flag 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 178863, + "gasUsed": 178794, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 108476, + "gasUsed": 108443, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 580, - "gasUsed": 250203, + "gasUsed": 250038, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 181043, + "gasUsed": 180944, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 124732, + "gasUsed": 124699, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 804, - "gasUsed": 129873, + "gasUsed": 129855, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ERC20 --> ETH gas: exactOut, with ETH fee 1`] = ` Object { "calldataByteLength": 964, - "gasUsed": 137838, + "gasUsed": 137832, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 108395, + "gasUsed": 108359, } `; exports[`Uniswap Gas Tests Trade on UniswapV2 with Universal Router. ETH --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 127028, + "gasUsed": 126998, } `; @@ -283,69 +283,69 @@ Object { exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 116484, + "gasUsed": 116392, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 274394, + "gasUsed": 274118, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactIn, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 191082, + "gasUsed": 190898, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, one hop 1`] = ` Object { "calldataByteLength": 516, - "gasUsed": 118046, + "gasUsed": 117913, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, three hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 419298, + "gasUsed": 419295, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ERC20 gas: exactOut, one trade, two hops 1`] = ` Object { "calldataByteLength": 548, - "gasUsed": 313634, + "gasUsed": 313566, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 132724, + "gasUsed": 132635, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ERC20 --> ETH gas: exactOut swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 134358, + "gasUsed": 134228, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactIn swap 1`] = ` Object { "calldataByteLength": 644, - "gasUsed": 241649, + "gasUsed": 241557, } `; exports[`Uniswap Gas Tests Trade on UniswapV3 with Universal Router. ETH --> ERC20 gas: exactOut swap 1`] = ` Object { "calldataByteLength": 772, - "gasUsed": 128835, + "gasUsed": 128705, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap index a11eb4c6..40d37155 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalRouter.gas.test.ts.snap @@ -1,24 +1,24 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17725`; +exports[`UniversalRouter Gas Tests gas: bytecode size 1`] = `17851`; exports[`UniversalRouter Gas Tests trading for NFTs gas: ERC20 --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2468, - "gasUsed": 252163, + "gasUsed": 252136, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2020, - "gasUsed": 152079, + "gasUsed": 152082, } `; exports[`UniversalRouter Gas Tests trading for NFTs gas: WETH --> ETH --> Seaport NFT 1`] = ` Object { "calldataByteLength": 2308, - "gasUsed": 186796, + "gasUsed": 186814, } `; diff --git a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap index f0b27d27..d639c0dc 100644 --- a/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/UniversalVSSwapRouter.gas.test.ts.snap @@ -7,32 +7,32 @@ Object { } `; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1154812`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Max Approval Swap 1`] = `1153820`; -exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1189146`; +exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps Permit2 Sign Per Swap 1`] = `1188154`; exports[`Uniswap UX Tests gas: Comparisons Casual Swapper - 3 swaps SwapRouter02 1`] = `1172945`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3217540`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Max Approval Swap 1`] = `3214600`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3370745`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps Permit2 Sign Per Swap 1`] = `3367805`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper - 10 swaps SwapRouter02 1`] = `3320620`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4300676`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Max Approval Swap 1`] = `4296816`; -exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4504162`; +exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions Permit2 Sign Per Swap 1`] = `4500302`; exports[`Uniswap UX Tests gas: Comparisons Frequent Swapper across 3 swap router versions - 15 swaps across 3 versions SwapRouter02 1`] = `4470036`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529768`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Max Approval Swap 1`] = `529364`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `530086`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap Permit2 Sign Per Swap 1`] = `529682`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Complex Swap SwapRouter02 1`] = `520492`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309371`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Max Approval Swap 1`] = `309187`; -exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309307`; +exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap Permit2 Sign Per Swap 1`] = `309123`; exports[`Uniswap UX Tests gas: Comparisons One Time Swapper - Simple Swap SwapRouter02 1`] = `278117`; diff --git a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap index 7fead112..4f364da0 100644 --- a/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap +++ b/test/integration-tests/gas-tests/__snapshots__/X2Y2.gas.test.ts.snap @@ -3,13 +3,13 @@ exports[`X2Y2 ERC-721 purchase gas: purchases 1 ERC-721 on X2Y2 1`] = ` Object { "calldataByteLength": 2212, - "gasUsed": 210120, + "gasUsed": 210123, } `; exports[`X2Y2 ERC-1155 purchase gas: purchases 1 ERC-1155 on X2Y2 1`] = ` Object { "calldataByteLength": 2276, - "gasUsed": 211177, + "gasUsed": 211180, } `;