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] 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);