From 95c2ab403855e6657cd210b2373b256bb5d11689 Mon Sep 17 00:00:00 2001 From: chefburger Date: Mon, 14 Oct 2024 12:39:29 +0800 Subject: [PATCH 1/3] feat: add offchain clPosition descriptor --- ...Test#testCLMigrateFromV2IncludingInit.snap | 2 +- ...V2Test#testCLMigrateFromV2WithoutInit.snap | 2 +- ...testCLMigrateFromV2WithoutNativeToken.snap | 2 +- ...Test#testCLMigrateFromV3IncludingInit.snap | 2 +- ...V3Test#testCLMigrateFromV3WithoutInit.snap | 2 +- ...testCLMigrateFromV3WithoutNativeToken.snap | 2 +- ...Test#testCLMigrateFromV2IncludingInit.snap | 2 +- ...V2Test#testCLMigrateFromV2WithoutInit.snap | 2 +- ...testCLMigrateFromV2WithoutNativeToken.snap | 2 +- ...Test#testCLMigrateFromV3IncludingInit.snap | 2 +- ...V3Test#testCLMigrateFromV3WithoutInit.snap | 2 +- ...testCLMigrateFromV3WithoutNativeToken.snap | 2 +- .../CLPositionManager_burn_empty.snap | 2 +- .../CLPositionManager_burn_empty_native.snap | 2 +- ...anager_burn_nonEmpty_native_withClose.snap | 2 +- ...ger_burn_nonEmpty_native_withTakePair.snap | 2 +- ...sitionManager_burn_nonEmpty_withClose.snap | 2 +- ...ionManager_burn_nonEmpty_withTakePair.snap | 2 +- .../CLPositionManager_collect_native.snap | 2 +- .../CLPositionManager_collect_sameRange.snap | 2 +- .../CLPositionManager_collect_withClose.snap | 2 +- ...LPositionManager_collect_withTakePair.snap | 2 +- ...itionManager_decreaseLiquidity_native.snap | 2 +- ...onManager_decreaseLiquidity_withClose.snap | 2 +- ...anager_decreaseLiquidity_withTakePair.snap | 2 +- .../CLPositionManager_decrease_burnEmpty.snap | 2 +- ...tionManager_decrease_burnEmpty_native.snap | 2 +- ...nager_decrease_sameRange_allLiquidity.snap | 2 +- .../CLPositionManager_decrease_take_take.snap | 2 +- ...ger_increaseLiquidity_erc20_withClose.snap | 2 +- ...ncreaseLiquidity_erc20_withSettlePair.snap | 2 +- ...itionManager_increaseLiquidity_native.snap | 2 +- ...crease_autocompoundExactUnclaimedFees.snap | 2 +- ...increase_autocompoundExcessFeesCredit.snap | 2 +- ...ger_increase_autocompound_clearExcess.snap | 2 +- .../CLPositionManager_mint_native.snap | 2 +- ...anager_mint_nativeWithSweep_withClose.snap | 2 +- ...r_mint_nativeWithSweep_withSettlePair.snap | 2 +- ...LPositionManager_mint_onSameTickLower.snap | 2 +- ...LPositionManager_mint_onSameTickUpper.snap | 2 +- .../CLPositionManager_mint_sameRange.snap | 2 +- ...nManager_mint_settleWithBalance_sweep.snap | 2 +- ...anager_mint_warmedPool_differentRange.snap | 2 +- .../CLPositionManager_mint_withClose.snap | 2 +- ...CLPositionManager_mint_withSettlePair.snap | 2 +- ...tionManager_multicall_initialize_mint.snap | 2 +- .../CLPositionManager_unsubscribe.snap | 2 +- ...1_DeployCLPositionDescriptorOffchain.s.sol | 25 ++++++++ ...s.sol => 02_DeployCLPositionManager.s.sol} | 12 +++- ....sol => 03_DeployBinPositionManager.s.sol} | 2 +- ...CLQuoter.s.sol => 04_DeployCLQuoter.s.sol} | 2 +- ...nQuoter.s.sol => 05_DeployBinQuoter.s.sol} | 2 +- ...grator.s.sol => 06_DeployCLMigrator.s.sol} | 2 +- ...rator.s.sol => 07_DeployBinMigrator.s.sol} | 2 +- ...uoter.s.sol => 08_DeployMixedQuoter.s.sol} | 2 +- script/config/bsc-testnet.json | 1 + script/config/ethereum-mainnet.json | 1 + script/config/ethereum-sepolia.json | 1 + src/pool-cl/CLPositionDescriptorOffChain.sol | 28 +++++++++ src/pool-cl/CLPositionManager.sol | 16 ++++- src/pool-cl/base/ERC721Permit_v4.sol | 5 -- .../interfaces/ICLPositionDescriptor.sol | 14 +++++ .../CLPositionDescriptorOffChain.t.sol | 62 +++++++++++++++++++ test/pool-cl/mocks/MockERC721Permit.sol | 4 ++ .../position-managers/CLPositionManager.t.sol | 31 ++++++++++ test/pool-cl/shared/CLLiquidityOperations.sol | 2 + test/pool-cl/shared/PosmTestSetup.sol | 5 +- 67 files changed, 251 insertions(+), 62 deletions(-) create mode 100644 script/01_DeployCLPositionDescriptorOffchain.s.sol rename script/{01_DeployCLPositionManager.s.sol => 02_DeployCLPositionManager.s.sol} (72%) rename script/{02_DeployBinPositionManager.s.sol => 03_DeployBinPositionManager.s.sol} (95%) rename script/{03_DeployCLQuoter.s.sol => 04_DeployCLQuoter.s.sol} (92%) rename script/{04_DeployBinQuoter.s.sol => 05_DeployBinQuoter.s.sol} (92%) rename script/{05_DeployCLMigrator.s.sol => 06_DeployCLMigrator.s.sol} (94%) rename script/{06_DeployBinMigrator.s.sol => 07_DeployBinMigrator.s.sol} (95%) rename script/{07_DeployMixedQuoter.s.sol => 08_DeployMixedQuoter.s.sol} (96%) create mode 100644 src/pool-cl/CLPositionDescriptorOffChain.sol create mode 100644 src/pool-cl/interfaces/ICLPositionDescriptor.sol create mode 100644 test/pool-cl/CLPositionDescriptorOffChain.t.sol diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2IncludingInit.snap b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2IncludingInit.snap index 0e90938..771c774 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2IncludingInit.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2IncludingInit.snap @@ -1 +1 @@ -743154 \ No newline at end of file +743226 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2WithoutInit.snap b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2WithoutInit.snap index 0d93e99..f88dc65 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2WithoutInit.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2WithoutInit.snap @@ -1 +1 @@ -628697 \ No newline at end of file +628769 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2WithoutNativeToken.snap b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2WithoutNativeToken.snap index 5fbd628..36ede60 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2WithoutNativeToken.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testCLMigrateFromV2WithoutNativeToken.snap @@ -1 +1 @@ -700310 \ No newline at end of file +700382 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3IncludingInit.snap b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3IncludingInit.snap index 548d04b..759440f 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3IncludingInit.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3IncludingInit.snap @@ -1 +1 @@ -793013 \ No newline at end of file +793085 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3WithoutInit.snap b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3WithoutInit.snap index 6f1ad1d..eefb18c 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3WithoutInit.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3WithoutInit.snap @@ -1 +1 @@ -681111 \ No newline at end of file +681183 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3WithoutNativeToken.snap b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3WithoutNativeToken.snap index 30b70e1..7c3231b 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3WithoutNativeToken.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testCLMigrateFromV3WithoutNativeToken.snap @@ -1 +1 @@ -750212 \ No newline at end of file +750284 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2IncludingInit.snap b/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2IncludingInit.snap index d9c99f3..53d2934 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2IncludingInit.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2IncludingInit.snap @@ -1 +1 @@ -743166 \ No newline at end of file +743238 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2WithoutInit.snap b/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2WithoutInit.snap index a6f13cf..a8fda99 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2WithoutInit.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2WithoutInit.snap @@ -1 +1 @@ -628709 \ No newline at end of file +628781 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2WithoutNativeToken.snap b/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2WithoutNativeToken.snap index 5fbd628..36ede60 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2WithoutNativeToken.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV2Test#testCLMigrateFromV2WithoutNativeToken.snap @@ -1 +1 @@ -700310 \ No newline at end of file +700382 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3IncludingInit.snap b/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3IncludingInit.snap index 5117c29..3944497 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3IncludingInit.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3IncludingInit.snap @@ -1 +1 @@ -790995 \ No newline at end of file +791067 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3WithoutInit.snap b/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3WithoutInit.snap index 4438117..231c2b1 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3WithoutInit.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3WithoutInit.snap @@ -1 +1 @@ -679093 \ No newline at end of file +679165 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3WithoutNativeToken.snap b/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3WithoutNativeToken.snap index 105e2e4..84fb437 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3WithoutNativeToken.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV3Test#testCLMigrateFromV3WithoutNativeToken.snap @@ -1 +1 @@ -748194 \ No newline at end of file +748266 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_burn_empty.snap b/.forge-snapshots/CLPositionManager_burn_empty.snap index 2083dfc..9b31a82 100644 --- a/.forge-snapshots/CLPositionManager_burn_empty.snap +++ b/.forge-snapshots/CLPositionManager_burn_empty.snap @@ -1 +1 @@ -60159 \ No newline at end of file +60231 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_burn_empty_native.snap b/.forge-snapshots/CLPositionManager_burn_empty_native.snap index 2083dfc..9b31a82 100644 --- a/.forge-snapshots/CLPositionManager_burn_empty_native.snap +++ b/.forge-snapshots/CLPositionManager_burn_empty_native.snap @@ -1 +1 @@ -60159 \ No newline at end of file +60231 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_burn_nonEmpty_native_withClose.snap b/.forge-snapshots/CLPositionManager_burn_nonEmpty_native_withClose.snap index 73a08b9..f313e5e 100644 --- a/.forge-snapshots/CLPositionManager_burn_nonEmpty_native_withClose.snap +++ b/.forge-snapshots/CLPositionManager_burn_nonEmpty_native_withClose.snap @@ -1 +1 @@ -173520 \ No newline at end of file +173592 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_burn_nonEmpty_native_withTakePair.snap b/.forge-snapshots/CLPositionManager_burn_nonEmpty_native_withTakePair.snap index fd52093..59f9456 100644 --- a/.forge-snapshots/CLPositionManager_burn_nonEmpty_native_withTakePair.snap +++ b/.forge-snapshots/CLPositionManager_burn_nonEmpty_native_withTakePair.snap @@ -1 +1 @@ -172835 \ No newline at end of file +172907 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_burn_nonEmpty_withClose.snap b/.forge-snapshots/CLPositionManager_burn_nonEmpty_withClose.snap index 2139c89..4ad211f 100644 --- a/.forge-snapshots/CLPositionManager_burn_nonEmpty_withClose.snap +++ b/.forge-snapshots/CLPositionManager_burn_nonEmpty_withClose.snap @@ -1 +1 @@ -182099 \ No newline at end of file +182171 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_burn_nonEmpty_withTakePair.snap b/.forge-snapshots/CLPositionManager_burn_nonEmpty_withTakePair.snap index 2520ad2..7c890ed 100644 --- a/.forge-snapshots/CLPositionManager_burn_nonEmpty_withTakePair.snap +++ b/.forge-snapshots/CLPositionManager_burn_nonEmpty_withTakePair.snap @@ -1 +1 @@ -181414 \ No newline at end of file +181486 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_collect_native.snap b/.forge-snapshots/CLPositionManager_collect_native.snap index 17d6176..3a21e28 100644 --- a/.forge-snapshots/CLPositionManager_collect_native.snap +++ b/.forge-snapshots/CLPositionManager_collect_native.snap @@ -1 +1 @@ -205047 \ No newline at end of file +205119 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_collect_sameRange.snap b/.forge-snapshots/CLPositionManager_collect_sameRange.snap index f627a84..ca1bebd 100644 --- a/.forge-snapshots/CLPositionManager_collect_sameRange.snap +++ b/.forge-snapshots/CLPositionManager_collect_sameRange.snap @@ -1 +1 @@ -213626 \ No newline at end of file +213698 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_collect_withClose.snap b/.forge-snapshots/CLPositionManager_collect_withClose.snap index f627a84..ca1bebd 100644 --- a/.forge-snapshots/CLPositionManager_collect_withClose.snap +++ b/.forge-snapshots/CLPositionManager_collect_withClose.snap @@ -1 +1 @@ -213626 \ No newline at end of file +213698 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_collect_withTakePair.snap b/.forge-snapshots/CLPositionManager_collect_withTakePair.snap index b4dbf0f..661c583 100644 --- a/.forge-snapshots/CLPositionManager_collect_withTakePair.snap +++ b/.forge-snapshots/CLPositionManager_collect_withTakePair.snap @@ -1 +1 @@ -212953 \ No newline at end of file +213025 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_decreaseLiquidity_native.snap b/.forge-snapshots/CLPositionManager_decreaseLiquidity_native.snap index f55fd26..d1f5110 100644 --- a/.forge-snapshots/CLPositionManager_decreaseLiquidity_native.snap +++ b/.forge-snapshots/CLPositionManager_decreaseLiquidity_native.snap @@ -1 +1 @@ -170330 \ No newline at end of file +170402 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_decreaseLiquidity_withClose.snap b/.forge-snapshots/CLPositionManager_decreaseLiquidity_withClose.snap index fe7fb9a..bca2877 100644 --- a/.forge-snapshots/CLPositionManager_decreaseLiquidity_withClose.snap +++ b/.forge-snapshots/CLPositionManager_decreaseLiquidity_withClose.snap @@ -1 +1 @@ -178909 \ No newline at end of file +178981 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_decreaseLiquidity_withTakePair.snap b/.forge-snapshots/CLPositionManager_decreaseLiquidity_withTakePair.snap index 656f627..f73530f 100644 --- a/.forge-snapshots/CLPositionManager_decreaseLiquidity_withTakePair.snap +++ b/.forge-snapshots/CLPositionManager_decreaseLiquidity_withTakePair.snap @@ -1 +1 @@ -178236 \ No newline at end of file +178308 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_decrease_burnEmpty.snap b/.forge-snapshots/CLPositionManager_decrease_burnEmpty.snap index e4eeb51..af123ec 100644 --- a/.forge-snapshots/CLPositionManager_decrease_burnEmpty.snap +++ b/.forge-snapshots/CLPositionManager_decrease_burnEmpty.snap @@ -1 +1 @@ -185782 \ No newline at end of file +185854 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_decrease_burnEmpty_native.snap b/.forge-snapshots/CLPositionManager_decrease_burnEmpty_native.snap index 5410853..8a67eca 100644 --- a/.forge-snapshots/CLPositionManager_decrease_burnEmpty_native.snap +++ b/.forge-snapshots/CLPositionManager_decrease_burnEmpty_native.snap @@ -1 +1 @@ -177203 \ No newline at end of file +177275 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_decrease_sameRange_allLiquidity.snap b/.forge-snapshots/CLPositionManager_decrease_sameRange_allLiquidity.snap index 5aeba74..519df12 100644 --- a/.forge-snapshots/CLPositionManager_decrease_sameRange_allLiquidity.snap +++ b/.forge-snapshots/CLPositionManager_decrease_sameRange_allLiquidity.snap @@ -1 +1 @@ -191534 \ No newline at end of file +191606 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_decrease_take_take.snap b/.forge-snapshots/CLPositionManager_decrease_take_take.snap index 6eae5b4..c8e5b42 100644 --- a/.forge-snapshots/CLPositionManager_decrease_take_take.snap +++ b/.forge-snapshots/CLPositionManager_decrease_take_take.snap @@ -1 +1 @@ -179502 \ No newline at end of file +179574 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_increaseLiquidity_erc20_withClose.snap b/.forge-snapshots/CLPositionManager_increaseLiquidity_erc20_withClose.snap index 5ce8a91..bb01612 100644 --- a/.forge-snapshots/CLPositionManager_increaseLiquidity_erc20_withClose.snap +++ b/.forge-snapshots/CLPositionManager_increaseLiquidity_erc20_withClose.snap @@ -1 +1 @@ -220439 \ No newline at end of file +220511 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_increaseLiquidity_erc20_withSettlePair.snap b/.forge-snapshots/CLPositionManager_increaseLiquidity_erc20_withSettlePair.snap index a796468..0ed9d55 100644 --- a/.forge-snapshots/CLPositionManager_increaseLiquidity_erc20_withSettlePair.snap +++ b/.forge-snapshots/CLPositionManager_increaseLiquidity_erc20_withSettlePair.snap @@ -1 +1 @@ -219406 \ No newline at end of file +219478 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_increaseLiquidity_native.snap b/.forge-snapshots/CLPositionManager_increaseLiquidity_native.snap index 7a8a38c..973ff15 100644 --- a/.forge-snapshots/CLPositionManager_increaseLiquidity_native.snap +++ b/.forge-snapshots/CLPositionManager_increaseLiquidity_native.snap @@ -1 +1 @@ -202317 \ No newline at end of file +202389 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_increase_autocompoundExactUnclaimedFees.snap b/.forge-snapshots/CLPositionManager_increase_autocompoundExactUnclaimedFees.snap index 54d174d..563321e 100644 --- a/.forge-snapshots/CLPositionManager_increase_autocompoundExactUnclaimedFees.snap +++ b/.forge-snapshots/CLPositionManager_increase_autocompoundExactUnclaimedFees.snap @@ -1 +1 @@ -165325 \ No newline at end of file +165397 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_increase_autocompoundExcessFeesCredit.snap b/.forge-snapshots/CLPositionManager_increase_autocompoundExcessFeesCredit.snap index c5178dc..6de237e 100644 --- a/.forge-snapshots/CLPositionManager_increase_autocompoundExcessFeesCredit.snap +++ b/.forge-snapshots/CLPositionManager_increase_autocompoundExcessFeesCredit.snap @@ -1 +1 @@ -238620 \ No newline at end of file +238692 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_increase_autocompound_clearExcess.snap b/.forge-snapshots/CLPositionManager_increase_autocompound_clearExcess.snap index 029b182..46ca0ea 100644 --- a/.forge-snapshots/CLPositionManager_increase_autocompound_clearExcess.snap +++ b/.forge-snapshots/CLPositionManager_increase_autocompound_clearExcess.snap @@ -1 +1 @@ -209378 \ No newline at end of file +209450 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_native.snap b/.forge-snapshots/CLPositionManager_mint_native.snap index 3094ae5..96735b5 100644 --- a/.forge-snapshots/CLPositionManager_mint_native.snap +++ b/.forge-snapshots/CLPositionManager_mint_native.snap @@ -1 +1 @@ -538507 \ No newline at end of file +538579 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_nativeWithSweep_withClose.snap b/.forge-snapshots/CLPositionManager_mint_nativeWithSweep_withClose.snap index 5e57e8e..251ec68 100644 --- a/.forge-snapshots/CLPositionManager_mint_nativeWithSweep_withClose.snap +++ b/.forge-snapshots/CLPositionManager_mint_nativeWithSweep_withClose.snap @@ -1 +1 @@ -547036 \ No newline at end of file +547108 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_nativeWithSweep_withSettlePair.snap b/.forge-snapshots/CLPositionManager_mint_nativeWithSweep_withSettlePair.snap index 0a6adf9..2c30bf6 100644 --- a/.forge-snapshots/CLPositionManager_mint_nativeWithSweep_withSettlePair.snap +++ b/.forge-snapshots/CLPositionManager_mint_nativeWithSweep_withSettlePair.snap @@ -1 +1 @@ -546287 \ No newline at end of file +546359 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_onSameTickLower.snap b/.forge-snapshots/CLPositionManager_mint_onSameTickLower.snap index b74f88f..d9e9868 100644 --- a/.forge-snapshots/CLPositionManager_mint_onSameTickLower.snap +++ b/.forge-snapshots/CLPositionManager_mint_onSameTickLower.snap @@ -1 +1 @@ -393425 \ No newline at end of file +393497 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_onSameTickUpper.snap b/.forge-snapshots/CLPositionManager_mint_onSameTickUpper.snap index 862d104..b03b765 100644 --- a/.forge-snapshots/CLPositionManager_mint_onSameTickUpper.snap +++ b/.forge-snapshots/CLPositionManager_mint_onSameTickUpper.snap @@ -1 +1 @@ -393862 \ No newline at end of file +393934 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_sameRange.snap b/.forge-snapshots/CLPositionManager_mint_sameRange.snap index b3a41e5..829917a 100644 --- a/.forge-snapshots/CLPositionManager_mint_sameRange.snap +++ b/.forge-snapshots/CLPositionManager_mint_sameRange.snap @@ -1 +1 @@ -302650 \ No newline at end of file +302722 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_settleWithBalance_sweep.snap b/.forge-snapshots/CLPositionManager_mint_settleWithBalance_sweep.snap index 8d19861..f87a965 100644 --- a/.forge-snapshots/CLPositionManager_mint_settleWithBalance_sweep.snap +++ b/.forge-snapshots/CLPositionManager_mint_settleWithBalance_sweep.snap @@ -1 +1 @@ -592719 \ No newline at end of file +592791 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_warmedPool_differentRange.snap b/.forge-snapshots/CLPositionManager_mint_warmedPool_differentRange.snap index dd2aed1..d0180d7 100644 --- a/.forge-snapshots/CLPositionManager_mint_warmedPool_differentRange.snap +++ b/.forge-snapshots/CLPositionManager_mint_warmedPool_differentRange.snap @@ -1 +1 @@ -399137 \ No newline at end of file +399209 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_withClose.snap b/.forge-snapshots/CLPositionManager_mint_withClose.snap index 6c38ea6..7390679 100644 --- a/.forge-snapshots/CLPositionManager_mint_withClose.snap +++ b/.forge-snapshots/CLPositionManager_mint_withClose.snap @@ -1 +1 @@ -593869 \ No newline at end of file +593941 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_mint_withSettlePair.snap b/.forge-snapshots/CLPositionManager_mint_withSettlePair.snap index 5f65dbc..e793a8c 100644 --- a/.forge-snapshots/CLPositionManager_mint_withSettlePair.snap +++ b/.forge-snapshots/CLPositionManager_mint_withSettlePair.snap @@ -1 +1 @@ -592978 \ No newline at end of file +593050 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_multicall_initialize_mint.snap b/.forge-snapshots/CLPositionManager_multicall_initialize_mint.snap index 7567852..da524d6 100644 --- a/.forge-snapshots/CLPositionManager_multicall_initialize_mint.snap +++ b/.forge-snapshots/CLPositionManager_multicall_initialize_mint.snap @@ -1 +1 @@ -652835 \ No newline at end of file +652929 \ No newline at end of file diff --git a/.forge-snapshots/CLPositionManager_unsubscribe.snap b/.forge-snapshots/CLPositionManager_unsubscribe.snap index 5ac69c2..f49ce0b 100644 --- a/.forge-snapshots/CLPositionManager_unsubscribe.snap +++ b/.forge-snapshots/CLPositionManager_unsubscribe.snap @@ -1 +1 @@ -59181 \ No newline at end of file +59203 \ No newline at end of file diff --git a/script/01_DeployCLPositionDescriptorOffchain.s.sol b/script/01_DeployCLPositionDescriptorOffchain.s.sol new file mode 100644 index 0000000..b348c26 --- /dev/null +++ b/script/01_DeployCLPositionDescriptorOffchain.s.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import "forge-std/Script.sol"; +import {BaseScript} from "./BaseScript.sol"; +import {CLPositionDescriptorOffChain} from "../src/pool-cl/CLPositionDescriptorOffChain.sol"; + +/** + * forge script --sig 'run(uint256)' script/01_DeployCLPositionDescriptor.s.sol:DeployCLPositionDescriptorOffChainScript -vvv \ + * --rpc-url $RPC_URL \ + * --broadcast \ + * --slow \ + * --verify + */ +contract DeployCLPositionDescriptorOffChainScript is BaseScript { + function run(string memory baseTokenURI) public { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + vm.startBroadcast(deployerPrivateKey); + + CLPositionDescriptorOffChain clPositionDescriptor = new CLPositionDescriptorOffChain(baseTokenURI); + emit log_named_address("CLPositionDescriptorOffChain", address(clPositionDescriptor)); + + vm.stopBroadcast(); + } +} diff --git a/script/01_DeployCLPositionManager.s.sol b/script/02_DeployCLPositionManager.s.sol similarity index 72% rename from script/01_DeployCLPositionManager.s.sol rename to script/02_DeployCLPositionManager.s.sol index 3429020..b715596 100644 --- a/script/01_DeployCLPositionManager.s.sol +++ b/script/02_DeployCLPositionManager.s.sol @@ -7,9 +7,10 @@ import {IVault} from "pancake-v4-core/src/interfaces/IVault.sol"; import {ICLPoolManager} from "pancake-v4-core/src/pool-cl/interfaces/ICLPoolManager.sol"; import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"; import {CLPositionManager} from "../src/pool-cl/CLPositionManager.sol"; +import {ICLPositionDescriptor} from "../src/pool-cl/interfaces/ICLPositionDescriptor.sol"; /** - * forge script --sig 'run(uint256)' script/01_DeployCLPositionManager.s.sol:DeployCLPositionManagerScript -vvv \ + * forge script --sig 'run(uint256)' script/02_DeployCLPositionManager.s.sol:DeployCLPositionManagerScript -vvv \ * --rpc-url $RPC_URL \ * --broadcast \ * --slow \ @@ -29,8 +30,15 @@ contract DeployCLPositionManagerScript is BaseScript { address permit2 = getAddressFromConfig("permit2"); emit log_named_address("Permit2", permit2); + address clPositionDescriptor = getAddressFromConfig("clPositionDescriptor"); + emit log_named_address("CLPositionDescriptor", clPositionDescriptor); + CLPositionManager clPositionManager = new CLPositionManager( - IVault(vault), ICLPoolManager(clPoolManager), IAllowanceTransfer(permit2), unsubscribeGasLimit + IVault(vault), + ICLPoolManager(clPoolManager), + IAllowanceTransfer(permit2), + unsubscribeGasLimit, + ICLPositionDescriptor(clPositionDescriptor) ); emit log_named_address("CLPositionManager", address(clPositionManager)); diff --git a/script/02_DeployBinPositionManager.s.sol b/script/03_DeployBinPositionManager.s.sol similarity index 95% rename from script/02_DeployBinPositionManager.s.sol rename to script/03_DeployBinPositionManager.s.sol index 7b1ac5c..578d867 100644 --- a/script/02_DeployBinPositionManager.s.sol +++ b/script/03_DeployBinPositionManager.s.sol @@ -9,7 +9,7 @@ import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol" import {BinPositionManager} from "../src/pool-bin/BinPositionManager.sol"; /** - * forge script script/02_DeployBinPositionManager.s.sol:DeployBinPositionManagerScript -vvv \ + * forge script script/03_DeployBinPositionManager.s.sol:DeployBinPositionManagerScript -vvv \ * --rpc-url $RPC_URL \ * --broadcast \ * --slow \ diff --git a/script/03_DeployCLQuoter.s.sol b/script/04_DeployCLQuoter.s.sol similarity index 92% rename from script/03_DeployCLQuoter.s.sol rename to script/04_DeployCLQuoter.s.sol index 7e42202..dad25f6 100644 --- a/script/03_DeployCLQuoter.s.sol +++ b/script/04_DeployCLQuoter.s.sol @@ -6,7 +6,7 @@ import {BaseScript} from "./BaseScript.sol"; import {CLQuoter} from "../src/pool-cl/lens/CLQuoter.sol"; /** - * forge script script/03_DeployCLQuoter.s.sol:DeployCLQuoterScript -vvv \ + * forge script script/04_DeployCLQuoter.s.sol:DeployCLQuoterScript -vvv \ * --rpc-url $RPC_URL \ * --broadcast \ * --slow \ diff --git a/script/04_DeployBinQuoter.s.sol b/script/05_DeployBinQuoter.s.sol similarity index 92% rename from script/04_DeployBinQuoter.s.sol rename to script/05_DeployBinQuoter.s.sol index daa3c0d..cae0c2a 100644 --- a/script/04_DeployBinQuoter.s.sol +++ b/script/05_DeployBinQuoter.s.sol @@ -6,7 +6,7 @@ import {BaseScript} from "./BaseScript.sol"; import {BinQuoter} from "../src/pool-bin/lens/BinQuoter.sol"; /** - * forge script script/04_DeployBinQuoter.s.sol:DeployBinQuoterScript -vvv \ + * forge script script/05_DeployBinQuoter.s.sol:DeployBinQuoterScript -vvv \ * --rpc-url $RPC_URL \ * --broadcast \ * --slow \ diff --git a/script/05_DeployCLMigrator.s.sol b/script/06_DeployCLMigrator.s.sol similarity index 94% rename from script/05_DeployCLMigrator.s.sol rename to script/06_DeployCLMigrator.s.sol index ec91921..09ebdcf 100644 --- a/script/05_DeployCLMigrator.s.sol +++ b/script/06_DeployCLMigrator.s.sol @@ -8,7 +8,7 @@ import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol" import {CLMigrator} from "../src/pool-cl/CLMigrator.sol"; /** - * forge script script/05_DeployCLMigrator.s.sol:DeployCLMigratorScript -vvv \ + * forge script script/06_DeployCLMigrator.s.sol:DeployCLMigratorScript -vvv \ * --rpc-url $RPC_URL \ * --broadcast \ * --slow \ diff --git a/script/06_DeployBinMigrator.s.sol b/script/07_DeployBinMigrator.s.sol similarity index 95% rename from script/06_DeployBinMigrator.s.sol rename to script/07_DeployBinMigrator.s.sol index 84408ec..18f32a3 100644 --- a/script/06_DeployBinMigrator.s.sol +++ b/script/07_DeployBinMigrator.s.sol @@ -8,7 +8,7 @@ import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol" import {BinMigrator} from "../src/pool-bin/BinMigrator.sol"; /** - * forge script script/06_DeployBinMigrator.s.sol:DeployBinMigratorScript -vvv \ + * forge script script/07_DeployBinMigrator.s.sol:DeployBinMigratorScript -vvv \ * --rpc-url $RPC_URL \ * --broadcast \ * --slow \ diff --git a/script/07_DeployMixedQuoter.s.sol b/script/08_DeployMixedQuoter.s.sol similarity index 96% rename from script/07_DeployMixedQuoter.s.sol rename to script/08_DeployMixedQuoter.s.sol index 12ab479..60ca2d6 100644 --- a/script/07_DeployMixedQuoter.s.sol +++ b/script/08_DeployMixedQuoter.s.sol @@ -8,7 +8,7 @@ import {ICLQuoter} from "../src/pool-cl/interfaces/ICLQuoter.sol"; import {MixedQuoter} from "../src/MixedQuoter.sol"; /** - * forge script script/07_DeployMixedQuoter.s.sol:DeployMixedQuoterScript -vvv \ + * forge script script/08_DeployMixedQuoter.s.sol:DeployMixedQuoterScript -vvv \ * --rpc-url $RPC_URL \ * --broadcast \ * --slow \ diff --git a/script/config/bsc-testnet.json b/script/config/bsc-testnet.json index 41da0f0..da003a7 100644 --- a/script/config/bsc-testnet.json +++ b/script/config/bsc-testnet.json @@ -4,6 +4,7 @@ "vault": "0x0a125Bb36e409957Ed951eF1FBe20e81D682EAb6", "clPoolManager": "0x26Ca53c8C5CE90E22aA1FadDA68AB9a08f7BA06f", "binPoolManager": "0x1DF0be383e9d17DA4448E57712849aBE5b3Fa33b", + "clPositionDescriptor": "0x", "clPositionManager": "0x095bd2cf90ef113aa8c53904cE54C17f4583046d", "binPositionManager": "0x26008c91a2D47147d6739db3fFd3598A27da859d", "clQuoter": "0xcC2d7c533444BE73F15de4C20F4a4773966989dC", diff --git a/script/config/ethereum-mainnet.json b/script/config/ethereum-mainnet.json index 70a26ff..e097eb6 100755 --- a/script/config/ethereum-mainnet.json +++ b/script/config/ethereum-mainnet.json @@ -4,6 +4,7 @@ "vault": "0x", "clPoolManager": "0x", "binPoolManager": "0x", + "clPositionDescriptor": "0x", "clPositionManager": "0x", "binPositionManager": "0x", "clQuoter:": "0x", diff --git a/script/config/ethereum-sepolia.json b/script/config/ethereum-sepolia.json index 9c31452..a81a917 100755 --- a/script/config/ethereum-sepolia.json +++ b/script/config/ethereum-sepolia.json @@ -4,6 +4,7 @@ "vault": "0x4670F769Daa625FF5F89719AE5295E9824f5805f", "clPoolManager": "0xD4EAc75ee0E76EAD6AC6995DF30CA14b38549682", "binPoolManager": "0x0Ca8430E263A098B998E47e0544C2C82B30CbDB1", + "clPositionDescriptor": "0x", "clPositionManager": "0x53C9802F47295979c0E154779eD10fa6af27D7cA", "binPositionManager": "0x21015eF9927e06b7Fc19D986A214e449Aa22FF7d", "clQuoter": "0x6B71bA938100FD313Be08E680639900E0cfE3d74", diff --git a/src/pool-cl/CLPositionDescriptorOffChain.sol b/src/pool-cl/CLPositionDescriptorOffChain.sol new file mode 100644 index 0000000..38f10cc --- /dev/null +++ b/src/pool-cl/CLPositionDescriptorOffChain.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) 2024 PancakeSwap +pragma solidity ^0.8.24; + +import {ICLPositionDescriptor} from "./interfaces/ICLPositionDescriptor.sol"; +import {ICLPositionManager} from "./interfaces/ICLPositionManager.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +/// @title Describes NFT token positions +contract CLPositionDescriptorOffChain is ICLPositionDescriptor, Ownable { + using Strings for uint256; + + string private _baseTokenURI; + + constructor(string memory baseTokenURI) Ownable(msg.sender) { + _baseTokenURI = baseTokenURI; + } + + function setBaseTokenURI(string memory baseTokenURI) external onlyOwner { + _baseTokenURI = baseTokenURI; + } + + /// @inheritdoc ICLPositionDescriptor + function tokenURI(ICLPositionManager, uint256 tokenId) external view override returns (string memory) { + return bytes(_baseTokenURI).length > 0 ? string(abi.encodePacked(_baseTokenURI, tokenId.toString())) : ""; + } +} diff --git a/src/pool-cl/CLPositionManager.sol b/src/pool-cl/CLPositionManager.sol index 4a5b868..60e0c62 100644 --- a/src/pool-cl/CLPositionManager.sol +++ b/src/pool-cl/CLPositionManager.sol @@ -27,6 +27,7 @@ import {Multicall_v4} from "../base/Multicall_v4.sol"; import {CLNotifier} from "./base/CLNotifier.sol"; import {CLPositionInfo, CLPositionInfoLibrary} from "./libraries/CLPositionInfoLibrary.sol"; import {ICLSubscriber} from "./interfaces/ICLSubscriber.sol"; +import {ICLPositionDescriptor} from "./interfaces/ICLPositionDescriptor.sol"; /// @title CLPositionManager /// @notice Contract for modifying liquidity for PCS v4 CL pools @@ -52,16 +53,25 @@ contract CLPositionManager is /// @dev The ID of the next token that will be minted. Skips 0 uint256 public nextTokenId = 1; + ICLPositionDescriptor public immutable tokenDescriptor; + mapping(uint256 tokenId => CLPositionInfo info) public positionInfo; mapping(bytes25 poolId => PoolKey poolKey) public poolKeys; - constructor(IVault _vault, ICLPoolManager _clPoolManager, IAllowanceTransfer _permit2, uint256 _unsubscribeGasLimit) + constructor( + IVault _vault, + ICLPoolManager _clPoolManager, + IAllowanceTransfer _permit2, + uint256 _unsubscribeGasLimit, + ICLPositionDescriptor _tokenDescriptor + ) BaseActionsRouter(_vault) Permit2Forwarder(_permit2) ERC721Permit_v4("Pancakeswap V4 Positions NFT", "PCS-V4-POSM") CLNotifier(_unsubscribeGasLimit) { clPoolManager = _clPoolManager; + tokenDescriptor = _tokenDescriptor; } /// @dev might be refactored to BasePositionManager later @@ -82,6 +92,10 @@ contract CLPositionManager is _; } + function tokenURI(uint256 tokenId) public view override returns (string memory) { + return ICLPositionDescriptor(tokenDescriptor).tokenURI(this, tokenId); + } + /// @inheritdoc ICLPositionManager function initializePool(PoolKey calldata key, uint160 sqrtPriceX96) external payable override returns (int24) { return clPoolManager.initialize(key, sqrtPriceX96); diff --git a/src/pool-cl/base/ERC721Permit_v4.sol b/src/pool-cl/base/ERC721Permit_v4.sol index 9cc8135..5331c6d 100644 --- a/src/pool-cl/base/ERC721Permit_v4.sol +++ b/src/pool-cl/base/ERC721Permit_v4.sol @@ -95,9 +95,4 @@ abstract contract ERC721Permit_v4 is ERC721, IERC721Permit_v4, EIP712_v4, Unorde return spender == ownerOf(tokenId) || getApproved[tokenId] == spender || isApprovedForAll[ownerOf(tokenId)][spender]; } - - // TODO: to be implemented after audits - function tokenURI(uint256) public pure override returns (string memory) { - return "https://example.com"; - } } diff --git a/src/pool-cl/interfaces/ICLPositionDescriptor.sol b/src/pool-cl/interfaces/ICLPositionDescriptor.sol new file mode 100644 index 0000000..8092ac3 --- /dev/null +++ b/src/pool-cl/interfaces/ICLPositionDescriptor.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.24; + +import {ICLPositionManager} from "./ICLPositionManager.sol"; + +/// @title Describes cl pool position NFT tokens via URI +interface ICLPositionDescriptor { + /// @notice Produces the URI describing a particular token ID + /// @dev Note this URI may be a data: URI with the JSON contents directly inlined + /// @param positionManager The position manager for which to describe the token + /// @param tokenId The ID of the token for which to produce a description, which may not be valid + /// @return The URI of the ERC721-compliant metadata + function tokenURI(ICLPositionManager positionManager, uint256 tokenId) external view returns (string memory); +} diff --git a/test/pool-cl/CLPositionDescriptorOffChain.t.sol b/test/pool-cl/CLPositionDescriptorOffChain.t.sol new file mode 100644 index 0000000..7698b01 --- /dev/null +++ b/test/pool-cl/CLPositionDescriptorOffChain.t.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {Test} from "forge-std/Test.sol"; +import {CLPositionDescriptorOffChain} from "../../src/pool-cl/CLPositionDescriptorOffChain.sol"; +import {ICLPositionManager} from "../../src/pool-cl/interfaces/ICLPositionManager.sol"; +import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +contract CLPositionDescriptorOffChainTest is Test { + CLPositionDescriptorOffChain clPositionDescriptorOffChain; + + function setUp() public { + clPositionDescriptorOffChain = + new CLPositionDescriptorOffChain("https://pancakeswap.finance/v4/pool-cl/positions/"); + } + + function testTokenURI() public view { + // tokenId=1 + string memory tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0)), 1); + assertEq(tokenURI, "https://pancakeswap.finance/v4/pool-cl/positions/1"); + + // tokenId=uint.max + tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0)), type(uint256).max); + assertEq( + tokenURI, + "https://pancakeswap.finance/v4/pool-cl/positions/115792089237316195423570985008687907853269984665640564039457584007913129639935" + ); + + // positionManager is not used + tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0x01)), 1); + assertEq(tokenURI, "https://pancakeswap.finance/v4/pool-cl/positions/1"); + } + + function testTokenURIFuzz(uint256 tokenId) public view { + string memory tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0)), tokenId); + assertEq( + tokenURI, string.concat("https://pancakeswap.finance/v4/pool-cl/positions/", Strings.toString(tokenId)) + ); + } + + function testSetBaseTokenURI() public { + clPositionDescriptorOffChain.setBaseTokenURI("https://pancakeswap.finance/swap/v4/pool-cl/positions/"); + string memory tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0)), 1); + assertEq(tokenURI, "https://pancakeswap.finance/swap/v4/pool-cl/positions/1"); + + clPositionDescriptorOffChain.setBaseTokenURI("https://pancakeswap.finance/"); + tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0)), 2); + assertEq(tokenURI, "https://pancakeswap.finance/2"); + + clPositionDescriptorOffChain.setBaseTokenURI(""); + tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0)), 3); + assertEq(tokenURI, ""); + } + + function testSetBaseTokenURI_NotOwner(address msgSender) public { + vm.assume(msgSender != address(this)); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, msgSender)); + vm.prank(msgSender); + clPositionDescriptorOffChain.setBaseTokenURI("whatever"); + } +} diff --git a/test/pool-cl/mocks/MockERC721Permit.sol b/test/pool-cl/mocks/MockERC721Permit.sol index 8c8a991..e240e10 100644 --- a/test/pool-cl/mocks/MockERC721Permit.sol +++ b/test/pool-cl/mocks/MockERC721Permit.sol @@ -12,4 +12,8 @@ contract MockERC721Permit is ERC721Permit_v4 { tokenId = ++lastTokenId; _mint(msg.sender, tokenId); } + + function tokenURI(uint256 tokenId) public pure override returns (string memory) { + return string(abi.encodePacked("https://example.com/token/", tokenId)); + } } diff --git a/test/pool-cl/position-managers/CLPositionManager.t.sol b/test/pool-cl/position-managers/CLPositionManager.t.sol index b0fdb3b..8c7ba2f 100644 --- a/test/pool-cl/position-managers/CLPositionManager.t.sol +++ b/test/pool-cl/position-managers/CLPositionManager.t.sol @@ -35,6 +35,7 @@ import {LiquidityFuzzers} from "../shared/fuzz/LiquidityFuzzers.sol"; import {BaseActionsRouter} from "../../../src/base/BaseActionsRouter.sol"; import {ReentrantToken} from "../mocks/ReentrantToken.sol"; import {ICLSubscriber} from "../../../src/pool-cl/interfaces/ICLSubscriber.sol"; +import {CLPositionDescriptorOffChain} from "../../../src/pool-cl/CLPositionDescriptorOffChain.sol"; contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { using FixedPointMathLib for uint256; @@ -66,6 +67,36 @@ contract PositionManagerTest is Test, PosmTestSetup, LiquidityFuzzers { approvePosmFor(alice); } + function test_tokenURI() public { + assertEq(lpm.tokenURI(1), "https://pancakeswap.finance/v4/pool-cl/positions/1"); + assertEq(lpm.tokenURI(10), "https://pancakeswap.finance/v4/pool-cl/positions/10"); + assertEq(lpm.tokenURI(2), "https://pancakeswap.finance/v4/pool-cl/positions/2"); + assertEq(lpm.tokenURI(20), "https://pancakeswap.finance/v4/pool-cl/positions/20"); + assertEq( + lpm.tokenURI(type(uint256).max), + "https://pancakeswap.finance/v4/pool-cl/positions/115792089237316195423570985008687907853269984665640564039457584007913129639935" + ); + + // update the base token URI to be empty + CLPositionDescriptorOffChain(address(positionDescriptor)).setBaseTokenURI(""); + assertEq(lpm.tokenURI(1), ""); + assertEq(lpm.tokenURI(10), ""); + assertEq(lpm.tokenURI(2), ""); + assertEq(lpm.tokenURI(20), ""); + assertEq(lpm.tokenURI(type(uint256).max), ""); + + // update to be ipfs base URI + CLPositionDescriptorOffChain(address(positionDescriptor)).setBaseTokenURI("ipfs://abcd/"); + assertEq(lpm.tokenURI(1), "ipfs://abcd/1"); + assertEq(lpm.tokenURI(10), "ipfs://abcd/10"); + assertEq(lpm.tokenURI(2), "ipfs://abcd/2"); + assertEq(lpm.tokenURI(20), "ipfs://abcd/20"); + assertEq( + lpm.tokenURI(type(uint256).max), + "ipfs://abcd/115792089237316195423570985008687907853269984665640564039457584007913129639935" + ); + } + function test_modifyLiquidities_reverts_deadlinePassed() public { bytes memory calls = getMintEncoded(key, 0, 60, 1e18, ActionConstants.MSG_SENDER, ""); diff --git a/test/pool-cl/shared/CLLiquidityOperations.sol b/test/pool-cl/shared/CLLiquidityOperations.sol index 7ae4580..d7a6794 100644 --- a/test/pool-cl/shared/CLLiquidityOperations.sol +++ b/test/pool-cl/shared/CLLiquidityOperations.sol @@ -13,12 +13,14 @@ import {CLPositionManager} from "../../../src/pool-cl/CLPositionManager.sol"; import {Actions} from "../../../src/libraries/Actions.sol"; import {Planner, Plan} from "../../../src/libraries/Planner.sol"; import {HookSavesDelta} from "./HookSavesDelta.sol"; +import {ICLPositionDescriptor} from "../../../src/pool-cl/interfaces/ICLPositionDescriptor.sol"; abstract contract CLLiquidityOperations is CommonBase { using Planner for Plan; using SafeCastTemp for uint256; CLPositionManager lpm; + ICLPositionDescriptor positionDescriptor; uint256 _deadline = block.timestamp + 1; diff --git a/test/pool-cl/shared/PosmTestSetup.sol b/test/pool-cl/shared/PosmTestSetup.sol index 97195ca..871225b 100644 --- a/test/pool-cl/shared/PosmTestSetup.sol +++ b/test/pool-cl/shared/PosmTestSetup.sol @@ -18,6 +18,8 @@ import {HookSavesDelta} from "./HookSavesDelta.sol"; import {HookModifyLiquidities} from "./HookModifyLiquidities.sol"; import {ERC721PermitHash} from "../../../src/pool-cl/libraries/ERC721PermitHash.sol"; import {CLPoolManagerRouter} from "pancake-v4-core/test/pool-cl/helpers/CLPoolManagerRouter.sol"; +import {ICLPositionDescriptor} from "../../../src/pool-cl/interfaces/ICLPositionDescriptor.sol"; +import {CLPositionDescriptorOffChain} from "../../../src/pool-cl/CLPositionDescriptorOffChain.sol"; /// @notice A shared test contract that wraps the v4-core deployers contract and exposes basic liquidity operations on posm. contract PosmTestSetup is Test, Deployers, DeployPermit2, CLLiquidityOperations { @@ -60,7 +62,8 @@ contract PosmTestSetup is Test, Deployers, DeployPermit2, CLLiquidityOperations function deployPosm(IVault vault, ICLPoolManager poolManager) internal { // We use deployPermit2() to prevent having to use via-ir in this repository. permit2 = IAllowanceTransfer(deployPermit2()); - lpm = new CLPositionManager(vault, poolManager, permit2, 100_000); + positionDescriptor = new CLPositionDescriptorOffChain("https://pancakeswap.finance/v4/pool-cl/positions/"); + lpm = new CLPositionManager(vault, poolManager, permit2, 100_000, positionDescriptor); } function seedBalance(address to) internal { From 890613193f8803ca170d11c807e4efc2ace1ea9f Mon Sep 17 00:00:00 2001 From: chefburger Date: Mon, 14 Oct 2024 16:12:23 +0800 Subject: [PATCH 2/3] chore: minor issues per comments --- script/01_DeployCLPositionDescriptorOffchain.s.sol | 2 +- src/pool-cl/CLPositionManager.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script/01_DeployCLPositionDescriptorOffchain.s.sol b/script/01_DeployCLPositionDescriptorOffchain.s.sol index b348c26..f91e2fb 100644 --- a/script/01_DeployCLPositionDescriptorOffchain.s.sol +++ b/script/01_DeployCLPositionDescriptorOffchain.s.sol @@ -6,7 +6,7 @@ import {BaseScript} from "./BaseScript.sol"; import {CLPositionDescriptorOffChain} from "../src/pool-cl/CLPositionDescriptorOffChain.sol"; /** - * forge script --sig 'run(uint256)' script/01_DeployCLPositionDescriptor.s.sol:DeployCLPositionDescriptorOffChainScript -vvv \ + * forge script --sig 'run(string)' script/01_DeployCLPositionDescriptorOffchain.s.sol:DeployCLPositionDescriptorOffChainScript -vvv \ * --rpc-url $RPC_URL \ * --broadcast \ * --slow \ diff --git a/src/pool-cl/CLPositionManager.sol b/src/pool-cl/CLPositionManager.sol index 60e0c62..ac28bd9 100644 --- a/src/pool-cl/CLPositionManager.sol +++ b/src/pool-cl/CLPositionManager.sol @@ -93,7 +93,7 @@ contract CLPositionManager is } function tokenURI(uint256 tokenId) public view override returns (string memory) { - return ICLPositionDescriptor(tokenDescriptor).tokenURI(this, tokenId); + return tokenDescriptor.tokenURI(this, tokenId); } /// @inheritdoc ICLPositionManager From ce81ac24b8b8a48495fc26c57e7fb8bcf2368f66 Mon Sep 17 00:00:00 2001 From: chefburger Date: Mon, 14 Oct 2024 16:37:21 +0800 Subject: [PATCH 3/3] feat: add TokenURIContract in case we want to update tokenURI logic --- src/pool-cl/CLPositionDescriptorOffChain.sol | 20 +++++++++++++- .../CLPositionDescriptorOffChain.t.sol | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/pool-cl/CLPositionDescriptorOffChain.sol b/src/pool-cl/CLPositionDescriptorOffChain.sol index 38f10cc..5c9f27d 100644 --- a/src/pool-cl/CLPositionDescriptorOffChain.sol +++ b/src/pool-cl/CLPositionDescriptorOffChain.sol @@ -13,6 +13,10 @@ contract CLPositionDescriptorOffChain is ICLPositionDescriptor, Ownable { string private _baseTokenURI; + /// @notice Just in case we want to upgrade the tokenURI generation logic + /// This defaults to address(0) but will be used if set + ICLPositionDescriptor public tokenURIContract; + constructor(string memory baseTokenURI) Ownable(msg.sender) { _baseTokenURI = baseTokenURI; } @@ -21,8 +25,22 @@ contract CLPositionDescriptorOffChain is ICLPositionDescriptor, Ownable { _baseTokenURI = baseTokenURI; } + function setTokenURIContract(ICLPositionDescriptor newTokenURIContract) external onlyOwner { + tokenURIContract = newTokenURIContract; + } + /// @inheritdoc ICLPositionDescriptor - function tokenURI(ICLPositionManager, uint256 tokenId) external view override returns (string memory) { + function tokenURI(ICLPositionManager positionManager, uint256 tokenId) + external + view + override + returns (string memory) + { + // if set, this will be used instead of _baseTokenURI + if (address(tokenURIContract) != address(0)) { + return tokenURIContract.tokenURI(positionManager, tokenId); + } + return bytes(_baseTokenURI).length > 0 ? string(abi.encodePacked(_baseTokenURI, tokenId.toString())) : ""; } } diff --git a/test/pool-cl/CLPositionDescriptorOffChain.t.sol b/test/pool-cl/CLPositionDescriptorOffChain.t.sol index 7698b01..4f85027 100644 --- a/test/pool-cl/CLPositionDescriptorOffChain.t.sol +++ b/test/pool-cl/CLPositionDescriptorOffChain.t.sol @@ -6,6 +6,18 @@ import {CLPositionDescriptorOffChain} from "../../src/pool-cl/CLPositionDescript import {ICLPositionManager} from "../../src/pool-cl/interfaces/ICLPositionManager.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {ICLPositionDescriptor} from "../../src/pool-cl/interfaces/ICLPositionDescriptor.sol"; + +contract FakeTokenURIContract is ICLPositionDescriptor { + function tokenURI(ICLPositionManager positionManager, uint256 tokenId) + external + pure + override + returns (string memory) + { + return string.concat(Strings.toString(uint160(address(positionManager))), "/", Strings.toString(tokenId)); + } +} contract CLPositionDescriptorOffChainTest is Test { CLPositionDescriptorOffChain clPositionDescriptorOffChain; @@ -32,6 +44,14 @@ contract CLPositionDescriptorOffChainTest is Test { assertEq(tokenURI, "https://pancakeswap.finance/v4/pool-cl/positions/1"); } + function testTokenURI_generateByTokenURIContract() public { + clPositionDescriptorOffChain.setTokenURIContract(new FakeTokenURIContract()); + + // tokenId=1 + string memory tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0x1234)), 1); + assertEq(tokenURI, "4660/1"); + } + function testTokenURIFuzz(uint256 tokenId) public view { string memory tokenURI = clPositionDescriptorOffChain.tokenURI(ICLPositionManager(address(0)), tokenId); assertEq( @@ -59,4 +79,11 @@ contract CLPositionDescriptorOffChainTest is Test { vm.prank(msgSender); clPositionDescriptorOffChain.setBaseTokenURI("whatever"); } + + function testSetTokenURIContract_NotOwner(address msgSender) public { + vm.assume(msgSender != address(this)); + vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, msgSender)); + vm.prank(msgSender); + clPositionDescriptorOffChain.setTokenURIContract(ICLPositionDescriptor(address(0x01))); + } }