From 9e5a6f39f54812c3b222fa87844d3ecc6a7fa603 Mon Sep 17 00:00:00 2001 From: ChefMist <133624774+ChefMist@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:42:50 +0800 Subject: [PATCH 1/2] feat: Add new pool initialization action and validation at PM call (#26) * feat: update universal router with new action and position call protection * test: fix test case * feat: reduce optimizer runs * cleanup: remove unused code * doc: update command comments --- ...test_v4BinSwap_ExactInSingle_NativeIn.snap | 2 +- ...actInSingle_NativeOut_RouterRecipient.snap | 2 +- ...st#test_v4BinSwap_v4InitializeBinPool.snap | 1 + ...apV4Test#test_v4BinSwap_ExactInSingle.snap | 2 +- ...4Test#test_v4BinSwap_ExactIn_MultiHop.snap | 2 +- ...Test#test_v4BinSwap_ExactIn_SingleHop.snap | 2 +- ...Test#test_v4BinSwap_ExactOut_MultiHop.snap | 2 +- ...est#test_v4BinSwap_ExactOut_SingleHop.snap | 2 +- ...Test#test_v4BinSwap_InitializeBinPool.snap | 1 + ...apV4Test#test_v4ClSwap_ExactOutSingle.snap | 2 +- ...#test_v4ClSwap_ExactInSingle_NativeIn.snap | 2 +- ...test_v4ClSwap_ExactInSingle_NativeOut.snap | 2 +- ...Test#test_v4ClSwap_v4InitializeClPool.snap | 1 + ...wapV4Test#test_v4ClSwap_ExactInSingle.snap | 2 +- ...V4Test#test_v4ClSwap_ExactIn_MultiHop.snap | 2 +- ...4Test#test_v4ClSwap_ExactIn_SingleHop.snap | 2 +- ...apV4Test#test_v4ClSwap_ExactOutSingle.snap | 2 +- ...4Test#test_v4ClSwap_ExactOut_MultiHop.snap | 2 +- ...Test#test_v4ClSwap_ExactOut_SingleHop.snap | 2 +- ...Test#test_v4ClSwap_v4InitializeClPool.snap | 1 + ...wapV2Test#test_v2Swap_exactInput0For1.snap | 2 +- ...apV2Test#test_v2Swap_exactOutput0For1.snap | 2 +- ...wapV3Test#test_v3Swap_ExactInput0For1.snap | 2 +- ...3Swap_ExactInput0For1_ContractBalance.snap | 2 +- ...3Test#test_v3Swap_exactInput_MultiHop.snap | 2 +- ...apV3Test#test_v3Swap_exactOutput0For1.snap | 2 +- ...Test#test_v3Swap_exactOutput_MultiHop.snap | 2 +- ...pTest#test_stableSwap_ExactInput0For1.snap | 2 +- ...pTest#test_stableSwap_ExactInput1For0.snap | 2 +- .../UniversalRouterBytecodeSize.snap | 2 +- .../UniversalRouterTest#test_sweep_token.snap | 2 +- ...#test_v4CLPositionmanager_Mint_Native.snap | 2 +- ...ationTest#test_v3PositionManager_burn.snap | 2 +- ..._v4BinPositionmanager_BinAddLiquidity.snap | 2 +- ...ositionmanager_BinAddLiquidity_Native.snap | 2 +- ...ionTest#test_v4CLPositionmanager_Mint.snap | 2 +- foundry.toml | 2 +- src/base/Dispatcher.sol | 33 ++++++-- src/libraries/Commands.sol | 8 +- src/modules/V3ToV4Migrator.sol | 75 +++++++++++++++++++ test/UniversalRouter.t.sol | 2 +- test/V3ToV4Migration.t.sol | 73 +++++++++++++++++- test/v4/BinNativePancakeSwapV4.t.sol | 38 ++++++++++ test/v4/BinPancakeSwapV4.t.sol | 43 ++++++++++- test/v4/CLNativePancakeSwapV4.t.sol | 38 ++++++++++ test/v4/CLPancakeSwapV4.t.sol | 44 ++++++++++- 46 files changed, 377 insertions(+), 47 deletions(-) create mode 100644 .forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool.snap create mode 100644 .forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool.snap create mode 100644 .forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap create mode 100644 .forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap diff --git a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeIn.snap b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeIn.snap index 808a24e..e7e8167 100644 --- a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeIn.snap +++ b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeIn.snap @@ -1 +1 @@ -145983 \ No newline at end of file +146503 \ No newline at end of file diff --git a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeOut_RouterRecipient.snap b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeOut_RouterRecipient.snap index 2b0ec9e..fb3f970 100644 --- a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeOut_RouterRecipient.snap +++ b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeOut_RouterRecipient.snap @@ -1 +1 @@ -123041 \ No newline at end of file +123561 \ No newline at end of file diff --git a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool.snap b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool.snap new file mode 100644 index 0000000..b77884b --- /dev/null +++ b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool.snap @@ -0,0 +1 @@ +138906 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactInSingle.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactInSingle.snap index 98e956e..878dfb2 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactInSingle.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactInSingle.snap @@ -1 +1 @@ -146945 \ No newline at end of file +147468 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_MultiHop.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_MultiHop.snap index 0fe2d22..b2616c5 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_MultiHop.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_MultiHop.snap @@ -1 +1 @@ -177946 \ No newline at end of file +178629 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_SingleHop.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_SingleHop.snap index 126d63a..84d9bed 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_SingleHop.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_SingleHop.snap @@ -1 +1 @@ -148741 \ No newline at end of file +149270 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_MultiHop.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_MultiHop.snap index 4d4f830..8eba04b 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_MultiHop.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_MultiHop.snap @@ -1 +1 @@ -181787 \ No newline at end of file +182474 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_SingleHop.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_SingleHop.snap index 1b36a65..c0a6bc6 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_SingleHop.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_SingleHop.snap @@ -1 +1 @@ -153114 \ No newline at end of file +153645 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool.snap new file mode 100644 index 0000000..c22791e --- /dev/null +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool.snap @@ -0,0 +1 @@ +159096 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap index 48a84f4..0a1d0ec 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap @@ -1 +1 @@ -151324 \ No newline at end of file +151845 \ No newline at end of file diff --git a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeIn.snap b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeIn.snap index 9d56c86..324d453 100644 --- a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeIn.snap +++ b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeIn.snap @@ -1 +1 @@ -171713 \ No newline at end of file +172182 \ No newline at end of file diff --git a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeOut.snap b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeOut.snap index 07fb784..5036e07 100644 --- a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeOut.snap +++ b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeOut.snap @@ -1 +1 @@ -173985 \ No newline at end of file +174508 \ No newline at end of file diff --git a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap new file mode 100644 index 0000000..80a30bc --- /dev/null +++ b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap @@ -0,0 +1 @@ +140212 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactInSingle.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactInSingle.snap index 23f1b5e..5ae8857 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactInSingle.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactInSingle.snap @@ -1 +1 @@ -181084 \ No newline at end of file +181622 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_MultiHop.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_MultiHop.snap index 290806a..5833d4b 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_MultiHop.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_MultiHop.snap @@ -1 +1 @@ -245737 \ No newline at end of file +246450 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_SingleHop.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_SingleHop.snap index 78e1c21..ae927f7 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_SingleHop.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_SingleHop.snap @@ -1 +1 @@ -182624 \ No newline at end of file +183168 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap index e2a5b08..b6b0011 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap @@ -1 +1 @@ -185423 \ No newline at end of file +185957 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_MultiHop.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_MultiHop.snap index 87b2b35..6b76d2a 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_MultiHop.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_MultiHop.snap @@ -1 +1 @@ -249496 \ No newline at end of file +250209 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_SingleHop.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_SingleHop.snap index e6417fd..aec40bf 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_SingleHop.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_SingleHop.snap @@ -1 +1 @@ -186956 \ No newline at end of file +187500 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap new file mode 100644 index 0000000..a91adf9 --- /dev/null +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap @@ -0,0 +1 @@ +160352 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactInput0For1.snap b/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactInput0For1.snap index dfed1e4..abb220c 100644 --- a/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactInput0For1.snap +++ b/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactInput0For1.snap @@ -1 +1 @@ -100125 \ No newline at end of file +100195 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactOutput0For1.snap b/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactOutput0For1.snap index 5f918e2..1a5e88d 100644 --- a/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactOutput0For1.snap +++ b/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactOutput0For1.snap @@ -1 +1 @@ -100754 \ No newline at end of file +100807 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1.snap index 3d7ea4e..cb85e1b 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1.snap @@ -1 +1 @@ -151862 \ No newline at end of file +151937 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1_ContractBalance.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1_ContractBalance.snap index 276ad91..6622494 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1_ContractBalance.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1_ContractBalance.snap @@ -1 +1 @@ -154767 \ No newline at end of file +154851 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactInput_MultiHop.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactInput_MultiHop.snap index 916451a..9bbe512 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactInput_MultiHop.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactInput_MultiHop.snap @@ -1 +1 @@ -243258 \ No newline at end of file +243405 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput0For1.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput0For1.snap index 0216e10..e2b9081 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput0For1.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput0For1.snap @@ -1 +1 @@ -150557 \ No newline at end of file +150632 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput_MultiHop.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput_MultiHop.snap index a3b5743..8a14517 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput_MultiHop.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput_MultiHop.snap @@ -1 +1 @@ -254163 \ No newline at end of file +254310 \ No newline at end of file diff --git a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap index d058e92..7521163 100644 --- a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap +++ b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap @@ -1 +1 @@ -193973 \ No newline at end of file +194005 \ No newline at end of file diff --git a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap index f376cc1..6cfbfcc 100644 --- a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap +++ b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap @@ -1 +1 @@ -194041 \ No newline at end of file +194073 \ No newline at end of file diff --git a/.forge-snapshots/UniversalRouterBytecodeSize.snap b/.forge-snapshots/UniversalRouterBytecodeSize.snap index 714963e..4759177 100644 --- a/.forge-snapshots/UniversalRouterBytecodeSize.snap +++ b/.forge-snapshots/UniversalRouterBytecodeSize.snap @@ -1 +1 @@ -24566 \ No newline at end of file +24411 \ No newline at end of file diff --git a/.forge-snapshots/UniversalRouterTest#test_sweep_token.snap b/.forge-snapshots/UniversalRouterTest#test_sweep_token.snap index 63f357d..9b29a51 100644 --- a/.forge-snapshots/UniversalRouterTest#test_sweep_token.snap +++ b/.forge-snapshots/UniversalRouterTest#test_sweep_token.snap @@ -1 +1 @@ -55441 \ No newline at end of file +55456 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationNativeTest#test_v4CLPositionmanager_Mint_Native.snap b/.forge-snapshots/V3ToV4MigrationNativeTest#test_v4CLPositionmanager_Mint_Native.snap index ee5ceac..863b59c 100644 --- a/.forge-snapshots/V3ToV4MigrationNativeTest#test_v4CLPositionmanager_Mint_Native.snap +++ b/.forge-snapshots/V3ToV4MigrationNativeTest#test_v4CLPositionmanager_Mint_Native.snap @@ -1 +1 @@ -558784 \ No newline at end of file +561402 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationTest#test_v3PositionManager_burn.snap b/.forge-snapshots/V3ToV4MigrationTest#test_v3PositionManager_burn.snap index 0b72187..20ab411 100644 --- a/.forge-snapshots/V3ToV4MigrationTest#test_v3PositionManager_burn.snap +++ b/.forge-snapshots/V3ToV4MigrationTest#test_v3PositionManager_burn.snap @@ -1 +1 @@ -291590 \ No newline at end of file +291611 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity.snap b/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity.snap index 85d1da3..9ac2d08 100644 --- a/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity.snap +++ b/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity.snap @@ -1 +1 @@ -594723 \ No newline at end of file +597077 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity_Native.snap b/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity_Native.snap index 846e348..df32336 100644 --- a/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity_Native.snap +++ b/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity_Native.snap @@ -1 +1 @@ -570540 \ No newline at end of file +572762 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationTest#test_v4CLPositionmanager_Mint.snap b/.forge-snapshots/V3ToV4MigrationTest#test_v4CLPositionmanager_Mint.snap index 73dfb8a..abf5365 100644 --- a/.forge-snapshots/V3ToV4MigrationTest#test_v4CLPositionmanager_Mint.snap +++ b/.forge-snapshots/V3ToV4MigrationTest#test_v4CLPositionmanager_Mint.snap @@ -1 +1 @@ -582977 \ No newline at end of file +585727 \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 1d2dc5c..701e777 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,7 +3,7 @@ src = "src" out = 'foundry-out' libs = ["lib"] via_ir = true -optimizer_runs = 30_000 +optimizer_runs = 10_000 ffi = true fs_permissions = [ { access = "read-write", path = ".forge-snapshots/" }, diff --git a/src/base/Dispatcher.sol b/src/base/Dispatcher.sol index ee8cd78..09a8e02 100755 --- a/src/base/Dispatcher.sol +++ b/src/base/Dispatcher.sol @@ -17,6 +17,9 @@ import {IERC721Permit} from "pancake-v4-periphery/src/pool-cl/interfaces/IERC721 import {ActionConstants} from "pancake-v4-periphery/src/libraries/ActionConstants.sol"; import {BaseActionsRouter} from "pancake-v4-periphery/src/base/BaseActionsRouter.sol"; import {CalldataDecoder} from "pancake-v4-periphery/src/libraries/CalldataDecoder.sol"; +import {PoolKey} from "pancake-v4-core/src/types/PoolKey.sol"; +import {ICLPoolManager} from "pancake-v4-core/src/pool-cl/interfaces/ICLPoolManager.sol"; +import {IBinPoolManager} from "pancake-v4-core/src/pool-bin/interfaces/IBinPoolManager.sol"; /// @title Decodes and Executes Commands /// @notice Called by the UniversalRouter contract to efficiently decode and execute a singular command @@ -34,8 +37,6 @@ abstract contract Dispatcher is error InvalidCommandType(uint256 commandType); error BalanceTooLow(); - error InvalidAction(bytes4 action); - error NotAuthorizedForToken(uint256 tokenId); /// @notice Executes encoded commands along with provided inputs. /// @param commands A set of concatenated commands, each 1 byte in length @@ -305,14 +306,34 @@ abstract contract Dispatcher is /// @dev ensure there's follow-up action if v3 position's removed token are sent to router contract (success, output) = address(V3_POSITION_MANAGER).call(inputs); return (success, output); + } else if (command == Commands.V4_CL_INITIALIZE_POOL) { + PoolKey calldata poolKey; + uint160 sqrtPriceX96; + assembly { + poolKey := inputs.offset + sqrtPriceX96 := calldataload(add(inputs.offset, 0xc0)) // poolKey has 6 variable, so it takes 192 space = 0xc0 + } + // remove "" hookData once we updated universal-router dependencies + (success, output) = address(clPoolManager).call( + abi.encodeCall(ICLPoolManager.initialize, (poolKey, sqrtPriceX96, "")) + ); + } else if (command == Commands.V4_BIN_INITIALIZE_POOL) { + PoolKey calldata poolKey; + uint24 activeId; + assembly { + poolKey := inputs.offset + activeId := calldataload(add(inputs.offset, 0xc0)) // poolKey has 6 variable, so it takes 192 space = 0xc0 + } + // remove "" hookData once we updated universal-router dependencies + (success, output) = address(binPoolManager).call( + abi.encodeCall(IBinPoolManager.initialize, (poolKey, activeId, "")) + ); } else if (command == Commands.V4_CL_POSITION_CALL) { - // should only call modifyLiquidities() with Actions.CL_MINT_POSITION - // do not permit or approve this contract over a v4 position or someone could use this command to decrease, burn, or transfer your position + _checkV4ClPositionManagerCall(inputs); (success, output) = address(V4_CL_POSITION_MANAGER).call{value: address(this).balance}(inputs); return (success, output); } else if (command == Commands.V4_BIN_POSITION_CALL) { - // should only call modifyLiquidities() with Actions.BIN_ADD_LIQUIDITY - // do not permit or approve this contract over a v4 position or someone could use this command to decrease, burn, or transfer your position + _checkV4BinPositionManagerCall(inputs); (success, output) = address(V4_BIN_POSITION_MANAGER).call{value: address(this).balance}(inputs); return (success, output); } else { diff --git a/src/libraries/Commands.sol b/src/libraries/Commands.sol index 948c2be..b1a266f 100755 --- a/src/libraries/Commands.sol +++ b/src/libraries/Commands.sol @@ -35,9 +35,11 @@ library Commands { uint256 constant V4_SWAP = 0x10; uint256 constant V3_POSITION_MANAGER_PERMIT = 0x11; uint256 constant V3_POSITION_MANAGER_CALL = 0x12; - uint256 constant V4_CL_POSITION_CALL = 0x13; - uint256 constant V4_BIN_POSITION_CALL = 0x14; - // COMMAND_PLACEHOLDER = 0x15 -> 0x20 + uint256 constant V4_CL_INITIALIZE_POOL = 0x13; + uint256 constant V4_BIN_INITIALIZE_POOL = 0x14; + uint256 constant V4_CL_POSITION_CALL = 0x15; + uint256 constant V4_BIN_POSITION_CALL = 0x16; + // COMMAND_PLACEHOLDER = 0x17 -> 0x20 // Command Types where 0x21<=value<=0x3f uint256 constant EXECUTE_SUB_PLAN = 0x21; diff --git a/src/modules/V3ToV4Migrator.sol b/src/modules/V3ToV4Migrator.sol index 05c6ce3..80c79dd 100644 --- a/src/modules/V3ToV4Migrator.sol +++ b/src/modules/V3ToV4Migrator.sol @@ -4,10 +4,21 @@ pragma solidity ^0.8.0; import {RouterImmutables} from "../base/RouterImmutables.sol"; import {IV3NonfungiblePositionManager} from "pancake-v4-periphery/src/interfaces/external/IV3NonfungiblePositionManager.sol"; +import {Actions} from "pancake-v4-periphery/src/libraries/Actions.sol"; +import {CalldataDecoder} from "pancake-v4-periphery/src/libraries/CalldataDecoder.sol"; +import {IPositionManager} from "pancake-v4-periphery/src/interfaces/IPositionManager.sol"; +import {console2} from "forge-std/console2.sol"; /// @title V3 to V4 Migrator /// @notice A contract that migrates liquidity from PancakeSwap V3 to V4 abstract contract V3ToV4Migrator is RouterImmutables { + using CalldataDecoder for bytes; + + error NotAuthorizedForToken(uint256 tokenId); + error InvalidAction(bytes4 action); + error OnlyMintAllowed(); + error OnlyAddLiqudityAllowed(); + /// @dev validate if an action is decreaseLiquidity, collect, or burn function isValidAction(bytes4 selector) internal pure returns (bool) { return selector == IV3NonfungiblePositionManager.decreaseLiquidity.selector @@ -21,4 +32,68 @@ abstract contract V3ToV4Migrator is RouterImmutables { return caller == owner || V3_POSITION_MANAGER.getApproved(tokenId) == caller || V3_POSITION_MANAGER.isApprovedForAll(owner, caller); } + + /// @dev check that the v4 position manager call is a safe call + /// of the position-altering Actions, we only allow Actions.MINT + /// this is because, if a user could be tricked into approving the UniversalRouter for + /// their position, an attacker could take their fees, or drain their entire position + function _checkV4ClPositionManagerCall(bytes calldata inputs) internal view { + bytes4 selector; + assembly { + selector := calldataload(inputs.offset) + } + if (selector != V4_CL_POSITION_MANAGER.modifyLiquidities.selector) { + revert InvalidAction(selector); + } + + // slice is `abi.encode(bytes unlockData, uint256 deadline)` + bytes calldata slice = inputs[4:]; + // the first bytes(0) extracts the unlockData parameter from modifyLiquidities + // unlockData = `abi.encode(bytes actions, bytes[] params)` + // the second bytes(0) extracts the actions parameter from unlockData + bytes calldata actions = slice.toBytes(0).toBytes(0); + + uint256 numActions = actions.length; + + for (uint256 actionIndex = 0; actionIndex < numActions; actionIndex++) { + uint256 action = uint8(actions[actionIndex]); + + if ( + action == Actions.CL_INCREASE_LIQUIDITY || action == Actions.CL_DECREASE_LIQUIDITY + || action == Actions.CL_BURN_POSITION + ) { + revert OnlyMintAllowed(); + } + } + } + + /// @dev check that the v4 position manager call is a safe call + /// of the position-altering Actions, we only allow Actions.BIN_ADD_LIQUIDITY + /// this is because, if a user could be tricked into approving the UniversalRouter for + /// their position, an attacker could drain their entire position + function _checkV4BinPositionManagerCall(bytes calldata inputs) internal view { + bytes4 selector; + assembly { + selector := calldataload(inputs.offset) + } + if (selector != V4_BIN_POSITION_MANAGER.modifyLiquidities.selector) { + revert InvalidAction(selector); + } + + // slice is `abi.encode(bytes unlockData, uint256 deadline)` + bytes calldata slice = inputs[4:]; + // the first bytes(0) extracts the unlockData parameter from modifyLiquidities + // unlockData = `abi.encode(bytes actions, bytes[] params)` + // the second bytes(0) extracts the actions parameter from unlockData + bytes calldata actions = slice.toBytes(0).toBytes(0); + + uint256 numActions = actions.length; + + for (uint256 actionIndex = 0; actionIndex < numActions; actionIndex++) { + uint256 action = uint8(actions[actionIndex]); + if (action == Actions.BIN_REMOVE_LIQUIDITY) { + revert OnlyAddLiqudityAllowed(); + } + } + } } diff --git a/test/UniversalRouter.t.sol b/test/UniversalRouter.t.sol index 77284d1..52a355a 100644 --- a/test/UniversalRouter.t.sol +++ b/test/UniversalRouter.t.sol @@ -631,7 +631,7 @@ contract UniversalRouterTest is Test, GasSnapshot, Permit2SignatureHelpers, Depl // if valid commands, return if (command >= 0x00 && command <= 0x06) return; if (command >= 0x08 && command <= 0x0e) return; - if (command >= 0x10 && command <= 0x14) return; + if (command >= 0x10 && command <= 0x16) return; if (command >= 0x21 && command <= 0x23) return; bytes memory commands = abi.encodePacked(bytes1(uint8(command))); diff --git a/test/V3ToV4Migration.t.sol b/test/V3ToV4Migration.t.sol index 283cf76..df46989 100644 --- a/test/V3ToV4Migration.t.sol +++ b/test/V3ToV4Migration.t.sol @@ -34,6 +34,7 @@ import {BinLiquidityHelper} from "pancake-v4-periphery/test/pool-bin/helper/BinL import {IPancakeV3PoolDeployer} from "../src/modules/pancakeswap/v3/interfaces/IPancakeV3PoolDeployer.sol"; import {IPancakeV3Factory} from "../src/modules/pancakeswap/v3/interfaces/IPancakeV3Factory.sol"; +import {V3ToV4Migrator} from "../src/modules/V3ToV4Migrator.sol"; import {IUniversalRouter} from "../src/interfaces/IUniversalRouter.sol"; import {Commands} from "../src/libraries/Commands.sol"; import {RouterParameters} from "../src/base/RouterImmutables.sol"; @@ -184,7 +185,7 @@ contract V3ToV4MigrationTest is BasePancakeSwapV4, OldVersionHelper, BinLiquidit bytes[] memory inputs = new bytes[](1); inputs[0] = abi.encodePacked(IV3NonfungiblePositionManager.collect.selector, abi.encode(params)); - vm.expectRevert(abi.encodeWithSelector(Dispatcher.NotAuthorizedForToken.selector, params.tokenId)); + vm.expectRevert(abi.encodeWithSelector(V3ToV4Migrator.NotAuthorizedForToken.selector, params.tokenId)); router.execute(commands, inputs); } @@ -208,7 +209,7 @@ contract V3ToV4MigrationTest is BasePancakeSwapV4, OldVersionHelper, BinLiquidit inputs[0] = abi.encodePacked(IV3NonfungiblePositionManager.mint.selector, abi.encode(params)); vm.expectRevert( - abi.encodeWithSelector(Dispatcher.InvalidAction.selector, IV3NonfungiblePositionManager.mint.selector) + abi.encodeWithSelector(V3ToV4Migrator.InvalidAction.selector, IV3NonfungiblePositionManager.mint.selector) ); router.execute(commands, inputs); } @@ -280,6 +281,74 @@ contract V3ToV4MigrationTest is BasePancakeSwapV4, OldVersionHelper, BinLiquidit assertEq(token1.balanceOf(address(router)), 9999999999999999999); } + function test_v4CLPositionmanger_InvalidAction() public { + Plan memory planner = Planner.init(); + + // prep universal router actions + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_CL_POSITION_CALL))); + bytes[] memory inputs = new bytes[](1); + + bytes4 invalidSelector = IPositionManager.modifyLiquiditiesWithoutLock.selector; + inputs[0] = abi.encodePacked(invalidSelector, abi.encode(planner.encode(), block.timestamp)); + vm.expectRevert(abi.encodeWithSelector(V3ToV4Migrator.InvalidAction.selector, invalidSelector)); + router.execute(commands, inputs); + } + + function test_v4CLPositionmanger_BlacklistedAction() public { + Plan memory planner = Planner.init(); + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_CL_POSITION_CALL))); + bytes[] memory inputs = new bytes[](1); + + uint256[] memory invalidActions = new uint256[](3); + invalidActions[0] = Actions.CL_INCREASE_LIQUIDITY; + invalidActions[1] = Actions.CL_DECREASE_LIQUIDITY; + invalidActions[2] = Actions.CL_BURN_POSITION; + + for (uint256 i; i < invalidActions.length; i++) { + planner.add(invalidActions[i], ""); + inputs[0] = abi.encodePacked( + IPositionManager.modifyLiquidities.selector, abi.encode(planner.encode(), block.timestamp) + ); + + // verify revert for invalid actions + vm.expectRevert(V3ToV4Migrator.OnlyMintAllowed.selector); + router.execute(commands, inputs); + } + } + + function test_v4BinPositionmanger_InvalidAction() public { + Plan memory planner = Planner.init(); + + // prep universal router actions + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_BIN_POSITION_CALL))); + bytes[] memory inputs = new bytes[](1); + + bytes4 invalidSelector = IPositionManager.modifyLiquiditiesWithoutLock.selector; + inputs[0] = abi.encodePacked(invalidSelector, abi.encode(planner.encode(), block.timestamp)); + vm.expectRevert(abi.encodeWithSelector(V3ToV4Migrator.InvalidAction.selector, invalidSelector)); + router.execute(commands, inputs); + } + + function test_v4BinPositionmanger_BlacklistedAction() public { + Plan memory planner = Planner.init(); + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_BIN_POSITION_CALL))); + bytes[] memory inputs = new bytes[](1); + + uint256[] memory invalidActions = new uint256[](1); + invalidActions[0] = Actions.BIN_REMOVE_LIQUIDITY; + + for (uint256 i; i < invalidActions.length; i++) { + planner.add(invalidActions[i], ""); + inputs[0] = abi.encodePacked( + IPositionManager.modifyLiquidities.selector, abi.encode(planner.encode(), block.timestamp) + ); + + // verify revert for invalid actions + vm.expectRevert(V3ToV4Migrator.OnlyAddLiqudityAllowed.selector); + router.execute(commands, inputs); + } + } + /// @dev Assume token0/token1 is aready in universal router from earlier steps on v3 /// then add liquidity to v4 cl and sweep remaining token function test_v4CLPositionmanager_Mint() public { diff --git a/test/v4/BinNativePancakeSwapV4.t.sol b/test/v4/BinNativePancakeSwapV4.t.sol index 5764737..59a70f7 100644 --- a/test/v4/BinNativePancakeSwapV4.t.sol +++ b/test/v4/BinNativePancakeSwapV4.t.sol @@ -23,9 +23,11 @@ import {IBinRouterBase} from "pancake-v4-periphery/src/pool-bin/interfaces/IBinR import {BinLiquidityHelper} from "pancake-v4-periphery/test/pool-bin/helper/BinLiquidityHelper.sol"; import {IBinPositionManager} from "pancake-v4-periphery/src/pool-bin/interfaces/IBinPositionManager.sol"; import {PathKey} from "pancake-v4-periphery/src/libraries/PathKey.sol"; +import {BinPool} from "pancake-v4-core/src/pool-bin/libraries/BinPool.sol"; import {BasePancakeSwapV4} from "./BasePancakeSwapV4.sol"; import {UniversalRouter} from "../../src/UniversalRouter.sol"; +import {IUniversalRouter} from "../../src/interfaces/IUniversalRouter.sol"; import {Constants} from "../../src/libraries/Constants.sol"; import {Commands} from "../../src/libraries/Commands.sol"; import {RouterParameters} from "../../src/base/RouterImmutables.sol"; @@ -96,6 +98,42 @@ contract BinNativePancakeSwapV4Test is BasePancakeSwapV4, BinLiquidityHelper { _mint(poolKey0); } + function test_v4BinSwap_v4InitializeBinPool() public { + MockERC20 _token = new MockERC20("token", "token", 18); + PoolKey memory _poolKey = PoolKey({ + currency0: CurrencyLibrary.NATIVE, + currency1: Currency.wrap(address(_token)), + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + parameters: bytes32(0).setBinStep(55) + }); + + // before + (uint24 activeId,,) = poolManager.getSlot0(_poolKey.toId()); + assertEq(activeId, 0); + + // initialize + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_BIN_INITIALIZE_POOL))); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(_poolKey, ACTIVE_ID_1_1); + snapStart("BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool"); + router.execute(commands, inputs); + snapEnd(); + + // verify + (activeId,,) = poolManager.getSlot0(_poolKey.toId()); + assertEq(activeId, ACTIVE_ID_1_1); + + // initialize again + vm.expectRevert( + abi.encodeWithSelector( + IUniversalRouter.ExecutionFailed.selector, 0, abi.encodePacked(BinPool.PoolAlreadyInitialized.selector) + ) + ); + router.execute(commands, inputs); + } + function test_v4BinSwap_ExactInSingle_NativeIn() public { uint128 amountIn = 0.01 ether; vm.deal(alice, amountIn); diff --git a/test/v4/BinPancakeSwapV4.t.sol b/test/v4/BinPancakeSwapV4.t.sol index 1111c5b..b3349a6 100644 --- a/test/v4/BinPancakeSwapV4.t.sol +++ b/test/v4/BinPancakeSwapV4.t.sol @@ -23,9 +23,11 @@ import {IBinRouterBase} from "pancake-v4-periphery/src/pool-bin/interfaces/IBinR import {BinLiquidityHelper} from "pancake-v4-periphery/test/pool-bin/helper/BinLiquidityHelper.sol"; import {IBinPositionManager} from "pancake-v4-periphery/src/pool-bin/interfaces/IBinPositionManager.sol"; import {PathKey} from "pancake-v4-periphery/src/libraries/PathKey.sol"; +import {BinPool} from "pancake-v4-core/src/pool-bin/libraries/BinPool.sol"; import {BasePancakeSwapV4} from "./BasePancakeSwapV4.sol"; import {UniversalRouter} from "../../src/UniversalRouter.sol"; +import {IUniversalRouter} from "../../src/interfaces/IUniversalRouter.sol"; import {Constants} from "../../src/libraries/Constants.sol"; import {Commands} from "../../src/libraries/Commands.sol"; import {RouterParameters} from "../../src/base/RouterImmutables.sol"; @@ -105,6 +107,7 @@ contract BinPancakeSwapV4Test is BasePancakeSwapV4, BinLiquidityHelper { poolManager.initialize(poolKey0, ACTIVE_ID_1_1, new bytes(0)); _mint(poolKey0); + // initialize poolKey1 via universal-router poolKey1 = PoolKey({ currency0: currency1, currency1: currency2, @@ -113,10 +116,48 @@ contract BinPancakeSwapV4Test is BasePancakeSwapV4, BinLiquidityHelper { fee: uint24(3000), parameters: bytes32(0).setBinStep(10) }); - poolManager.initialize(poolKey1, ACTIVE_ID_1_1, new bytes(0)); + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_BIN_INITIALIZE_POOL))); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(poolKey1, ACTIVE_ID_1_1); + router.execute(commands, inputs); _mint(poolKey1); } + function test_v4BinSwap_InitializeBinPool() public { + PoolKey memory _poolKey = PoolKey({ + currency0: currency0, + currency1: currency2, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + parameters: bytes32(0).setBinStep(55) + }); + + // before + (uint24 activeId,,) = poolManager.getSlot0(_poolKey.toId()); + assertEq(activeId, 0); + + // initialize + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_BIN_INITIALIZE_POOL))); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(_poolKey, ACTIVE_ID_1_1); + snapStart("BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool"); + router.execute(commands, inputs); + snapEnd(); + + // verify + (activeId,,) = poolManager.getSlot0(_poolKey.toId()); + assertEq(activeId, ACTIVE_ID_1_1); + + // initialize again + vm.expectRevert( + abi.encodeWithSelector( + IUniversalRouter.ExecutionFailed.selector, 0, abi.encodePacked(BinPool.PoolAlreadyInitialized.selector) + ) + ); + router.execute(commands, inputs); + } + function test_v4BinSwap_ExactInSingle() public { uint128 amountIn = 0.01 ether; MockERC20(Currency.unwrap(currency0)).mint(alice, amountIn); diff --git a/test/v4/CLNativePancakeSwapV4.t.sol b/test/v4/CLNativePancakeSwapV4.t.sol index 152aaaf..2cd6d58 100644 --- a/test/v4/CLNativePancakeSwapV4.t.sol +++ b/test/v4/CLNativePancakeSwapV4.t.sol @@ -25,9 +25,11 @@ import {Actions} from "pancake-v4-periphery/src/libraries/Actions.sol"; import {ICLRouterBase} from "pancake-v4-periphery/src/pool-cl/interfaces/ICLRouterBase.sol"; import {LiquidityAmounts} from "pancake-v4-periphery/src/pool-cl/libraries/LiquidityAmounts.sol"; import {PathKey} from "pancake-v4-periphery/src/libraries/PathKey.sol"; +import {CLPool} from "pancake-v4-core/src/pool-cl/libraries/CLPool.sol"; import {BasePancakeSwapV4} from "./BasePancakeSwapV4.sol"; import {UniversalRouter} from "../../src/UniversalRouter.sol"; +import {IUniversalRouter} from "../../src/interfaces/IUniversalRouter.sol"; import {Constants} from "../../src/libraries/Constants.sol"; import {Commands} from "../../src/libraries/Commands.sol"; import {RouterParameters} from "../../src/base/RouterImmutables.sol"; @@ -98,6 +100,42 @@ contract CLNativePancakeSwapV4Test is BasePancakeSwapV4 { _mint(poolKey0); } + function test_v4ClSwap_v4InitializeClPool() public { + MockERC20 _token = new MockERC20("token", "token", 18); + PoolKey memory _poolKey = PoolKey({ + currency0: CurrencyLibrary.NATIVE, + currency1: Currency.wrap(address(_token)), + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + parameters: bytes32(0).setTickSpacing(10) + }); + + // before + (uint160 sqrtPriceX96,,,) = poolManager.getSlot0(_poolKey.toId()); + assertEq(sqrtPriceX96, 0); + + // initialize + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_CL_INITIALIZE_POOL))); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(_poolKey, SQRT_PRICE_1_1); + snapStart("CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool"); + router.execute(commands, inputs); + snapEnd(); + + // verify + (sqrtPriceX96,,,) = poolManager.getSlot0(_poolKey.toId()); + assertEq(sqrtPriceX96, SQRT_PRICE_1_1); + + // initialize again + vm.expectRevert( + abi.encodeWithSelector( + IUniversalRouter.ExecutionFailed.selector, 0, abi.encodePacked(CLPool.PoolAlreadyInitialized.selector) + ) + ); + router.execute(commands, inputs); + } + function test_v4ClSwap_ExactInSingle_NativeIn() public { uint128 amountIn = 0.01 ether; vm.deal(alice, amountIn); diff --git a/test/v4/CLPancakeSwapV4.t.sol b/test/v4/CLPancakeSwapV4.t.sol index 35a2062..3676843 100644 --- a/test/v4/CLPancakeSwapV4.t.sol +++ b/test/v4/CLPancakeSwapV4.t.sol @@ -25,9 +25,11 @@ import {Actions} from "pancake-v4-periphery/src/libraries/Actions.sol"; import {ICLRouterBase} from "pancake-v4-periphery/src/pool-cl/interfaces/ICLRouterBase.sol"; import {LiquidityAmounts} from "pancake-v4-periphery/src/pool-cl/libraries/LiquidityAmounts.sol"; import {PathKey} from "pancake-v4-periphery/src/libraries/PathKey.sol"; +import {CLPool} from "pancake-v4-core/src/pool-cl/libraries/CLPool.sol"; import {BasePancakeSwapV4} from "./BasePancakeSwapV4.sol"; import {UniversalRouter} from "../../src/UniversalRouter.sol"; +import {IUniversalRouter} from "../../src/interfaces/IUniversalRouter.sol"; import {Constants} from "../../src/libraries/Constants.sol"; import {Commands} from "../../src/libraries/Commands.sol"; import {RouterParameters} from "../../src/base/RouterImmutables.sol"; @@ -107,6 +109,7 @@ contract CLPancakeSwapV4Test is BasePancakeSwapV4 { poolManager.initialize(poolKey0, SQRT_PRICE_1_1, new bytes(0)); _mint(poolKey0); + // initialize poolKey1 via universal-router poolKey1 = PoolKey({ currency0: currency1, currency1: currency2, @@ -115,10 +118,49 @@ contract CLPancakeSwapV4Test is BasePancakeSwapV4 { fee: uint24(3000), parameters: bytes32(0).setTickSpacing(10) }); - poolManager.initialize(poolKey1, SQRT_PRICE_1_1, new bytes(0)); + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_CL_INITIALIZE_POOL))); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(poolKey1, SQRT_PRICE_1_1); + router.execute(commands, inputs); _mint(poolKey1); } + function test_v4ClSwap_v4InitializeClPool() public { + MockERC20 _token = new MockERC20("token", "token", 18); + PoolKey memory _poolKey = PoolKey({ + currency0: currency0, + currency1: currency2, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + parameters: bytes32(0).setTickSpacing(10) + }); + + // before + (uint160 sqrtPriceX96,,,) = poolManager.getSlot0(_poolKey.toId()); + assertEq(sqrtPriceX96, 0); + + // initialize + bytes memory commands = abi.encodePacked(bytes1(uint8(Commands.V4_CL_INITIALIZE_POOL))); + bytes[] memory inputs = new bytes[](1); + inputs[0] = abi.encode(_poolKey, SQRT_PRICE_1_1); + snapStart("CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool"); + router.execute(commands, inputs); + snapEnd(); + + // verify + (sqrtPriceX96,,,) = poolManager.getSlot0(_poolKey.toId()); + assertEq(sqrtPriceX96, SQRT_PRICE_1_1); + + // initialize again + vm.expectRevert( + abi.encodeWithSelector( + IUniversalRouter.ExecutionFailed.selector, 0, abi.encodePacked(CLPool.PoolAlreadyInitialized.selector) + ) + ); + router.execute(commands, inputs); + } + function test_v4ClSwap_ExactInSingle() public { uint128 amountIn = 0.01 ether; MockERC20(Currency.unwrap(currency0)).mint(alice, amountIn); From 0dcafa4f81597601a665827302def61bb1e22718 Mon Sep 17 00:00:00 2001 From: ChefMist <133624774+ChefMist@users.noreply.github.com> Date: Mon, 18 Nov 2024 17:51:41 +0800 Subject: [PATCH 2/2] feat: gas optimization (#28) * feat: update universal router with new action and position call protection * test: fix test case * feat: reduce optimizer runs * cleanup: remove unused code * refactor 3: move v3 position manager refactoring to v3tov4migrator * refactor 4: tweak permit2 call --- ...test_v4BinSwap_ExactInSingle_NativeIn.snap | 2 +- ...actInSingle_NativeOut_RouterRecipient.snap | 2 +- ...st#test_v4BinSwap_v4InitializeBinPool.snap | 2 +- ...apV4Test#test_v4BinSwap_ExactInSingle.snap | 2 +- ...4Test#test_v4BinSwap_ExactIn_MultiHop.snap | 2 +- ...Test#test_v4BinSwap_ExactIn_SingleHop.snap | 2 +- ...Test#test_v4BinSwap_ExactOut_MultiHop.snap | 2 +- ...est#test_v4BinSwap_ExactOut_SingleHop.snap | 2 +- ...Test#test_v4BinSwap_InitializeBinPool.snap | 2 +- ...apV4Test#test_v4ClSwap_ExactOutSingle.snap | 2 +- ...#test_v4ClSwap_ExactInSingle_NativeIn.snap | 2 +- ...test_v4ClSwap_ExactInSingle_NativeOut.snap | 2 +- ...Test#test_v4ClSwap_v4InitializeClPool.snap | 2 +- ...wapV4Test#test_v4ClSwap_ExactInSingle.snap | 2 +- ...V4Test#test_v4ClSwap_ExactIn_MultiHop.snap | 2 +- ...4Test#test_v4ClSwap_ExactIn_SingleHop.snap | 2 +- ...apV4Test#test_v4ClSwap_ExactOutSingle.snap | 2 +- ...4Test#test_v4ClSwap_ExactOut_MultiHop.snap | 2 +- ...Test#test_v4ClSwap_ExactOut_SingleHop.snap | 2 +- ...Test#test_v4ClSwap_v4InitializeClPool.snap | 2 +- ...wapV2Test#test_v2Swap_exactInput0For1.snap | 2 +- ...apV2Test#test_v2Swap_exactOutput0For1.snap | 2 +- ...wapV3Test#test_v3Swap_ExactInput0For1.snap | 2 +- ...3Swap_ExactInput0For1_ContractBalance.snap | 2 +- ...3Test#test_v3Swap_exactInput_MultiHop.snap | 2 +- ...apV3Test#test_v3Swap_exactOutput0For1.snap | 2 +- ...Test#test_v3Swap_exactOutput_MultiHop.snap | 2 +- ...pTest#test_stableSwap_ExactInput0For1.snap | 2 +- ...pTest#test_stableSwap_ExactInput1For0.snap | 2 +- .../UniversalRouterBytecodeSize.snap | 2 +- .../UniversalRouterTest#test_sweep_token.snap | 2 +- ...#test_v4CLPositionmanager_Mint_Native.snap | 2 +- ...ationTest#test_v3PositionManager_burn.snap | 2 +- ..._v4BinPositionmanager_BinAddLiquidity.snap | 2 +- ...ositionmanager_BinAddLiquidity_Native.snap | 2 +- ...ionTest#test_v4CLPositionmanager_Mint.snap | 2 +- src/base/Dispatcher.sol | 59 ++++++------------- src/base/RouterImmutables.sol | 2 +- src/interfaces/IWETH9.sol | 13 ---- src/libraries/Commands.sol | 2 +- src/modules/V3ToV4Migrator.sol | 43 +++++++++++++- 41 files changed, 96 insertions(+), 95 deletions(-) delete mode 100755 src/interfaces/IWETH9.sol diff --git a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeIn.snap b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeIn.snap index e7e8167..5cf302e 100644 --- a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeIn.snap +++ b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeIn.snap @@ -1 +1 @@ -146503 \ No newline at end of file +146493 \ No newline at end of file diff --git a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeOut_RouterRecipient.snap b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeOut_RouterRecipient.snap index fb3f970..706c476 100644 --- a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeOut_RouterRecipient.snap +++ b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_ExactInSingle_NativeOut_RouterRecipient.snap @@ -1 +1 @@ -123561 \ No newline at end of file +123551 \ No newline at end of file diff --git a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool.snap b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool.snap index b77884b..b846563 100644 --- a/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool.snap +++ b/.forge-snapshots/BinNativePancakeSwapV4Test#test_v4BinSwap_v4InitializeBinPool.snap @@ -1 +1 @@ -138906 \ No newline at end of file +138896 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactInSingle.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactInSingle.snap index 878dfb2..70adf0b 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactInSingle.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactInSingle.snap @@ -1 +1 @@ -147468 \ No newline at end of file +147458 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_MultiHop.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_MultiHop.snap index b2616c5..2d15956 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_MultiHop.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_MultiHop.snap @@ -1 +1 @@ -178629 \ No newline at end of file +178619 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_SingleHop.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_SingleHop.snap index 84d9bed..c5366c4 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_SingleHop.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactIn_SingleHop.snap @@ -1 +1 @@ -149270 \ No newline at end of file +149260 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_MultiHop.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_MultiHop.snap index 8eba04b..dd55f8a 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_MultiHop.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_MultiHop.snap @@ -1 +1 @@ -182474 \ No newline at end of file +182464 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_SingleHop.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_SingleHop.snap index c0a6bc6..54eea20 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_SingleHop.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_ExactOut_SingleHop.snap @@ -1 +1 @@ -153645 \ No newline at end of file +153635 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool.snap index c22791e..0da2823 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4BinSwap_InitializeBinPool.snap @@ -1 +1 @@ -159096 \ No newline at end of file +159086 \ No newline at end of file diff --git a/.forge-snapshots/BinPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap b/.forge-snapshots/BinPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap index 0a1d0ec..62086ad 100644 --- a/.forge-snapshots/BinPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap +++ b/.forge-snapshots/BinPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap @@ -1 +1 @@ -151845 \ No newline at end of file +151835 \ No newline at end of file diff --git a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeIn.snap b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeIn.snap index 324d453..2d70b9f 100644 --- a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeIn.snap +++ b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeIn.snap @@ -1 +1 @@ -172182 \ No newline at end of file +172172 \ No newline at end of file diff --git a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeOut.snap b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeOut.snap index 5036e07..e7589ba 100644 --- a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeOut.snap +++ b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_ExactInSingle_NativeOut.snap @@ -1 +1 @@ -174508 \ No newline at end of file +174498 \ No newline at end of file diff --git a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap index 80a30bc..b56b41b 100644 --- a/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap +++ b/.forge-snapshots/CLNativePancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap @@ -1 +1 @@ -140212 \ No newline at end of file +140202 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactInSingle.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactInSingle.snap index 5ae8857..70fa8e0 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactInSingle.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactInSingle.snap @@ -1 +1 @@ -181622 \ No newline at end of file +181612 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_MultiHop.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_MultiHop.snap index 5833d4b..234ad8a 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_MultiHop.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_MultiHop.snap @@ -1 +1 @@ -246450 \ No newline at end of file +246440 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_SingleHop.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_SingleHop.snap index ae927f7..59a9c41 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_SingleHop.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactIn_SingleHop.snap @@ -1 +1 @@ -183168 \ No newline at end of file +183158 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap index b6b0011..74d9ff9 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOutSingle.snap @@ -1 +1 @@ -185957 \ No newline at end of file +185947 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_MultiHop.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_MultiHop.snap index 6b76d2a..e239e91 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_MultiHop.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_MultiHop.snap @@ -1 +1 @@ -250209 \ No newline at end of file +250199 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_SingleHop.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_SingleHop.snap index aec40bf..03538d4 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_SingleHop.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_ExactOut_SingleHop.snap @@ -1 +1 @@ -187500 \ No newline at end of file +187490 \ No newline at end of file diff --git a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap index a91adf9..50aa11a 100644 --- a/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap +++ b/.forge-snapshots/CLPancakeSwapV4Test#test_v4ClSwap_v4InitializeClPool.snap @@ -1 +1 @@ -160352 \ No newline at end of file +160342 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactInput0For1.snap b/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactInput0For1.snap index abb220c..43b8c36 100644 --- a/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactInput0For1.snap +++ b/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactInput0For1.snap @@ -1 +1 @@ -100195 \ No newline at end of file +100185 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactOutput0For1.snap b/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactOutput0For1.snap index 1a5e88d..1a90aab 100644 --- a/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactOutput0For1.snap +++ b/.forge-snapshots/PancakeSwapV2Test#test_v2Swap_exactOutput0For1.snap @@ -1 +1 @@ -100807 \ No newline at end of file +100797 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1.snap index cb85e1b..92d594d 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1.snap @@ -1 +1 @@ -151937 \ No newline at end of file +151904 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1_ContractBalance.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1_ContractBalance.snap index 6622494..9baf992 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1_ContractBalance.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_ExactInput0For1_ContractBalance.snap @@ -1 +1 @@ -154851 \ No newline at end of file +154817 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactInput_MultiHop.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactInput_MultiHop.snap index 9bbe512..73a092f 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactInput_MultiHop.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactInput_MultiHop.snap @@ -1 +1 @@ -243405 \ No newline at end of file +243354 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput0For1.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput0For1.snap index e2b9081..b898a75 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput0For1.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput0For1.snap @@ -1 +1 @@ -150632 \ No newline at end of file +150606 \ No newline at end of file diff --git a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput_MultiHop.snap b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput_MultiHop.snap index 8a14517..a1d9649 100644 --- a/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput_MultiHop.snap +++ b/.forge-snapshots/PancakeSwapV3Test#test_v3Swap_exactOutput_MultiHop.snap @@ -1 +1 @@ -254310 \ No newline at end of file +254284 \ No newline at end of file diff --git a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap index 7521163..68a33db 100644 --- a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap +++ b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput0For1.snap @@ -1 +1 @@ -194005 \ No newline at end of file +194002 \ No newline at end of file diff --git a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap index 6cfbfcc..2672f0e 100644 --- a/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap +++ b/.forge-snapshots/StableSwapTest#test_stableSwap_ExactInput1For0.snap @@ -1 +1 @@ -194073 \ No newline at end of file +194070 \ No newline at end of file diff --git a/.forge-snapshots/UniversalRouterBytecodeSize.snap b/.forge-snapshots/UniversalRouterBytecodeSize.snap index 4759177..b6a4fae 100644 --- a/.forge-snapshots/UniversalRouterBytecodeSize.snap +++ b/.forge-snapshots/UniversalRouterBytecodeSize.snap @@ -1 +1 @@ -24411 \ No newline at end of file +24246 \ No newline at end of file diff --git a/.forge-snapshots/UniversalRouterTest#test_sweep_token.snap b/.forge-snapshots/UniversalRouterTest#test_sweep_token.snap index 9b29a51..0345b59 100644 --- a/.forge-snapshots/UniversalRouterTest#test_sweep_token.snap +++ b/.forge-snapshots/UniversalRouterTest#test_sweep_token.snap @@ -1 +1 @@ -55456 \ No newline at end of file +55435 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationNativeTest#test_v4CLPositionmanager_Mint_Native.snap b/.forge-snapshots/V3ToV4MigrationNativeTest#test_v4CLPositionmanager_Mint_Native.snap index 863b59c..6e69381 100644 --- a/.forge-snapshots/V3ToV4MigrationNativeTest#test_v4CLPositionmanager_Mint_Native.snap +++ b/.forge-snapshots/V3ToV4MigrationNativeTest#test_v4CLPositionmanager_Mint_Native.snap @@ -1 +1 @@ -561402 \ No newline at end of file +561379 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationTest#test_v3PositionManager_burn.snap b/.forge-snapshots/V3ToV4MigrationTest#test_v3PositionManager_burn.snap index 20ab411..e378723 100644 --- a/.forge-snapshots/V3ToV4MigrationTest#test_v3PositionManager_burn.snap +++ b/.forge-snapshots/V3ToV4MigrationTest#test_v3PositionManager_burn.snap @@ -1 +1 @@ -291611 \ No newline at end of file +291572 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity.snap b/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity.snap index 9ac2d08..9f2efb3 100644 --- a/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity.snap +++ b/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity.snap @@ -1 +1 @@ -597077 \ No newline at end of file +597025 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity_Native.snap b/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity_Native.snap index df32336..0183ac9 100644 --- a/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity_Native.snap +++ b/.forge-snapshots/V3ToV4MigrationTest#test_v4BinPositionmanager_BinAddLiquidity_Native.snap @@ -1 +1 @@ -572762 \ No newline at end of file +572739 \ No newline at end of file diff --git a/.forge-snapshots/V3ToV4MigrationTest#test_v4CLPositionmanager_Mint.snap b/.forge-snapshots/V3ToV4MigrationTest#test_v4CLPositionmanager_Mint.snap index abf5365..659a9df 100644 --- a/.forge-snapshots/V3ToV4MigrationTest#test_v4CLPositionmanager_Mint.snap +++ b/.forge-snapshots/V3ToV4MigrationTest#test_v4CLPositionmanager_Mint.snap @@ -1 +1 @@ -585727 \ No newline at end of file +585675 \ No newline at end of file diff --git a/src/base/Dispatcher.sol b/src/base/Dispatcher.sol index 09a8e02..12f9f0f 100755 --- a/src/base/Dispatcher.sol +++ b/src/base/Dispatcher.sol @@ -13,7 +13,6 @@ import {Commands} from "../libraries/Commands.sol"; import {Lock} from "./Lock.sol"; import {ERC20} from "solmate/src/tokens/ERC20.sol"; import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; -import {IERC721Permit} from "pancake-v4-periphery/src/pool-cl/interfaces/IERC721Permit.sol"; import {ActionConstants} from "pancake-v4-periphery/src/libraries/ActionConstants.sol"; import {BaseActionsRouter} from "pancake-v4-periphery/src/base/BaseActionsRouter.sol"; import {CalldataDecoder} from "pancake-v4-periphery/src/libraries/CalldataDecoder.sol"; @@ -120,11 +119,14 @@ abstract contract Dispatcher is permitBatch := add(inputs.offset, calldataload(inputs.offset)) } bytes calldata data = inputs.toBytes(1); - try PERMIT2.permit(msgSender(), permitBatch, data) {} - catch (bytes memory reason) { - output = reason; - success = false; - } + (success, output) = address(PERMIT2).call( + abi.encodeWithSignature( + "permit(address,((address,uint160,uint48,uint48)[],address,uint256),bytes)", + msgSender(), + permitBatch, + data + ) + ); return (success, output); } else if (command == Commands.SWEEP) { // equivalent: abi.decode(inputs, (address, address, uint256)) @@ -209,11 +211,14 @@ abstract contract Dispatcher is permitSingle := inputs.offset } bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5) - try PERMIT2.permit(msgSender(), permitSingle, data) {} - catch (bytes memory reason) { - output = reason; - success = false; - } + (success, output) = address(PERMIT2).call( + abi.encodeWithSignature( + "permit(address,((address,uint160,uint48,uint48),address,uint256),bytes)", + msgSender(), + permitSingle, + data + ) + ); return (success, output); } else if (command == Commands.WRAP_ETH) { // equivalent: abi.decode(inputs, (address, uint256)) @@ -270,39 +275,11 @@ abstract contract Dispatcher is return (success, output); // This contract MUST be approved to spend the token since its going to be doing the call on the position manager } else if (command == Commands.V3_POSITION_MANAGER_PERMIT) { - bytes4 selector; - assembly { - selector := calldataload(inputs.offset) - } - if (selector != IERC721Permit.permit.selector) { - revert InvalidAction(selector); - } - + _checkV3PermitCall(inputs); (success, output) = address(V3_POSITION_MANAGER).call(inputs); return (success, output); } else if (command == Commands.V3_POSITION_MANAGER_CALL) { - bytes4 selector; - assembly { - selector := calldataload(inputs.offset) - } - if (!isValidAction(selector)) { - revert InvalidAction(selector); - } - - uint256 tokenId; - assembly { - // tokenId is always the first parameter in the valid actions - tokenId := calldataload(add(inputs.offset, 0x04)) - } - - // If any other address that is not the owner wants to call this function, it also needs to be approved (in addition to this contract) - // This can be done in 2 ways: - // 1. This contract is permitted for the specific token and the caller is approved for ALL of the owner's tokens - // 2. This contract is permitted for ALL of the owner's tokens and the caller is permitted for the specific token - if (!isAuthorizedForToken(msgSender(), tokenId)) { - revert NotAuthorizedForToken(tokenId); - } - + _checkV3PositionManagerCall(inputs, msgSender()); /// @dev ensure there's follow-up action if v3 position's removed token are sent to router contract (success, output) = address(V3_POSITION_MANAGER).call(inputs); return (success, output); diff --git a/src/base/RouterImmutables.sol b/src/base/RouterImmutables.sol index f39fbec..6fe658e 100755 --- a/src/base/RouterImmutables.sol +++ b/src/base/RouterImmutables.sol @@ -6,7 +6,7 @@ import {IV3NonfungiblePositionManager} from "pancake-v4-periphery/src/interfaces/external/IV3NonfungiblePositionManager.sol"; import {IPositionManager} from "pancake-v4-periphery/src/interfaces/IPositionManager.sol"; import {ERC20} from "solmate/src/tokens/ERC20.sol"; -import {IWETH9} from "../interfaces/IWETH9.sol"; +import {IWETH9} from "pancake-v4-periphery/src/interfaces/external/IWETH9.sol"; struct RouterParameters { // Payment parameters diff --git a/src/interfaces/IWETH9.sol b/src/interfaces/IWETH9.sol deleted file mode 100755 index cb82c0b..0000000 --- a/src/interfaces/IWETH9.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.0; - -import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -/// @title Interface for WETH9 -interface IWETH9 is IERC20 { - /// @notice Deposit ether to get wrapped ether - function deposit() external payable; - - /// @notice Withdraw wrapped ether to get ether - function withdraw(uint256) external; -} diff --git a/src/libraries/Commands.sol b/src/libraries/Commands.sol index b1a266f..9d3b31e 100755 --- a/src/libraries/Commands.sol +++ b/src/libraries/Commands.sol @@ -39,7 +39,7 @@ library Commands { uint256 constant V4_BIN_INITIALIZE_POOL = 0x14; uint256 constant V4_CL_POSITION_CALL = 0x15; uint256 constant V4_BIN_POSITION_CALL = 0x16; - // COMMAND_PLACEHOLDER = 0x17 -> 0x20 + // COMMAND_PLACEHOLDER = 0x15 -> 0x20 // Command Types where 0x21<=value<=0x3f uint256 constant EXECUTE_SUB_PLAN = 0x21; diff --git a/src/modules/V3ToV4Migrator.sol b/src/modules/V3ToV4Migrator.sol index 80c79dd..9ac9726 100644 --- a/src/modules/V3ToV4Migrator.sol +++ b/src/modules/V3ToV4Migrator.sol @@ -7,7 +7,7 @@ import {IV3NonfungiblePositionManager} from import {Actions} from "pancake-v4-periphery/src/libraries/Actions.sol"; import {CalldataDecoder} from "pancake-v4-periphery/src/libraries/CalldataDecoder.sol"; import {IPositionManager} from "pancake-v4-periphery/src/interfaces/IPositionManager.sol"; -import {console2} from "forge-std/console2.sol"; +import {IERC721Permit} from "pancake-v4-periphery/src/pool-cl/interfaces/IERC721Permit.sol"; /// @title V3 to V4 Migrator /// @notice A contract that migrates liquidity from PancakeSwap V3 to V4 @@ -20,19 +20,56 @@ abstract contract V3ToV4Migrator is RouterImmutables { error OnlyAddLiqudityAllowed(); /// @dev validate if an action is decreaseLiquidity, collect, or burn - function isValidAction(bytes4 selector) internal pure returns (bool) { + function _isValidAction(bytes4 selector) private pure returns (bool) { return selector == IV3NonfungiblePositionManager.decreaseLiquidity.selector || selector == IV3NonfungiblePositionManager.collect.selector || selector == IV3NonfungiblePositionManager.burn.selector; } /// @dev the caller is authorized for the token if its the owner, spender, or operator - function isAuthorizedForToken(address caller, uint256 tokenId) internal view returns (bool) { + function _isAuthorizedForToken(address caller, uint256 tokenId) private view returns (bool) { address owner = V3_POSITION_MANAGER.ownerOf(tokenId); return caller == owner || V3_POSITION_MANAGER.getApproved(tokenId) == caller || V3_POSITION_MANAGER.isApprovedForAll(owner, caller); } + /// @dev check that a call is to the ERC721 permit function + function _checkV3PermitCall(bytes calldata inputs) internal pure { + bytes4 selector; + assembly { + selector := calldataload(inputs.offset) + } + + if (selector != IERC721Permit.permit.selector) { + revert InvalidAction(selector); + } + } + + /// @dev check that the v3 position manager call is a safe call + function _checkV3PositionManagerCall(bytes calldata inputs, address caller) internal view { + bytes4 selector; + assembly { + selector := calldataload(inputs.offset) + } + + if (!_isValidAction(selector)) { + revert InvalidAction(selector); + } + + uint256 tokenId; + assembly { + // tokenId is always the first parameter in the valid actions + tokenId := calldataload(add(inputs.offset, 0x04)) + } + // If any other address that is not the owner wants to call this function, it also needs to be approved (in addition to this contract) + // This can be done in 2 ways: + // 1. This contract is permitted for the specific token and the caller is approved for ALL of the owner's tokens + // 2. This contract is permitted for ALL of the owner's tokens and the caller is permitted for the specific token + if (!_isAuthorizedForToken(caller, tokenId)) { + revert NotAuthorizedForToken(tokenId); + } + } + /// @dev check that the v4 position manager call is a safe call /// of the position-altering Actions, we only allow Actions.MINT /// this is because, if a user could be tricked into approving the UniversalRouter for