From 2ecd6252d2782f29bec85a6fd9d8780460af9d18 Mon Sep 17 00:00:00 2001 From: Chef Snoopy <91462681+ChefSnoopy@users.noreply.github.com> Date: Thu, 1 Aug 2024 16:12:20 +0800 Subject: [PATCH] Feat/support btach modifyliquidities for CL (#71) * feat: Add settleOrTake for optimization NonfungiblePositionManager contract size optimization Before | NonfungiblePositionManager | 21,512 | 3,064 | After | NonfungiblePositionManager | 20,805 | 3,771 | And also gas optimization * feat: Support batch liquidity modification Now contact size excess limit. | NonfungiblePositionManager | 27,112 | -2,536 | So we need to choose one solution 1. Use modifyLiquidities to support batch liquidity modification 2. use current separate function * feat: Add more test cases * feat: Draft version one * feat: Format codes * feat: Remove deadline from liquidity parameters * feat: Remove comments * feat: Add more comments for modifyLiquidities payload * feat: Add TransactionTooOld test cases * feat: Add native token refund * feat: Update test cases * feat: Optimize gas * feat: Rename burnAndTake parameter name * feat: Rename variable * feat: Rename variables --- ...yTest#testAddLiquidityOutsideActiveId.snap | 1 - ...ddLiquidityOutsideActiveId_ExistingId.snap | 2 +- ...testAddLiquidityOutsideActiveId_NewId.snap | 2 +- ...dityTest#testAddLiquidityWithActiveId.snap | 2 +- ...testAddLiquidityWithActiveId_WithHook.snap | 2 +- ...yTest#testAddLiquidity_WithMasterChef.snap | 1 - ...dLiquidityTest#testAfterTokenTransfer.snap | 1 - ...oveLiquidityOutsideActiveId_ThreeBins.snap | 2 +- ...RemoveLiquidityWithActiveId_ThreeBins.snap | 2 +- ...iquidityTest#testRemoveLiquidity_Half.snap | 2 +- ...st#testRemoveLiquidity_WithMasterChef.snap | 1 - ...kenTest#testBatchTransferFrom_FromBob.snap | 2 +- ...nTest#testBatchTransferFrom_FromOwner.snap | 2 +- .../BinFungibleTokenTest#testBurn.snap | 2 +- .../BinFungibleTokenTest#testMint.snap | 2 +- ...V2Test#testMigrateFromV2IncludingInit.snap | 2 +- ...apV2Test#testMigrateFromV2WithoutInit.snap | 2 +- ...t#testMigrateFromV2WithoutNativeToken.snap | 2 +- ...V3Test#testMigrateFromV3IncludingInit.snap | 2 +- ...apV3Test#testMigrateFromV3WithoutInit.snap | 2 +- ...t#testMigrateFromV3WithoutNativeToken.snap | 2 +- ...V2Test#testMigrateFromV2IncludingInit.snap | 2 +- ...apV2Test#testMigrateFromV2WithoutInit.snap | 2 +- ...t#testMigrateFromV2WithoutNativeToken.snap | 2 +- ...V3Test#testMigrateFromV3IncludingInit.snap | 2 +- ...apV3Test#testMigrateFromV3WithoutInit.snap | 2 +- ...t#testMigrateFromV3WithoutNativeToken.snap | 2 +- ...Test#testQuoter_quoteExactInputSingle.snap | 2 +- ...t#testQuoter_quoteExactInput_MultiHop.snap | 2 +- ...#testQuoter_quoteExactInput_SingleHop.snap | 2 +- ...est#testQuoter_quoteExactOutputSingle.snap | 2 +- ...#testQuoter_quoteExactOutput_MultiHop.snap | 2 +- ...testQuoter_quoteExactOutput_SingleHop.snap | 2 +- ...stExactInputSingle_DifferentRecipient.snap | 2 +- ...uterTest#testExactInputSingle_EthPool.snap | 1 - ...ctInputSingle_EthPool_SwapEthForToken.snap | 2 +- ...ctInputSingle_EthPool_SwapTokenForEth.snap | 2 +- ...terTest#testExactInputSingle_SwapForY.snap | 1 - ...rTest#testExactInputSingle_SwapForY_1.snap | 2 +- ...rTest#testExactInputSingle_SwapForY_2.snap | 2 +- ...ExactInput_MultiHopDifferentRecipient.snap | 2 +- ...tExactOutputSingle_DifferentRecipient.snap | 2 +- ...Test#testExactOutputSingle_SwapForY_1.snap | 2 +- ...Test#testExactOutputSingle_SwapForY_2.snap | 2 +- ...apRouterTest#testExactOutput_MultiHop.snap | 1 - ...xactOutput_MultiHopDifferentRecipient.snap | 2 +- ...pRouterTest#testExactOutput_SingleHop.snap | 2 +- ...V2Test#testMigrateFromV2IncludingInit.snap | 2 +- ...apV2Test#testMigrateFromV2WithoutInit.snap | 2 +- ...t#testMigrateFromV2WithoutNativeToken.snap | 2 +- ...V3Test#testMigrateFromV3IncludingInit.snap | 2 +- ...apV3Test#testMigrateFromV3WithoutInit.snap | 2 +- ...t#testMigrateFromV3WithoutNativeToken.snap | 2 +- ...V2Test#testMigrateFromV2IncludingInit.snap | 2 +- ...apV2Test#testMigrateFromV2WithoutInit.snap | 2 +- ...t#testMigrateFromV2WithoutNativeToken.snap | 2 +- ...V3Test#testMigrateFromV3IncludingInit.snap | 2 +- ...apV3Test#testMigrateFromV3WithoutInit.snap | 2 +- ...t#testMigrateFromV3WithoutNativeToken.snap | 2 +- .../CLSwapRouterTest#ExactInput.snap | 2 +- .../CLSwapRouterTest#ExactInputSingle.snap | 2 +- .../CLSwapRouterTest#ExactOutput.snap | 2 +- .../CLSwapRouterTest#ExactOutputSingle.snap | 2 +- ...erBatch#BatchMintAndIncreaseLiquidity.snap | 1 + ...batchMintIncreaseAndDecreaseLiquidity.snap | 1 + ...nFungiblePositionManagerBatch#collect.snap | 1 + ...agerBatch#collectWithoutCloseCurrency.snap | 1 + ...ositionManagerBatch#decreaseLiquidity.snap | 1 + ...decreaseLiquidityWithoutCloseCurrency.snap | 1 + ...ositionManagerBatch#increaseLiquidity.snap | 1 + ...increaseLiquidityWithoutCloseCurrency.snap | 1 + .../NonFungiblePositionManagerBatch#mint.snap | 1 + ...ManagerBatch#mintWithoutCloseCurrency.snap | 1 + .../NonfungiblePositionManager#burn.snap | 2 +- .../NonfungiblePositionManager#collect.snap | 2 +- ...iblePositionManager#decreaseLiquidity.snap | 2 +- ...iblePositionManager#increaseLiquidity.snap | 2 +- .../NonfungiblePositionManager#mint.snap | 2 +- foundry.toml | 2 +- src/pool-cl/CLMigrator.sol | 31 +- src/pool-cl/NonfungiblePositionManager.sol | 241 +-- src/pool-cl/base/LiquidityManagement.sol | 37 +- .../INonfungiblePositionManager.sol | 69 +- test/pool-cl/CLSwapRouterInvariant.t.sol | 41 +- test/pool-cl/NonFungiblePositionManager.t.sol | 1685 ++++++++++++++--- 85 files changed, 1706 insertions(+), 539 deletions(-) delete mode 100644 .forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId.snap delete mode 100644 .forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidity_WithMasterChef.snap delete mode 100644 .forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAfterTokenTransfer.snap delete mode 100644 .forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_WithMasterChef.snap delete mode 100644 .forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool.snap delete mode 100644 .forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY.snap delete mode 100644 .forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHop.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#BatchMintAndIncreaseLiquidity.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#batchMintIncreaseAndDecreaseLiquidity.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#collect.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#collectWithoutCloseCurrency.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#decreaseLiquidity.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#decreaseLiquidityWithoutCloseCurrency.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#increaseLiquidity.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#increaseLiquidityWithoutCloseCurrency.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#mint.snap create mode 100644 .forge-snapshots/NonFungiblePositionManagerBatch#mintWithoutCloseCurrency.snap diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId.snap deleted file mode 100644 index f6c3e03..0000000 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId.snap +++ /dev/null @@ -1 +0,0 @@ -511867 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_ExistingId.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_ExistingId.snap index 08291a9..e67a084 100644 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_ExistingId.snap +++ b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_ExistingId.snap @@ -1 +1 @@ -182560 \ No newline at end of file +180160 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_NewId.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_NewId.snap index b27573c..ddab7ba 100644 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_NewId.snap +++ b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityOutsideActiveId_NewId.snap @@ -1 +1 @@ -636579 \ No newline at end of file +634347 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId.snap index 909f1b6..10afbd6 100644 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId.snap +++ b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId.snap @@ -1 +1 @@ -904068 \ No newline at end of file +901398 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId_WithHook.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId_WithHook.snap index dffda17..20dab9e 100644 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId_WithHook.snap +++ b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidityWithActiveId_WithHook.snap @@ -1 +1 @@ -1270762 \ No newline at end of file +1267731 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidity_WithMasterChef.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidity_WithMasterChef.snap deleted file mode 100644 index be0a6c8..0000000 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAddLiquidity_WithMasterChef.snap +++ /dev/null @@ -1 +0,0 @@ -110700 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAfterTokenTransfer.snap b/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAfterTokenTransfer.snap deleted file mode 100644 index f7f0596..0000000 --- a/.forge-snapshots/BinFungiblePositionManager_AddLiquidityTest#testAfterTokenTransfer.snap +++ /dev/null @@ -1 +0,0 @@ -33703 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityOutsideActiveId_ThreeBins.snap b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityOutsideActiveId_ThreeBins.snap index 7e5695e..7d3e342 100644 --- a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityOutsideActiveId_ThreeBins.snap +++ b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityOutsideActiveId_ThreeBins.snap @@ -1 +1 @@ -147155 \ No newline at end of file +145917 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityWithActiveId_ThreeBins.snap b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityWithActiveId_ThreeBins.snap index 396e142..09cf27e 100644 --- a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityWithActiveId_ThreeBins.snap +++ b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidityWithActiveId_ThreeBins.snap @@ -1 +1 @@ -213706 \ No newline at end of file +212278 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_Half.snap b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_Half.snap index 36bfac9..c16fb44 100644 --- a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_Half.snap +++ b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_Half.snap @@ -1 +1 @@ -238930 \ No newline at end of file +237173 \ No newline at end of file diff --git a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_WithMasterChef.snap b/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_WithMasterChef.snap deleted file mode 100644 index be7abf2..0000000 --- a/.forge-snapshots/BinFungiblePositionManager_RemoveLiquidityTest#testRemoveLiquidity_WithMasterChef.snap +++ /dev/null @@ -1 +0,0 @@ -125474 \ No newline at end of file diff --git a/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromBob.snap b/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromBob.snap index 94e0022..013876b 100644 --- a/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromBob.snap +++ b/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromBob.snap @@ -1 +1 @@ -53942 \ No newline at end of file +53852 \ No newline at end of file diff --git a/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromOwner.snap b/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromOwner.snap index d48de5d..44acaf3 100644 --- a/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromOwner.snap +++ b/.forge-snapshots/BinFungibleTokenTest#testBatchTransferFrom_FromOwner.snap @@ -1 +1 @@ -53680 \ No newline at end of file +53602 \ No newline at end of file diff --git a/.forge-snapshots/BinFungibleTokenTest#testBurn.snap b/.forge-snapshots/BinFungibleTokenTest#testBurn.snap index c9ae354..da80a30 100644 --- a/.forge-snapshots/BinFungibleTokenTest#testBurn.snap +++ b/.forge-snapshots/BinFungibleTokenTest#testBurn.snap @@ -1 +1 @@ -26848 \ No newline at end of file +26823 \ No newline at end of file diff --git a/.forge-snapshots/BinFungibleTokenTest#testMint.snap b/.forge-snapshots/BinFungibleTokenTest#testMint.snap index ac934b2..63a6857 100644 --- a/.forge-snapshots/BinFungibleTokenTest#testMint.snap +++ b/.forge-snapshots/BinFungibleTokenTest#testMint.snap @@ -1 +1 @@ -73705 \ No newline at end of file +73681 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2IncludingInit.snap b/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2IncludingInit.snap index c5ce088..4cf2c96 100644 --- a/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2IncludingInit.snap +++ b/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2IncludingInit.snap @@ -1 +1 @@ -1017615 \ No newline at end of file +1014388 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutInit.snap b/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutInit.snap index bfa5bac..fe7df4b 100644 --- a/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutInit.snap +++ b/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutInit.snap @@ -1 +1 @@ -977598 \ No newline at end of file +974603 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutNativeToken.snap b/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutNativeToken.snap index e40db38..f2eb15e 100644 --- a/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutNativeToken.snap +++ b/.forge-snapshots/BinMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutNativeToken.snap @@ -1 +1 @@ -1022017 \ No newline at end of file +1018858 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3IncludingInit.snap b/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3IncludingInit.snap index 5a7fd6b..aa040fb 100644 --- a/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3IncludingInit.snap +++ b/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3IncludingInit.snap @@ -1 +1 @@ -1096580 \ No newline at end of file +1093305 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutInit.snap b/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutInit.snap index b4db3f3..e6425b8 100644 --- a/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutInit.snap +++ b/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutInit.snap @@ -1 +1 @@ -1056639 \ No newline at end of file +1053572 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutNativeToken.snap b/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutNativeToken.snap index 11bbe69..826bef2 100644 --- a/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutNativeToken.snap +++ b/.forge-snapshots/BinMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutNativeToken.snap @@ -1 +1 @@ -1094456 \ No newline at end of file +1091207 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2IncludingInit.snap b/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2IncludingInit.snap index 432348a..69d1d84 100644 --- a/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2IncludingInit.snap +++ b/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2IncludingInit.snap @@ -1 +1 @@ -1017627 \ No newline at end of file +1014400 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2WithoutInit.snap b/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2WithoutInit.snap index b5e449b..5a381e7 100644 --- a/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2WithoutInit.snap +++ b/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2WithoutInit.snap @@ -1 +1 @@ -977610 \ No newline at end of file +974615 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2WithoutNativeToken.snap b/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2WithoutNativeToken.snap index d35a8e0..1805b0c 100644 --- a/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2WithoutNativeToken.snap +++ b/.forge-snapshots/BinMigratorFromUniswapV2Test#testMigrateFromV2WithoutNativeToken.snap @@ -1 +1 @@ -1022014 \ No newline at end of file +1018855 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3IncludingInit.snap b/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3IncludingInit.snap index 5284644..138b605 100644 --- a/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3IncludingInit.snap +++ b/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3IncludingInit.snap @@ -1 +1 @@ -1094562 \ No newline at end of file +1091287 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3WithoutInit.snap b/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3WithoutInit.snap index 0798bc2..3ca4d06 100644 --- a/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3WithoutInit.snap +++ b/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3WithoutInit.snap @@ -1 +1 @@ -1054621 \ No newline at end of file +1051554 \ No newline at end of file diff --git a/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3WithoutNativeToken.snap b/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3WithoutNativeToken.snap index 2802d5f..3e4917f 100644 --- a/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3WithoutNativeToken.snap +++ b/.forge-snapshots/BinMigratorFromUniswapV3Test#testMigrateFromV3WithoutNativeToken.snap @@ -1 +1 @@ -1092434 \ No newline at end of file +1089185 \ No newline at end of file diff --git a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInputSingle.snap b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInputSingle.snap index 0886340..b54603c 100644 --- a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInputSingle.snap +++ b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInputSingle.snap @@ -1 +1 @@ -96000 \ No newline at end of file +95055 \ No newline at end of file diff --git a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInput_MultiHop.snap b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInput_MultiHop.snap index 4a699c1..3289f2e 100644 --- a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInput_MultiHop.snap +++ b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInput_MultiHop.snap @@ -1 +1 @@ -128542 \ No newline at end of file +126683 \ No newline at end of file diff --git a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInput_SingleHop.snap b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInput_SingleHop.snap index 3abca25..dff1f3d 100644 --- a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInput_SingleHop.snap +++ b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactInput_SingleHop.snap @@ -1 +1 @@ -90549 \ No newline at end of file +89469 \ No newline at end of file diff --git a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutputSingle.snap b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutputSingle.snap index eec80f2..1ac293d 100644 --- a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutputSingle.snap +++ b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutputSingle.snap @@ -1 +1 @@ -117963 \ No newline at end of file +116979 \ No newline at end of file diff --git a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutput_MultiHop.snap b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutput_MultiHop.snap index 21478d4..f9a9fa3 100644 --- a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutput_MultiHop.snap +++ b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutput_MultiHop.snap @@ -1 +1 @@ -170446 \ No newline at end of file +168539 \ No newline at end of file diff --git a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutput_SingleHop.snap b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutput_SingleHop.snap index 6f4226e..8f8f8e7 100644 --- a/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutput_SingleHop.snap +++ b/.forge-snapshots/BinQuoterTest#testQuoter_quoteExactOutput_SingleHop.snap @@ -1 +1 @@ -110528 \ No newline at end of file +109424 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_DifferentRecipient.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_DifferentRecipient.snap index 17dda44..9a481e4 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_DifferentRecipient.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_DifferentRecipient.snap @@ -1 +1 @@ -152204 \ No newline at end of file +150693 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool.snap deleted file mode 100644 index 4cd7112..0000000 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool.snap +++ /dev/null @@ -1 +0,0 @@ -202684 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapEthForToken.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapEthForToken.snap index c4d924a..bc86fa0 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapEthForToken.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapEthForToken.snap @@ -1 +1 @@ -143866 \ No newline at end of file +142495 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapTokenForEth.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapTokenForEth.snap index 9057e8c..18cdea6 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapTokenForEth.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_EthPool_SwapTokenForEth.snap @@ -1 +1 @@ -149552 \ No newline at end of file +148056 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY.snap deleted file mode 100644 index 469e689..0000000 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY.snap +++ /dev/null @@ -1 +0,0 @@ -202205 diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_1.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_1.snap index 7d0755e..0fa2b43 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_1.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_1.snap @@ -1 +1 @@ -150192 \ No newline at end of file +148681 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_2.snap b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_2.snap index 0ac9adb..7a318ce 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_2.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInputSingle_SwapForY_2.snap @@ -1 +1 @@ -150227 \ No newline at end of file +148707 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactInput_MultiHopDifferentRecipient.snap b/.forge-snapshots/BinSwapRouterTest#testExactInput_MultiHopDifferentRecipient.snap index 1af0770..c082eec 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactInput_MultiHopDifferentRecipient.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactInput_MultiHopDifferentRecipient.snap @@ -1 +1 @@ -175579 \ No newline at end of file +173408 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_DifferentRecipient.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_DifferentRecipient.snap index 240c109..ab27c01 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_DifferentRecipient.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_DifferentRecipient.snap @@ -1 +1 @@ -156380 \ No newline at end of file +154872 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_1.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_1.snap index da3a8e2..d2d2fbd 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_1.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_1.snap @@ -1 +1 @@ -154380 \ No newline at end of file +152872 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_2.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_2.snap index fdf8750..e686184 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_2.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutputSingle_SwapForY_2.snap @@ -1 +1 @@ -154370 \ No newline at end of file +152862 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHop.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHop.snap deleted file mode 100644 index d731dec..0000000 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHop.snap +++ /dev/null @@ -1 +0,0 @@ -264372 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHopDifferentRecipient.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHopDifferentRecipient.snap index 600a457..d81fe39 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHopDifferentRecipient.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutput_MultiHopDifferentRecipient.snap @@ -1 +1 @@ -177434 \ No newline at end of file +175293 \ No newline at end of file diff --git a/.forge-snapshots/BinSwapRouterTest#testExactOutput_SingleHop.snap b/.forge-snapshots/BinSwapRouterTest#testExactOutput_SingleHop.snap index f61c076..14561da 100644 --- a/.forge-snapshots/BinSwapRouterTest#testExactOutput_SingleHop.snap +++ b/.forge-snapshots/BinSwapRouterTest#testExactOutput_SingleHop.snap @@ -1 +1 @@ -146632 \ No newline at end of file +145038 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2IncludingInit.snap b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2IncludingInit.snap index 2fd85a1..4aac251 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2IncludingInit.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2IncludingInit.snap @@ -1 +1 @@ -736974 \ No newline at end of file +740743 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutInit.snap b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutInit.snap index 1c78123..22de1ac 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutInit.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutInit.snap @@ -1 +1 @@ -693861 \ No newline at end of file +697826 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutNativeToken.snap b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutNativeToken.snap index 577725f..5c22973 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutNativeToken.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV2Test#testMigrateFromV2WithoutNativeToken.snap @@ -1 +1 @@ -738290 \ No newline at end of file +741985 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3IncludingInit.snap b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3IncludingInit.snap index ca570ab..64eacca 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3IncludingInit.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3IncludingInit.snap @@ -1 +1 @@ -793398 \ No newline at end of file +797154 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutInit.snap b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutInit.snap index 01facce..c7734d7 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutInit.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutInit.snap @@ -1 +1 @@ -752832 \ No newline at end of file +756772 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutNativeToken.snap b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutNativeToken.snap index ffc5693..2b3667b 100644 --- a/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutNativeToken.snap +++ b/.forge-snapshots/CLMigratorFromPancakeswapV3Test#testMigrateFromV3WithoutNativeToken.snap @@ -1 +1 @@ -794750 \ No newline at end of file +798420 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2IncludingInit.snap b/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2IncludingInit.snap index 26cffde..6055f1d 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2IncludingInit.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2IncludingInit.snap @@ -1 +1 @@ -736986 \ No newline at end of file +740755 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2WithoutInit.snap b/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2WithoutInit.snap index f8e8bf1..5aef7b6 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2WithoutInit.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2WithoutInit.snap @@ -1 +1 @@ -693873 \ No newline at end of file +697838 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2WithoutNativeToken.snap b/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2WithoutNativeToken.snap index 7ab44b7..1af6144 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2WithoutNativeToken.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV2Test#testMigrateFromV2WithoutNativeToken.snap @@ -1 +1 @@ -738287 \ No newline at end of file +741982 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3IncludingInit.snap b/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3IncludingInit.snap index 473137d..c5fbc4d 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3IncludingInit.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3IncludingInit.snap @@ -1 +1 @@ -791380 \ No newline at end of file +795136 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3WithoutInit.snap b/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3WithoutInit.snap index 79eb1ec..e52439f 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3WithoutInit.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3WithoutInit.snap @@ -1 +1 @@ -750814 \ No newline at end of file +754754 \ No newline at end of file diff --git a/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3WithoutNativeToken.snap b/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3WithoutNativeToken.snap index 0a4c561..15eee2e 100644 --- a/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3WithoutNativeToken.snap +++ b/.forge-snapshots/CLMigratorFromUniswapV3Test#testMigrateFromV3WithoutNativeToken.snap @@ -1 +1 @@ -792728 \ No newline at end of file +796398 \ No newline at end of file diff --git a/.forge-snapshots/CLSwapRouterTest#ExactInput.snap b/.forge-snapshots/CLSwapRouterTest#ExactInput.snap index 15ee921..73f8bc4 100644 --- a/.forge-snapshots/CLSwapRouterTest#ExactInput.snap +++ b/.forge-snapshots/CLSwapRouterTest#ExactInput.snap @@ -1 +1 @@ -250776 \ No newline at end of file +248857 \ No newline at end of file diff --git a/.forge-snapshots/CLSwapRouterTest#ExactInputSingle.snap b/.forge-snapshots/CLSwapRouterTest#ExactInputSingle.snap index 1faa1ac..97a6859 100644 --- a/.forge-snapshots/CLSwapRouterTest#ExactInputSingle.snap +++ b/.forge-snapshots/CLSwapRouterTest#ExactInputSingle.snap @@ -1 +1 @@ -189707 \ No newline at end of file +188318 \ No newline at end of file diff --git a/.forge-snapshots/CLSwapRouterTest#ExactOutput.snap b/.forge-snapshots/CLSwapRouterTest#ExactOutput.snap index e058585..5937301 100644 --- a/.forge-snapshots/CLSwapRouterTest#ExactOutput.snap +++ b/.forge-snapshots/CLSwapRouterTest#ExactOutput.snap @@ -1 +1 @@ -256829 \ No newline at end of file +254954 \ No newline at end of file diff --git a/.forge-snapshots/CLSwapRouterTest#ExactOutputSingle.snap b/.forge-snapshots/CLSwapRouterTest#ExactOutputSingle.snap index d3d4634..de8ba76 100644 --- a/.forge-snapshots/CLSwapRouterTest#ExactOutputSingle.snap +++ b/.forge-snapshots/CLSwapRouterTest#ExactOutputSingle.snap @@ -1 +1 @@ -189184 \ No newline at end of file +187837 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#BatchMintAndIncreaseLiquidity.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#BatchMintAndIncreaseLiquidity.snap new file mode 100644 index 0000000..f7ed195 --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#BatchMintAndIncreaseLiquidity.snap @@ -0,0 +1 @@ +684324 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#batchMintIncreaseAndDecreaseLiquidity.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#batchMintIncreaseAndDecreaseLiquidity.snap new file mode 100644 index 0000000..a924f0b --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#batchMintIncreaseAndDecreaseLiquidity.snap @@ -0,0 +1 @@ +719013 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#collect.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#collect.snap new file mode 100644 index 0000000..2b2d3d6 --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#collect.snap @@ -0,0 +1 @@ +285998 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#collectWithoutCloseCurrency.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#collectWithoutCloseCurrency.snap new file mode 100644 index 0000000..7b05a2e --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#collectWithoutCloseCurrency.snap @@ -0,0 +1 @@ +272130 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#decreaseLiquidity.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#decreaseLiquidity.snap new file mode 100644 index 0000000..5ee3acc --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#decreaseLiquidity.snap @@ -0,0 +1 @@ +179882 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#decreaseLiquidityWithoutCloseCurrency.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#decreaseLiquidityWithoutCloseCurrency.snap new file mode 100644 index 0000000..f038fe7 --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#decreaseLiquidityWithoutCloseCurrency.snap @@ -0,0 +1 @@ +166247 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#increaseLiquidity.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#increaseLiquidity.snap new file mode 100644 index 0000000..b78dd93 --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#increaseLiquidity.snap @@ -0,0 +1 @@ +212813 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#increaseLiquidityWithoutCloseCurrency.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#increaseLiquidityWithoutCloseCurrency.snap new file mode 100644 index 0000000..10861f7 --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#increaseLiquidityWithoutCloseCurrency.snap @@ -0,0 +1 @@ +199174 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#mint.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#mint.snap new file mode 100644 index 0000000..51ed199 --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#mint.snap @@ -0,0 +1 @@ +619494 \ No newline at end of file diff --git a/.forge-snapshots/NonFungiblePositionManagerBatch#mintWithoutCloseCurrency.snap b/.forge-snapshots/NonFungiblePositionManagerBatch#mintWithoutCloseCurrency.snap new file mode 100644 index 0000000..229cab8 --- /dev/null +++ b/.forge-snapshots/NonFungiblePositionManagerBatch#mintWithoutCloseCurrency.snap @@ -0,0 +1 @@ +605846 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#burn.snap b/.forge-snapshots/NonfungiblePositionManager#burn.snap index 912d52e..f565788 100644 --- a/.forge-snapshots/NonfungiblePositionManager#burn.snap +++ b/.forge-snapshots/NonfungiblePositionManager#burn.snap @@ -1 +1 @@ -64460 \ No newline at end of file +64560 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#collect.snap b/.forge-snapshots/NonfungiblePositionManager#collect.snap index d63c490..774bb51 100644 --- a/.forge-snapshots/NonfungiblePositionManager#collect.snap +++ b/.forge-snapshots/NonfungiblePositionManager#collect.snap @@ -1 +1 @@ -266620 \ No newline at end of file +271543 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#decreaseLiquidity.snap b/.forge-snapshots/NonfungiblePositionManager#decreaseLiquidity.snap index be20379..90d1f95 100644 --- a/.forge-snapshots/NonfungiblePositionManager#decreaseLiquidity.snap +++ b/.forge-snapshots/NonfungiblePositionManager#decreaseLiquidity.snap @@ -1 +1 @@ -160400 \ No newline at end of file +165661 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#increaseLiquidity.snap b/.forge-snapshots/NonfungiblePositionManager#increaseLiquidity.snap index 4b9675c..ec1afcb 100644 --- a/.forge-snapshots/NonfungiblePositionManager#increaseLiquidity.snap +++ b/.forge-snapshots/NonfungiblePositionManager#increaseLiquidity.snap @@ -1 +1 @@ -193953 \ No newline at end of file +198579 \ No newline at end of file diff --git a/.forge-snapshots/NonfungiblePositionManager#mint.snap b/.forge-snapshots/NonfungiblePositionManager#mint.snap index a4e0034..dbe4577 100644 --- a/.forge-snapshots/NonfungiblePositionManager#mint.snap +++ b/.forge-snapshots/NonfungiblePositionManager#mint.snap @@ -1 +1 @@ -606838 \ No newline at end of file +609700 \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 16daa11..c61fcc9 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,7 +2,7 @@ src = 'src' out = 'foundry-out' solc_version = '0.8.26' -optimizer_runs = 400 +optimizer_runs = 9000 via_ir = true ffi = true fs_permissions = [ diff --git a/src/pool-cl/CLMigrator.sol b/src/pool-cl/CLMigrator.sol index ac6bdb5..c8243a4 100644 --- a/src/pool-cl/CLMigrator.sol +++ b/src/pool-cl/CLMigrator.sol @@ -44,10 +44,10 @@ contract CLMigrator is ICLMigrator, BaseMigrator { amount1Desired: amount1In, amount0Min: v4PoolParams.amount0Min, amount1Min: v4PoolParams.amount1Min, - recipient: v4PoolParams.recipient, - deadline: v4PoolParams.deadline + recipient: v4PoolParams.recipient }); - (,, uint256 amount0Consumed, uint256 amount1Consumed) = _addLiquidityToTargetPool(mintParams); + (,, uint256 amount0Consumed, uint256 amount1Consumed) = + _addLiquidityToTargetPool(mintParams, v4PoolParams.deadline); // refund if necessary, ETH is supported by CurrencyLib unchecked { @@ -88,10 +88,10 @@ contract CLMigrator is ICLMigrator, BaseMigrator { amount1Desired: amount1In, amount0Min: v4PoolParams.amount0Min, amount1Min: v4PoolParams.amount1Min, - recipient: v4PoolParams.recipient, - deadline: v4PoolParams.deadline + recipient: v4PoolParams.recipient }); - (,, uint256 amount0Consumed, uint256 amount1Consumed) = _addLiquidityToTargetPool(mintParams); + (,, uint256 amount0Consumed, uint256 amount1Consumed) = + _addLiquidityToTargetPool(mintParams, v4PoolParams.deadline); // refund if necessary, ETH is supported by CurrencyLib unchecked { @@ -106,11 +106,12 @@ contract CLMigrator is ICLMigrator, BaseMigrator { /// @dev adding liquidity to target cl pool, collect surplus ETH if necessary /// @param params cl position manager add liquidity params + /// @param deadline the deadline for the transaction /// @return tokenId the id of the newly minted position token /// @return liquidity the amount of liquidity minted /// @return amount0Consumed the actual amount of token0 consumed /// @return amount1Consumed the actual amount of token1 consumed - function _addLiquidityToTargetPool(INonfungiblePositionManager.MintParams memory params) + function _addLiquidityToTargetPool(INonfungiblePositionManager.MintParams memory params, uint256 deadline) internal returns (uint256 tokenId, uint128 liquidity, uint256 amount0Consumed, uint256 amount1Consumed) { @@ -121,8 +122,20 @@ contract CLMigrator is ICLMigrator, BaseMigrator { } approveMaxIfNeeded(params.poolKey.currency1, address(nonfungiblePositionManager), params.amount1Desired); - (tokenId, liquidity, amount0Consumed, amount1Consumed) = - nonfungiblePositionManager.mint{value: nativePair ? params.amount0Desired : 0}(params); + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(params) + ) + ); + bytes[] memory lockData = new bytes[](1); + lockData[0] = mintData; + + (tokenId, liquidity, amount0Consumed, amount1Consumed) = abi.decode( + nonfungiblePositionManager.modifyLiquidities{value: nativePair ? params.amount0Desired : 0}( + abi.encode(lockData), deadline + )[0], + (uint256, uint128, uint256, uint256) + ); // receive surplus ETH from positionManager if (nativePair && params.amount0Desired > amount0Consumed) { diff --git a/src/pool-cl/NonfungiblePositionManager.sol b/src/pool-cl/NonfungiblePositionManager.sol index 77d424c..6d63c0c 100644 --- a/src/pool-cl/NonfungiblePositionManager.sol +++ b/src/pool-cl/NonfungiblePositionManager.sol @@ -10,7 +10,7 @@ import {FullMath} from "pancake-v4-core/src/pool-cl/libraries/FullMath.sol"; import {CLPosition} from "pancake-v4-core/src/pool-cl/libraries/CLPosition.sol"; import {BalanceDelta, toBalanceDelta} from "pancake-v4-core/src/types/BalanceDelta.sol"; import {FixedPoint128} from "pancake-v4-core/src/pool-cl/libraries/FixedPoint128.sol"; -import {Currency} from "pancake-v4-core/src/types/Currency.sol"; +import {Currency, CurrencyLibrary} from "pancake-v4-core/src/types/Currency.sol"; import {INonfungiblePositionManager} from "./interfaces/INonfungiblePositionManager.sol"; import {INonfungibleTokenPositionDescriptor} from "./interfaces/INonfungibleTokenPositionDescriptor.sol"; @@ -35,6 +35,7 @@ contract NonfungiblePositionManager is Multicall { using PoolIdLibrary for PoolKey; + using CurrencyLibrary for Currency; /// @dev The ID of the next token that will be minted. Skips 0 uint256 private _nextId = 1; @@ -55,13 +56,6 @@ contract NonfungiblePositionManager is _tokenDescriptor = _tokenDescriptor_; } - modifier isAuthorizedForToken(uint256 tokenId) { - if (!_isApprovedOrOwner(msg.sender, tokenId)) { - revert NotOwnerOrOperator(); - } - _; - } - /// @inheritdoc INonfungiblePositionManager function positions(uint256 tokenId) external @@ -116,118 +110,77 @@ contract NonfungiblePositionManager is } /// @inheritdoc INonfungiblePositionManager - function mint(MintParams calldata params) + function modifyLiquidities(bytes calldata lockData, uint256 deadline) external payable - override - checkDeadline(params.deadline) - returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) + checkDeadline(deadline) + returns (bytes[] memory) { - // msg.sender as payer, params.recipient as NFT position receiver - (tokenId, liquidity, amount0, amount1) = abi.decode( - vault.lock(abi.encode(CallbackData(msg.sender, CallbackDataType.Mint, abi.encode(params)))), - (uint256, uint128, uint256, uint256) - ); - - emit IncreaseLiquidity(tokenId, liquidity, amount0, amount1); + return abi.decode(vault.lock(abi.encode(msg.sender, lockData)), (bytes[])); } /// @inheritdoc INonfungiblePositionManager - function increaseLiquidity(IncreaseLiquidityParams calldata params) - external - payable - override - checkDeadline(params.deadline) - returns (uint128 liquidity, uint256 amount0, uint256 amount1) - { - (liquidity, amount0, amount1) = abi.decode( - vault.lock(abi.encode(CallbackData(msg.sender, CallbackDataType.IncreaseLiquidity, abi.encode(params)))), - (uint128, uint256, uint256) - ); - - emit IncreaseLiquidity(params.tokenId, liquidity, amount0, amount1); + function burn(uint256 tokenId) external payable override { + _handleBurn(tokenId, msg.sender); } - /// @inheritdoc INonfungiblePositionManager - function decreaseLiquidity(DecreaseLiquidityParams calldata params) - external - payable - override - isAuthorizedForToken(params.tokenId) - checkDeadline(params.deadline) - returns (uint256 amount0, uint256 amount1) - { - if (params.liquidity == 0) { - revert InvalidLiquidityDecreaseAmount(); + function lockAcquired(bytes calldata rawData) external returns (bytes memory) { + if (msg.sender != address(vault)) { + revert OnlyVaultCaller(); } - (amount0, amount1) = abi.decode( - vault.lock(abi.encode(CallbackData(msg.sender, CallbackDataType.DecreaseLiquidity, abi.encode(params)))), - (uint256, uint256) - ); - emit DecreaseLiquidity(params.tokenId, params.liquidity, amount0, amount1); + (address sender, bytes memory lockData) = abi.decode(rawData, (address, bytes)); + bytes[] memory params = abi.decode(lockData, (bytes[])); + return _dispatch(params, sender); } - /// @inheritdoc INonfungiblePositionManager - function burn(uint256 tokenId) external payable override isAuthorizedForToken(tokenId) { - Position storage position = _positions[tokenId]; - if (position.liquidity > 0 || position.tokensOwed0 > 0 || position.tokensOwed1 > 0) { - revert NonEmptyPosition(); + function _dispatch(bytes[] memory params, address sender) internal returns (bytes memory returnDataArrayBytes) { + uint256 length = params.length; + bytes[] memory returnData = new bytes[](length); + // In order to save gas, we will set the settle flag to true if only one liquidity modification + bool shouldSettle = length == 1; + for (uint256 i; i < length; i++) { + CallbackData memory data = abi.decode(params[i], (CallbackData)); + returnData[i] = _handleSingleAction(data, sender, shouldSettle); } - delete _positions[tokenId]; - _burn(tokenId); + return abi.encode(returnData); } - /// @inheritdoc INonfungiblePositionManager - function collect(CollectParams memory params) - external - payable - override - isAuthorizedForToken(params.tokenId) - returns (uint256 amount0, uint256 amount1) + function _handleSingleAction(CallbackData memory data, address sender, bool shouldSettle) + internal + returns (bytes memory) { - if (params.amount0Max == 0 && params.amount1Max == 0) { - revert InvalidMaxCollectAmount(); - } - params.recipient = params.recipient == address(0) ? address(msg.sender) : params.recipient; - - (amount0, amount1) = abi.decode( - vault.lock(abi.encode(CallbackData(msg.sender, CallbackDataType.Collect, abi.encode(params)))), - (uint256, uint256) - ); - - emit Collect(params.tokenId, params.recipient, amount0, amount1); - } - - function lockAcquired(bytes calldata rawData) external returns (bytes memory) { - if (msg.sender != address(vault)) { - revert OnlyVaultCaller(); - } - - CallbackData memory data = abi.decode(rawData, (CallbackData)); if (data.callbackDataType == CallbackDataType.Mint) { - return _handleMint(data); + return _handleMint(data, sender, shouldSettle); } else if (data.callbackDataType == CallbackDataType.IncreaseLiquidity) { - return _handleIncreaseLiquidity(data); + return _handleIncreaseLiquidity(data, sender, shouldSettle); } else if (data.callbackDataType == CallbackDataType.DecreaseLiquidity) { - return _handleDecreaseLiquidity(data); + return _handleDecreaseLiquidity(data, sender, shouldSettle); } else if (data.callbackDataType == CallbackDataType.Collect) { - return _handleCollect(data); + return _handleCollect(data, sender, shouldSettle); + } else if (data.callbackDataType == CallbackDataType.CloseCurrency) { + return _close(data.params, sender); + } else if (data.callbackDataType == CallbackDataType.Burn) { + uint256 tokenId = abi.decode(data.params, (uint256)); + return _handleBurn(tokenId, sender); } else { revert InvalidCalldataType(); } } - function _handleMint(CallbackData memory data) internal returns (bytes memory) { + function _handleMint(CallbackData memory data, address sender, bool shouldSettle) internal returns (bytes memory) { INonfungiblePositionManager.MintParams memory params = abi.decode(data.params, (INonfungiblePositionManager.MintParams)); - + PoolKey memory poolKey = params.poolKey; + PoolId poolId = poolKey.toId(); + int24 tickLower = params.tickLower; + int24 tickUpper = params.tickUpper; (uint128 liquidity, BalanceDelta delta) = addLiquidity( AddLiquidityParams({ - poolKey: params.poolKey, - tickLower: params.tickLower, - tickUpper: params.tickUpper, + poolKey: poolKey, + tickLower: tickLower, + tickUpper: tickUpper, salt: params.salt, amount0Desired: params.amount0Desired, amount1Desired: params.amount1Desired, @@ -239,15 +192,14 @@ contract NonfungiblePositionManager is uint256 tokenId = _nextId++; _mint(params.recipient, tokenId); - CLPosition.Info memory positionInfo = poolManager.getPosition( - params.poolKey.toId(), address(this), params.tickLower, params.tickUpper, params.salt - ); + CLPosition.Info memory positionInfo = + poolManager.getPosition(poolId, address(this), tickLower, tickUpper, params.salt); _positions[tokenId] = Position({ nonce: 0, operator: address(0), - poolId: params.poolKey.toId(), - tickLower: params.tickLower, - tickUpper: params.tickUpper, + poolId: poolId, + tickLower: tickLower, + tickUpper: tickUpper, liquidity: liquidity, feeGrowthInside0LastX128: positionInfo.feeGrowthInside0LastX128, feeGrowthInside1LastX128: positionInfo.feeGrowthInside1LastX128, @@ -255,16 +207,25 @@ contract NonfungiblePositionManager is tokensOwed1: 0, salt: params.salt }); - if (address(_poolIdToPoolKey[params.poolKey.toId()].poolManager) == address(0)) { - _poolIdToPoolKey[params.poolKey.toId()] = params.poolKey; + if (address(_poolIdToPoolKey[poolId].poolManager) == address(0)) { + _poolIdToPoolKey[poolId] = poolKey; } - settleDeltas(data.sender, params.poolKey, delta); + if (shouldSettle) { + settleDeltas(sender, poolKey, delta); + } + uint128 amount0 = uint128(-delta.amount0()); + uint128 amount1 = uint128(-delta.amount1()); + + emit IncreaseLiquidity(tokenId, liquidity, amount0, amount1); - return abi.encode(tokenId, liquidity, -delta.amount0(), -delta.amount1()); + return abi.encode(tokenId, liquidity, amount0, amount1); } - function _handleIncreaseLiquidity(CallbackData memory data) internal returns (bytes memory) { + function _handleIncreaseLiquidity(CallbackData memory data, address sender, bool shouldSettle) + internal + returns (bytes memory) + { IncreaseLiquidityParams memory params = abi.decode(data.params, (IncreaseLiquidityParams)); Position storage nftPosition = _positions[params.tokenId]; PoolId poolId = nftPosition.poolId; @@ -320,13 +281,26 @@ contract NonfungiblePositionManager is nftPosition.feeGrowthInside1LastX128 = poolManagerPositionInfo.feeGrowthInside1LastX128; nftPosition.liquidity += liquidity; - settleDeltas(data.sender, poolKey, delta); + if (shouldSettle) { + settleDeltas(sender, poolKey, delta); + } + + uint128 amount0 = uint128(-delta.amount0()); + uint128 amount1 = uint128(-delta.amount1()); + emit IncreaseLiquidity(params.tokenId, liquidity, amount0, amount1); - return abi.encode(liquidity, -delta.amount0(), -delta.amount1()); + return abi.encode(liquidity, amount0, amount1); } - function _handleDecreaseLiquidity(CallbackData memory data) internal returns (bytes memory) { + function _handleDecreaseLiquidity(CallbackData memory data, address sender, bool shouldSettle) + internal + returns (bytes memory) + { DecreaseLiquidityParams memory params = abi.decode(data.params, (DecreaseLiquidityParams)); + if (params.liquidity == 0) { + revert InvalidLiquidityDecreaseAmount(); + } + _checkAuthorizedForToken(sender, params.tokenId); Position storage nftPosition = _positions[params.tokenId]; PoolId poolId = nftPosition.poolId; uint128 liquidity = nftPosition.liquidity; @@ -387,13 +361,25 @@ contract NonfungiblePositionManager is nftPosition.liquidity -= params.liquidity; } - settleDeltas(data.sender, poolKey, delta); + if (shouldSettle) { + settleDeltas(sender, poolKey, delta); + } + + emit DecreaseLiquidity(params.tokenId, params.liquidity, uint128(delta.amount0()), uint128(delta.amount1())); return abi.encode(delta.amount0(), delta.amount1()); } - function _handleCollect(CallbackData memory data) internal returns (bytes memory) { + function _handleCollect(CallbackData memory data, address sender, bool shouldTake) + internal + returns (bytes memory) + { CollectParams memory params = abi.decode(data.params, (CollectParams)); + if (params.amount0Max == 0 && params.amount1Max == 0) { + revert InvalidMaxCollectAmount(); + } + _checkAuthorizedForToken(sender, params.tokenId); + params.recipient = params.recipient == address(0) ? address(sender) : params.recipient; Position storage nftPosition = _positions[params.tokenId]; Position memory nftPositionCache = _positions[params.tokenId]; PoolId poolId = nftPositionCache.poolId; @@ -459,12 +445,45 @@ contract NonfungiblePositionManager is ); // cash out from vault - burnAndTake(poolKey.currency0, params.recipient, amount0Collect); - burnAndTake(poolKey.currency1, params.recipient, amount1Collect); + burnAndTake(poolKey.currency0, params.recipient, amount0Collect, shouldTake); + burnAndTake(poolKey.currency1, params.recipient, amount1Collect, shouldTake); + + emit Collect(params.tokenId, params.recipient, amount0Collect, amount1Collect); return abi.encode(amount0Collect, amount1Collect); } + function _handleBurn(uint256 tokenId, address sender) internal returns (bytes memory) { + _checkAuthorizedForToken(sender, tokenId); + Position storage position = _positions[tokenId]; + if (position.liquidity > 0 || position.tokensOwed0 > 0 || position.tokensOwed1 > 0) { + revert NonEmptyPosition(); + } + + delete _positions[tokenId]; + _burn(tokenId); + + return abi.encode(tokenId); + } + + /// @param params is an encoding of the Currency to close + /// @param sender is the msg.sender encoded by the `modifyLiquidities` function before the `lockAcquired`. + /// @return an encoding of int256 the balance of the currency being settled by this call + function _close(bytes memory params, address sender) internal returns (bytes memory) { + (Currency currency) = abi.decode(params, (Currency)); + // this address has applied all deltas on behalf of the user/owner + // it is safe to close this entire delta because of slippage checks throughout the batched calls. + int256 currencyDelta = vault.currencyDelta(address(this), currency); + + settleOrTake(currency, sender, int128(currencyDelta)); + // if there are native tokens left over after settling, return to sender + if (address(this).balance > 0 && currency.isNative()) { + CurrencyLibrary.NATIVE.transfer(sender, address(this).balance); + } + + return abi.encode(currencyDelta); + } + function tokenURI(uint256 tokenId) public view override(ERC721, IERC721Metadata) returns (string memory) { if (!_exists(tokenId)) { revert NonexistentToken(); @@ -488,6 +507,12 @@ contract NonfungiblePositionManager is return _positions[tokenId].operator; } + function _checkAuthorizedForToken(address sender, uint256 tokenId) internal view { + if (!_isApprovedOrOwner(sender, tokenId)) { + revert NotOwnerOrOperator(); + } + } + /// @dev Overrides _approve to use the operator in the position, which is packed with the position permit nonce function _approve(address to, uint256 tokenId) internal override(ERC721) { _positions[tokenId].operator = to; diff --git a/src/pool-cl/base/LiquidityManagement.sol b/src/pool-cl/base/LiquidityManagement.sol index e48e699..669f1a5 100644 --- a/src/pool-cl/base/LiquidityManagement.sol +++ b/src/pool-cl/base/LiquidityManagement.sol @@ -126,33 +126,28 @@ abstract contract LiquidityManagement is CLPeripheryImmutableState, PeripheryPay } } - function burnAndTake(Currency currency, address to, uint256 amount) internal { + function burnAndTake(Currency currency, address to, uint256 amount, bool shouldTake) internal { vault.burn(address(this), currency, amount); - vault.take(currency, to, amount); + if (shouldTake) { + vault.take(currency, to, amount); + } } function settleDeltas(address sender, PoolKey memory poolKey, BalanceDelta delta) internal { - if (delta.amount0() > 0) { - vault.take(poolKey.currency0, sender, uint128(delta.amount0())); - } else if (delta.amount0() < 0) { - if (poolKey.currency0.isNative()) { - vault.settle{value: uint256(int256(-delta.amount0()))}(poolKey.currency0); - } else { - vault.sync(poolKey.currency0); - pay(poolKey.currency0, sender, address(vault), uint256(int256(-delta.amount0()))); - vault.settle(poolKey.currency0); - } - } + settleOrTake(poolKey.currency0, sender, delta.amount0()); + settleOrTake(poolKey.currency1, sender, delta.amount1()); + } - if (delta.amount1() > 0) { - vault.take(poolKey.currency1, sender, uint128(delta.amount1())); - } else if (delta.amount1() < 0) { - if (poolKey.currency1.isNative()) { - vault.settle{value: uint256(int256(-delta.amount1()))}(poolKey.currency1); + function settleOrTake(Currency currency, address sender, int128 amount) internal { + if (amount > 0) { + vault.take(currency, sender, uint128(amount)); + } else if (amount < 0) { + if (currency.isNative()) { + vault.settle{value: uint256(int256(-amount))}(currency); } else { - vault.sync(poolKey.currency1); - pay(poolKey.currency1, sender, address(vault), uint256(int256(-delta.amount1()))); - vault.settle(poolKey.currency1); + vault.sync(currency); + pay(currency, sender, address(vault), uint256(int256(-amount))); + vault.settle(currency); } } } diff --git a/src/pool-cl/interfaces/INonfungiblePositionManager.sol b/src/pool-cl/interfaces/INonfungiblePositionManager.sol index 6bfe935..2fe762a 100644 --- a/src/pool-cl/interfaces/INonfungiblePositionManager.sol +++ b/src/pool-cl/interfaces/INonfungiblePositionManager.sol @@ -41,11 +41,13 @@ interface INonfungiblePositionManager is Mint, IncreaseLiquidity, DecreaseLiquidity, - Collect + Collect, + Burn, + BatchModifyLiquidity, + CloseCurrency } struct CallbackData { - address sender; CallbackDataType callbackDataType; bytes params; } @@ -136,21 +138,21 @@ interface INonfungiblePositionManager is uint256 amount0Min; uint256 amount1Min; address recipient; - uint256 deadline; } - /// @notice Creates a new position wrapped in a NFT - /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized - /// a method does not exist, i.e. the pool is assumed to be initialized. - /// @param params The params necessary to mint a position, encoded as `MintParams` in calldata - /// @return tokenId The ID of the token that represents the minted position - /// @return liquidity The amount of liquidity for this position - /// @return amount0 The amount of token0 - /// @return amount1 The amount of token1 - function mint(MintParams calldata params) - external - payable - returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); + /// @notice Batches many liquidity modification calls to pool manager + /// @param payload is an encoding of actions, and parameters for those actions + /// @dev The payload is a byte array that represents the encoded form of the CallbackData struct + /// for example to mint a position the payload would be: + /// bytes[] memory payloadArray = new bytes[](1); + /// bytes memory mintData = abi.encode(INonfungiblePositionManager.CallbackData( + /// INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(INonfungiblePositionManager.MintParams({...})) + /// )) + /// payloadArray[0] = mintData; + /// bytes memory payload = abi.encode(payloadArray); + /// @param deadline is the deadline for the batched actions to be executed + /// @return returnData is the endocing of each actions return information + function modifyLiquidities(bytes calldata payload, uint256 deadline) external payable returns (bytes[] memory); struct IncreaseLiquidityParams { uint256 tokenId; @@ -158,45 +160,15 @@ interface INonfungiblePositionManager is uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; - uint256 deadline; } - /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender` - /// @param params tokenId The ID of the token for which liquidity is being increased, - /// amount0Desired The desired amount of token0 to be spent, - /// amount1Desired The desired amount of token1 to be spent, - /// amount0Min The minimum amount of token0 to spend, which serves as a slippage check, - /// amount1Min The minimum amount of token1 to spend, which serves as a slippage check, - /// deadline The time by which the transaction must be included to effect the change - /// @return liquidity The new liquidity amount as a result of the increase - /// @return amount0 The amount of token0 to acheive resulting liquidity - /// @return amount1 The amount of token1 to acheive resulting liquidity - function increaseLiquidity(IncreaseLiquidityParams calldata params) - external - payable - returns (uint128 liquidity, uint256 amount0, uint256 amount1); - struct DecreaseLiquidityParams { uint256 tokenId; uint128 liquidity; uint256 amount0Min; uint256 amount1Min; - uint256 deadline; } - /// @notice Decreases the amount of liquidity in a position and accounts it to the position - /// @param params tokenId The ID of the token for which liquidity is being decreased, - /// amount The amount by which liquidity will be decreased, - /// amount0Min The minimum amount of token0 that should be accounted for the burned liquidity, - /// amount1Min The minimum amount of token1 that should be accounted for the burned liquidity, - /// deadline The time by which the transaction must be included to effect the change - /// @return amount0 The amount of token0 accounted to the position's tokens owed - /// @return amount1 The amount of token1 accounted to the position's tokens owed - function decreaseLiquidity(DecreaseLiquidityParams calldata params) - external - payable - returns (uint256 amount0, uint256 amount1); - struct CollectParams { uint256 tokenId; address recipient; @@ -204,13 +176,6 @@ interface INonfungiblePositionManager is uint128 amount1Max; } - /// @notice Collects the fees owed to a specific position to the recipient - /// @param params tokenId The ID of the NFT for which tokens are being collected, - /// recipient The account that should receive the tokens, - /// @return amount0 The amount of fees collected in token0 - /// @return amount1 The amount of fees collected in token1 - function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1); - /// @notice Burns a token ID, which deletes it from the NFT contract. The token must have 0 liquidity and all tokens /// must be collected first. /// @param tokenId The ID of the token that is being burned diff --git a/test/pool-cl/CLSwapRouterInvariant.t.sol b/test/pool-cl/CLSwapRouterInvariant.t.sol index 4f83b60..7a94a0a 100644 --- a/test/pool-cl/CLSwapRouterInvariant.t.sol +++ b/test/pool-cl/CLSwapRouterInvariant.t.sol @@ -270,15 +270,23 @@ contract CLSwapRouterHandler is Test { amount1Desired: amt, amount0Min: 0, amount1Min: 0, - recipient: alice, - deadline: block.timestamp + recipient: alice }); vm.startPrank(alice); + //generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(mintParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = mintData; + if (isNativePool) { - positionManager.mint{value: amt}(mintParams); + positionManager.modifyLiquidities{value: amt}(abi.encode(data), block.timestamp); } else { - positionManager.mint(mintParams); + positionManager.modifyLiquidities(abi.encode(data), block.timestamp); } vm.stopPrank(); } @@ -359,14 +367,25 @@ contract CLSwapRouterInvariant is Test { uint256 tokenId = positionManager.tokenOfOwnerByIndex(_handler.alice(), i); vm.prank(_handler.alice()); positionManager.approve(address(this), tokenId); - (uint256 _realFee0Accrued, uint256 _realFee1Accrued) = positionManager.collect( - INonfungiblePositionManager.CollectParams({ - tokenId: tokenId, - recipient: _handler.alice(), - amount0Max: type(uint128).max, - amount1Max: type(uint128).max - }) + + // generate modifyLiquidities data + bytes memory collectData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Collect, + abi.encode( + INonfungiblePositionManager.CollectParams({ + tokenId: tokenId, + recipient: _handler.alice(), + amount0Max: type(uint128).max, + amount1Max: type(uint128).max + }) + ) + ) ); + bytes[] memory data = new bytes[](1); + data[0] = collectData; + (uint256 _realFee0Accrued, uint256 _realFee1Accrued) = + abi.decode(positionManager.modifyLiquidities(abi.encode(data), block.timestamp)[0], (uint256, uint256)); realFee0Accrued += _realFee0Accrued; realFee1Accrued += _realFee1Accrued; } diff --git a/test/pool-cl/NonFungiblePositionManager.t.sol b/test/pool-cl/NonFungiblePositionManager.t.sol index e19dadf..d96b65f 100644 --- a/test/pool-cl/NonFungiblePositionManager.t.sol +++ b/test/pool-cl/NonFungiblePositionManager.t.sol @@ -30,6 +30,7 @@ import {NonfungiblePositionManager} from "../../src/pool-cl/NonfungiblePositionM import {INonfungiblePositionManager} from "../../src/pool-cl/interfaces/INonfungiblePositionManager.sol"; import {LiquidityAmounts} from "../../src/pool-cl/libraries/LiquidityAmounts.sol"; import {LiquidityManagement} from "../../src/pool-cl/base/LiquidityManagement.sol"; +import {PeripheryValidation} from "../../src/base/PeripheryValidation.sol"; contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { using PoolIdLibrary for PoolKey; @@ -75,6 +76,80 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { IERC20(Currency.unwrap(currency1)).approve(address(nonfungiblePoolManager), type(uint256).max); } + function mint(INonfungiblePositionManager.MintParams memory mintParams) + internal + returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) + { + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(mintParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = mintData; + + return abi.decode( + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max)[0], + (uint256, uint128, uint256, uint256) + ); + } + + function increaseLiquidity(INonfungiblePositionManager.IncreaseLiquidityParams memory params) + internal + returns (uint128 liquidity, uint256 amount0, uint256 amount1) + { + // generate modifyLiquidities data + bytes memory increaseLiquidityData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.IncreaseLiquidity, abi.encode(params) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = increaseLiquidityData; + + return abi.decode( + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max)[0], + (uint128, uint256, uint256) + ); + } + + function collect(INonfungiblePositionManager.CollectParams memory params) + internal + returns (uint256 amount0, uint256 amount1) + { + // generate modifyLiquidities data + bytes memory collectData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Collect, abi.encode(params) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = collectData; + + return abi.decode( + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max)[0], (uint256, uint256) + ); + } + + function decreaseLiquidity(INonfungiblePositionManager.DecreaseLiquidityParams memory params) + internal + returns (uint256 amount0, uint256 amount1) + { + // generate modifyLiquidities data + bytes memory decreaseLiquidityData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.DecreaseLiquidity, abi.encode(params) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = decreaseLiquidityData; + + return abi.decode( + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max)[0], (uint256, uint256) + ); + } + function testPositions() external { PoolKey memory key = PoolKey({ currency0: currency0, @@ -97,8 +172,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); @@ -107,7 +181,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // (, int24 tick,,,) = poolManager.getSlot0(key.toId()); // price = 100 i.e. tick 46054 // console2.log("tick", tick); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); @@ -115,12 +189,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // mint another position in the same price range but different recipient INonfungiblePositionManager.MintParams memory mintParams2 = mintParams; mintParams2.recipient = address(this); - nonfungiblePoolManager.mint(mintParams2); + mint(mintParams2); // mint another position in the same price range but different salt INonfungiblePositionManager.MintParams memory mintParams3 = mintParams; mintParams3.salt = bytes32(uint256(0xABCD)); - nonfungiblePoolManager.mint(mintParams3); + mint(mintParams3); } { @@ -240,14 +314,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // modifyPosition 0 to refresh position(1)'s LPing vm.prank(makeAddr("someone")); - nonfungiblePoolManager.increaseLiquidity( + increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: 1, amount0Desired: 0, amount1Desired: 0, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -337,7 +410,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { emit IncreaseLiquidity(1, liquidityExpected, 0, 0); } - (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = nonfungiblePoolManager.mint( + (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = mint( INonfungiblePositionManager.MintParams({ poolKey: key, tickLower: tickLower, @@ -347,8 +420,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: amount1Desired, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }) ); @@ -391,9 +463,11 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { assertEq(nonfungiblePoolManager.ownerOf(tokenId), address(this), "Unexpected owner of the position"); } - function testMint_gas() external { + function testMint_nativeToken() external { + vm.deal(address(this), 1000 ether); + PoolKey memory key = PoolKey({ - currency0: currency0, + currency0: Currency.wrap(address(0)), currency1: currency1, hooks: IHooks(address(0)), poolManager: poolManager, @@ -411,22 +485,339 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - snapStart("NonfungiblePositionManager#mint"); - nonfungiblePoolManager.mint( - INonfungiblePositionManager.MintParams({ - poolKey: key, - tickLower: tickLower, - tickUpper: tickUpper, - salt: bytes32(0), - amount0Desired: amount0Desired, - amount1Desired: amount1Desired, - amount0Min: 0, - amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max - }) + uint128 liquidityExpected = LiquidityAmounts.getLiquidityForAmounts( + sqrtPriceX96, + TickMath.getSqrtRatioAtTick(tickLower), + TickMath.getSqrtRatioAtTick(tickUpper), + amount0Desired, + amount1Desired ); - snapEnd(); + + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, + abi.encode( + INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: tickLower, + tickUpper: tickUpper, + salt: bytes32(0), + amount0Desired: amount0Desired, + amount1Desired: amount1Desired, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }) + ) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = mintData; + + bytes memory lockData = abi.encode(data); + vm.expectEmit(true, true, true, false); + emit IncreaseLiquidity(1, liquidityExpected, 0, 0); + uint256 nativeTokenBalanceBefore = address(this).balance; + (,, uint256 amount0, uint256 amount1) = abi.decode( + nonfungiblePoolManager.modifyLiquidities{value: 19824513708386292}(lockData, type(uint256).max)[0], + (uint256, uint128, uint256, uint256) + ); + + // token consumed + { + uint256 token0Left = address(this).balance; + uint256 token1Left = IERC20(Currency.unwrap(currency1)).balanceOf(address(this)); + + // 1.982e16 roughly 0.02 ether + assertEq(nativeTokenBalanceBefore - token0Left, 19824513708386292, "Unexpected currency0 consumed"); + assertEq(amount0, 19824513708386292, "Actual consumed currency0 mismatch"); + // 1e18 i.e. 2 ether, make sense because price is 100 + assertEq(1000 ether - token1Left, 2000000000000000000, "Unexpected currency1 consumed"); + assertEq(amount1, 2000000000000000000, "Actual consumed currency1 mismatch"); + } + } + + function testMint_nativeToken_withoutRefund() external { + vm.deal(address(this), 1000 ether); + + PoolKey memory key = PoolKey({ + currency0: Currency.wrap(address(0)), + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + int24 tickLower = 46053; + int24 tickUpper = 46055; + uint256 amount0Desired = 1 ether; + uint256 amount1Desired = 2 ether; + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + uint128 liquidityExpected = LiquidityAmounts.getLiquidityForAmounts( + sqrtPriceX96, + TickMath.getSqrtRatioAtTick(tickLower), + TickMath.getSqrtRatioAtTick(tickUpper), + amount0Desired, + amount1Desired + ); + + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, + abi.encode( + INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: tickLower, + tickUpper: tickUpper, + salt: bytes32(0), + amount0Desired: amount0Desired, + amount1Desired: amount1Desired, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }) + ) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = mintData; + + bytes memory lockData = abi.encode(data); + vm.expectEmit(true, true, true, false); + emit IncreaseLiquidity(1, liquidityExpected, 0, 0); + uint256 nativeTokenBalanceBefore = address(this).balance; + uint256 nonfungiblePoolManagerBalance = address(nonfungiblePoolManager).balance; + assertEq(nonfungiblePoolManagerBalance, 0, "Unexpected nonfungiblePoolManager balance"); + + (,, uint256 amount0, uint256 amount1) = abi.decode( + nonfungiblePoolManager.modifyLiquidities{value: 1 ether}(lockData, type(uint256).max)[0], + (uint256, uint128, uint256, uint256) + ); + + // token consumed + { + uint256 token0Left = address(this).balance; + uint256 token1Left = IERC20(Currency.unwrap(currency1)).balanceOf(address(this)); + uint256 nonfungiblePoolManagerBalanceAfter = address(nonfungiblePoolManager).balance; + + assertEq( + nonfungiblePoolManagerBalanceAfter, + 1 ether - 19824513708386292, + "Unexpected nonfungiblePoolManager balance" + ); + + // 1.982e16 roughly 0.02 ether + assertEq(nativeTokenBalanceBefore - token0Left, 1 ether, "Unexpected currency0 consumed"); + assertEq(amount0, 19824513708386292, "Actual consumed currency0 mismatch"); + // 1e18 i.e. 2 ether, make sense because price is 100 + assertEq(1000 ether - token1Left, 2000000000000000000, "Unexpected currency1 consumed"); + assertEq(amount1, 2000000000000000000, "Actual consumed currency1 mismatch"); + } + } + + function testMint_nativeToken_refund() external { + address alice = makeAddr("alice"); + vm.deal(alice, 1000 ether); + currency1.transfer(alice, 1000 ether); + + vm.startPrank(alice); + IERC20(Currency.unwrap(currency1)).approve(address(nonfungiblePoolManager), type(uint256).max); + + PoolKey memory key = PoolKey({ + currency0: Currency.wrap(address(0)), + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + int24 tickLower = 46053; + int24 tickUpper = 46055; + uint256 amount0Desired = 1 ether; + uint256 amount1Desired = 2 ether; + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + uint128 liquidityExpected = LiquidityAmounts.getLiquidityForAmounts( + sqrtPriceX96, + TickMath.getSqrtRatioAtTick(tickLower), + TickMath.getSqrtRatioAtTick(tickUpper), + amount0Desired, + amount1Desired + ); + + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, + abi.encode( + INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: tickLower, + tickUpper: tickUpper, + salt: bytes32(0), + amount0Desired: amount0Desired, + amount1Desired: amount1Desired, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }) + ) + ) + ); + bytes[] memory data = new bytes[](3); + data[0] = mintData; + + // set current close data + data[1] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(Currency.wrap(address(0))) + ) + ); + data[2] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency1) + ) + ); + + bytes memory lockData = abi.encode(data); + vm.expectEmit(true, true, true, false); + emit IncreaseLiquidity(1, liquidityExpected, 0, 0); + uint256 nativeTokenBalanceBefore = address(alice).balance; + uint256 nonfungiblePoolManagerBalance = address(nonfungiblePoolManager).balance; + assertEq(nonfungiblePoolManagerBalance, 0, "Unexpected nonfungiblePoolManager balance"); + + (,, uint256 amount0, uint256 amount1) = abi.decode( + nonfungiblePoolManager.modifyLiquidities{value: 1 ether}(lockData, type(uint256).max)[0], + (uint256, uint128, uint256, uint256) + ); + + // token consumed + { + uint256 token0Left = address(alice).balance; + uint256 token1Left = IERC20(Currency.unwrap(currency1)).balanceOf(alice); + uint256 nonfungiblePoolManagerBalanceAfter = address(nonfungiblePoolManager).balance; + + assertEq(nonfungiblePoolManagerBalanceAfter, 0, "Unexpected nonfungiblePoolManager balance"); + + // 1.982e16 roughly 0.02 ether + assertEq(nativeTokenBalanceBefore - token0Left, 19824513708386292, "Unexpected currency0 consumed"); + assertEq(amount0, 19824513708386292, "Actual consumed currency0 mismatch"); + // 1e18 i.e. 2 ether, make sense because price is 100 + assertEq(1000 ether - token1Left, 2000000000000000000, "Unexpected currency1 consumed"); + assertEq(amount1, 2000000000000000000, "Actual consumed currency1 mismatch"); + } + } + + function testMint_gas() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + { + int24 tickLower = 46053; + int24 tickUpper = 46055; + uint256 amount0Desired = 1 ether; + uint256 amount1Desired = 2 ether; + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, + abi.encode( + INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: tickLower, + tickUpper: tickUpper, + salt: bytes32(0), + amount0Desired: amount0Desired, + amount1Desired: amount1Desired, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }) + ) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = mintData; + + bytes memory lockData = abi.encode(data); + snapStart("NonfungiblePositionManager#mint"); + nonfungiblePoolManager.modifyLiquidities(lockData, type(uint256).max); + snapEnd(); + } + } + + function test_transactionTooOld() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + { + int24 tickLower = 46053; + int24 tickUpper = 46055; + uint256 amount0Desired = 1 ether; + uint256 amount1Desired = 2 ether; + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, + abi.encode( + INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: tickLower, + tickUpper: tickUpper, + salt: bytes32(0), + amount0Desired: amount0Desired, + amount1Desired: amount1Desired, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }) + ) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = mintData; + + bytes memory lockData = abi.encode(data); + vm.expectRevert(PeripheryValidation.TransactionTooOld.selector); + nonfungiblePoolManager.modifyLiquidities(lockData, block.timestamp - 1); + } } function testMint_multiPositionsInSamePriceRange() external { @@ -450,8 +841,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); @@ -461,7 +851,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // console2.log("tick", tick); // make the LPing balance of the position non-zero - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); router.donate(key, 1 ether, 1 ether, ""); uint256 token0Before = IERC20(Currency.unwrap(currency0)).balanceOf(address(this)); @@ -469,8 +859,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { INonfungiblePositionManager.MintParams memory mintParams2 = mintParams; mintParams2.recipient = address(this); - (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = - nonfungiblePoolManager.mint(mintParams2); + (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = mint(mintParams2); uint256 token0After = IERC20(Currency.unwrap(currency0)).balanceOf(address(this)); uint256 token1After = IERC20(Currency.unwrap(currency1)).balanceOf(address(this)); @@ -502,7 +891,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // mint another position in the same price range but different salt INonfungiblePositionManager.MintParams memory mintParams3 = mintParams; mintParams3.salt = bytes32(uint256(0xABCD)); - nonfungiblePoolManager.mint(mintParams3); + mint(mintParams3); assertEq( nonfungiblePoolManager.balanceOf(address(this)), @@ -546,15 +935,23 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // hence setting both amount0Min and amount1Min to 1 ether will cause slippage check failed amount0Min: 1 ether, amount1Min: 1 ether, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(mintParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = mintData; vm.expectRevert(LiquidityManagement.PriceSlippageCheckFailed.selector); - nonfungiblePoolManager.mint(mintParams); + // mint(mintParams); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); } function testMint_SamePersonMintTokensInSamePriceRange() external { @@ -578,8 +975,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); @@ -589,13 +985,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // console2.log("tick", tick); // make the LPing balance of the position non-zero - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); router.donate(key, 1 ether, 1 ether, ""); uint256 token0Before = IERC20(Currency.unwrap(currency0)).balanceOf(address(this)); uint256 token1Before = IERC20(Currency.unwrap(currency1)).balanceOf(address(this)); - (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = nonfungiblePoolManager.mint(mintParams); + (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = mint(mintParams); uint256 token0After = IERC20(Currency.unwrap(currency0)).balanceOf(address(this)); uint256 token1After = IERC20(Currency.unwrap(currency1)).balanceOf(address(this)); @@ -676,13 +1072,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); { ( @@ -719,14 +1114,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { emit IncreaseLiquidity(1, liquidityExpected, 9912256854193146, 1000000000000000000); // adding into exactly the same position - (uint128 liquidity, uint256 amount0, uint256 amount1) = nonfungiblePoolManager.increaseLiquidity( + (uint128 liquidity, uint256 amount0, uint256 amount1) = increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: 1, amount0Desired: 1 ether, amount1Desired: 1 ether, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -803,25 +1197,34 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); + + // generate modifyLiquidities data + bytes memory increaseData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.IncreaseLiquidity, + abi.encode( + INonfungiblePositionManager.IncreaseLiquidityParams({ + tokenId: 1, + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0 + }) + ) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = increaseData; + bytes memory lockData = abi.encode(data); snapStart("NonfungiblePositionManager#increaseLiquidity"); - nonfungiblePoolManager.increaseLiquidity( - INonfungiblePositionManager.IncreaseLiquidityParams({ - tokenId: 1, - amount0Desired: 1 ether, - amount1Desired: 1 ether, - amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max - }) - ); + nonfungiblePoolManager.modifyLiquidities(lockData, type(uint256).max); snapEnd(); } @@ -846,13 +1249,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); { ( @@ -890,14 +1292,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { emit IncreaseLiquidity(1, liquidityExpected, 9912256854193146, 1000000000000000000); // adding into exactly the same position - (uint128 liquidity, uint256 amount0, uint256 amount1) = nonfungiblePoolManager.increaseLiquidity( + (uint128 liquidity, uint256 amount0, uint256 amount1) = increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: 1, amount0Desired: 1 ether, amount1Desired: 1 ether, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -978,8 +1379,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); @@ -987,17 +1387,16 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // (, int24 tick,,,) = poolManager.getSlot0(key.toId()); // price = 100 i.e. tick 46054 // console2.log("tick", tick); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // adding into exactly the same position - nonfungiblePoolManager.increaseLiquidity( + increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: 1, amount0Desired: 1 ether, amount1Desired: 1 ether, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); } @@ -1023,8 +1422,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); @@ -1032,19 +1430,26 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // (, int24 tick,,,) = poolManager.getSlot0(key.toId()); // price = 100 i.e. tick 46054 // console2.log("tick", tick); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); - vm.expectRevert(LiquidityManagement.PriceSlippageCheckFailed.selector); - nonfungiblePoolManager.increaseLiquidity( - INonfungiblePositionManager.IncreaseLiquidityParams({ - tokenId: 1, - amount0Desired: 1 ether, - amount1Desired: 1 ether, - amount0Min: 0.01 ether, - amount1Min: 0, - deadline: type(uint256).max - }) + // generate modifyLiquidities data + INonfungiblePositionManager.IncreaseLiquidityParams memory params = INonfungiblePositionManager + .IncreaseLiquidityParams({ + tokenId: 1, + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0.01 ether, + amount1Min: 0 + }); + bytes memory increaseLiquidityData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.IncreaseLiquidity, abi.encode(params) + ) ); + bytes[] memory data = new bytes[](1); + data[0] = increaseLiquidityData; + vm.expectRevert(LiquidityManagement.PriceSlippageCheckFailed.selector); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); } function testDecreaseLiquidity(bytes32 salt) external { @@ -1068,14 +1473,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); ( , , @@ -1102,13 +1506,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { vm.expectEmit(true, true, true, true); emit DecreaseLiquidity(1, 1991375027067913587988, 9912256854193145, 999999999999999999); - (uint256 amount0, uint256 amount1) = nonfungiblePoolManager.decreaseLiquidity( + (uint256 amount0, uint256 amount1) = decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1164,24 +1567,33 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); - snapStart("NonfungiblePositionManager#decreaseLiquidity"); - nonfungiblePoolManager.decreaseLiquidity( - INonfungiblePositionManager.DecreaseLiquidityParams({ - tokenId: 1, - liquidity: 1991375027067913587988, - amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max - }) + mint(mintParams); + + // generate modifyLiquidities data + bytes memory decreaseData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.DecreaseLiquidity, + abi.encode( + INonfungiblePositionManager.DecreaseLiquidityParams({ + tokenId: 1, + liquidity: 1991375027067913587988, + amount0Min: 0, + amount1Min: 0 + }) + ) + ) ); + bytes[] memory data = new bytes[](1); + data[0] = decreaseData; + bytes memory lockData = abi.encode(data); + snapStart("NonfungiblePositionManager#decreaseLiquidity"); + nonfungiblePoolManager.modifyLiquidities(lockData, type(uint256).max); snapEnd(); } @@ -1206,25 +1618,26 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); - vm.expectRevert(INonfungiblePositionManager.NotOwnerOrOperator.selector); - nonfungiblePoolManager.decreaseLiquidity( - INonfungiblePositionManager.DecreaseLiquidityParams({ - tokenId: 1, - liquidity: 1991375027067913587988, - amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max - }) + // generate modifyLiquidities data + INonfungiblePositionManager.DecreaseLiquidityParams memory params = INonfungiblePositionManager + .DecreaseLiquidityParams({tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, amount1Min: 0}); + bytes memory decreaseLiquidityData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.DecreaseLiquidity, abi.encode(params) + ) ); + bytes[] memory data = new bytes[](1); + data[0] = decreaseLiquidityData; + vm.expectRevert(INonfungiblePositionManager.NotOwnerOrOperator.selector); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); } function testDecreaseLiquidity_forSomeoneElse_withoutApprove() external { @@ -1248,24 +1661,22 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); vm.prank(makeAddr("someone")); nonfungiblePoolManager.approve(address(this), 1); - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); } @@ -1291,26 +1702,26 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0 ether, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); - vm.expectRevert(LiquidityManagement.PriceSlippageCheckFailed.selector); - nonfungiblePoolManager.decreaseLiquidity( - INonfungiblePositionManager.DecreaseLiquidityParams({ - tokenId: 1, - liquidity: 1991375027067913587988, - // price 100 - amount0Min: 0.01 ether, - amount1Min: 0, - deadline: type(uint256).max - }) + // generate modifyLiquidities data + INonfungiblePositionManager.DecreaseLiquidityParams memory params = INonfungiblePositionManager + .DecreaseLiquidityParams({tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0.01 ether, amount1Min: 0}); + bytes memory decreaseLiquidityData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.DecreaseLiquidity, abi.encode(params) + ) ); + bytes[] memory data = new bytes[](1); + data[0] = decreaseLiquidityData; + vm.expectRevert(LiquidityManagement.PriceSlippageCheckFailed.selector); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); } function testDecreaseLiquidity_AccumulatedLPing() external { @@ -1334,13 +1745,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); ( , , @@ -1370,13 +1780,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { uint256 token1Before = IERC20(Currency.unwrap(currency1)).balanceOf(address(this)); console2.log("about to decrease liquidity"); - (uint256 amount0, uint256 amount1) = nonfungiblePoolManager.decreaseLiquidity( + (uint256 amount0, uint256 amount1) = decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1434,22 +1843,20 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1489,22 +1896,20 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1534,24 +1939,22 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); vm.prank(makeAddr("someone")); nonfungiblePoolManager.approve(address(this), 1); - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1587,24 +1990,22 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); vm.prank(makeAddr("someone")); nonfungiblePoolManager.approve(address(this), 1); - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1636,22 +2037,20 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988 - 1, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1683,25 +2082,23 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1739,14 +2136,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); @@ -1778,7 +2174,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { vm.expectEmit(true, true, true, true); emit Collect(1, address(this), 999999999999999999, 999999999999999999); - (uint256 amount0, uint256 amount1) = nonfungiblePoolManager.collect( + (uint256 amount0, uint256 amount1) = collect( INonfungiblePositionManager.CollectParams({ tokenId: 1, recipient: address(this), @@ -1841,14 +2237,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); @@ -1874,13 +2269,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { assertEq(tokensOwed1, 0, "Unexpected feesOwed1"); // decrease liquidity to 0 - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: _liquidity, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -1893,7 +2287,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { vm.expectEmit(true, true, true, true); emit Collect(1, address(this), 999999999999999999, 999999999999999999); - (uint256 amount0, uint256 amount1) = nonfungiblePoolManager.collect( + (uint256 amount0, uint256 amount1) = collect( INonfungiblePositionManager.CollectParams({ tokenId: 1, recipient: address(this), @@ -1950,26 +2344,33 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); + // generate modifyLiquidities data + INonfungiblePositionManager.CollectParams memory collectParams = INonfungiblePositionManager.CollectParams({ + tokenId: 1, + recipient: address(this), + amount0Max: 999999999999999999, + amount1Max: 999999999999999999 + }); + bytes memory collectData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Collect, abi.encode(collectParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = collectData; + bytes memory lockData = abi.encode(data); snapStart("NonfungiblePositionManager#collect"); - nonfungiblePoolManager.collect( - INonfungiblePositionManager.CollectParams({ - tokenId: 1, - recipient: address(this), - amount0Max: 999999999999999999, - amount1Max: 999999999999999999 - }) - ); + nonfungiblePoolManager.modifyLiquidities(lockData, type(uint256).max); snapEnd(); } @@ -1994,14 +2395,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); @@ -2026,15 +2426,22 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { assertEq(tokensOwed0, 0, "Unexpected feesOwed0"); assertEq(tokensOwed1, 0, "Unexpected feesOwed1"); - vm.expectRevert(INonfungiblePositionManager.InvalidMaxCollectAmount.selector); - nonfungiblePoolManager.collect( - INonfungiblePositionManager.CollectParams({ - tokenId: 1, - recipient: address(this), - amount0Max: 0, - amount1Max: 0 - }) + // generate modifyLiquidities data + INonfungiblePositionManager.CollectParams memory collectParams = INonfungiblePositionManager.CollectParams({ + tokenId: 1, + recipient: address(this), + amount0Max: 0, + amount1Max: 0 + }); + bytes memory collectData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Collect, abi.encode(collectParams) + ) ); + bytes[] memory data = new bytes[](1); + data[0] = collectData; + vm.expectRevert(INonfungiblePositionManager.InvalidMaxCollectAmount.selector); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); } function testCollect_partialAmount() external { @@ -2058,14 +2465,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); @@ -2096,7 +2502,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { vm.expectEmit(true, true, true, true); emit Collect(1, address(this), 0.5 ether, 0.5 ether); - (uint256 amount0, uint256 amount1) = nonfungiblePoolManager.collect( + (uint256 amount0, uint256 amount1) = collect( INonfungiblePositionManager.CollectParams({ tokenId: 1, recipient: address(this), @@ -2158,14 +2564,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); @@ -2191,15 +2596,24 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { uint256 token0Before = IERC20(Currency.unwrap(currency0)).balanceOf(address(this)); uint256 token1Before = IERC20(Currency.unwrap(currency1)).balanceOf(address(this)); + // generate modifyLiquidities data + INonfungiblePositionManager.CollectParams memory collectParams = INonfungiblePositionManager.CollectParams({ + tokenId: 1, + recipient: address(0), + amount0Max: 999999999999999999, + amount1Max: 999999999999999999 + }); + bytes memory collectData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Collect, abi.encode(collectParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = collectData; vm.expectEmit(true, true, true, true); emit Collect(1, address(this), 999999999999999999, 999999999999999999); - (uint256 amount0, uint256 amount1) = nonfungiblePoolManager.collect( - INonfungiblePositionManager.CollectParams({ - tokenId: 1, - recipient: address(0), - amount0Max: 999999999999999999, - amount1Max: 999999999999999999 - }) + (uint256 amount0, uint256 amount1) = abi.decode( + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max)[0], (uint256, uint256) ); uint256 token0After = IERC20(Currency.unwrap(currency0)).balanceOf(address(this)); @@ -2252,14 +2666,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); // make the LPing balance of the position non-zero router.donate(key, 1 ether, 1 ether, ""); @@ -2290,7 +2703,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { nonfungiblePoolManager.approve(address(this), 1); vm.expectEmit(true, true, true, true); emit Collect(1, makeAddr("someone"), 999999999999999999, 999999999999999999); - (uint256 amount0, uint256 amount1) = nonfungiblePoolManager.collect( + (uint256 amount0, uint256 amount1) = collect( INonfungiblePositionManager.CollectParams({ tokenId: 1, recipient: makeAddr("someone"), @@ -2356,24 +2769,31 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: makeAddr("someone"), - deadline: type(uint256).max + recipient: makeAddr("someone") }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); + mint(mintParams); - vm.expectRevert(INonfungiblePositionManager.NotOwnerOrOperator.selector); - nonfungiblePoolManager.collect( - INonfungiblePositionManager.CollectParams({ - tokenId: 1, - recipient: makeAddr("someone"), - amount0Max: 999999999999999999, - amount1Max: 999999999999999999 - }) + // generate modifyLiquidities data + INonfungiblePositionManager.CollectParams memory params = INonfungiblePositionManager.CollectParams({ + tokenId: 1, + recipient: makeAddr("someone"), + amount0Max: 999999999999999999, + amount1Max: 999999999999999999 + }); + bytes memory collectData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Collect, abi.encode(params) + ) ); + bytes[] memory data = new bytes[](1); + data[0] = collectData; + + vm.expectRevert(INonfungiblePositionManager.NotOwnerOrOperator.selector); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); } function testCollect_positionWithoutRewardsAndLiquidity() external { @@ -2397,25 +2817,23 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }); uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(key, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint(mintParams); - nonfungiblePoolManager.decreaseLiquidity( + mint(mintParams); + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); - nonfungiblePoolManager.collect( + collect( INonfungiblePositionManager.CollectParams({ tokenId: 1, recipient: address(this), @@ -2444,7 +2862,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(poolKey, sqrtPriceX96, new bytes(0)); - nonfungiblePoolManager.mint( + mint( INonfungiblePositionManager.MintParams({ poolKey: poolKey, tickLower: 46053, @@ -2454,8 +2872,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }) ); @@ -2476,13 +2893,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { router.donate(poolKey, 1 ether, 1 ether, ""); // 3. withdraw all liquidity, it's the only position in both ticks so ticks are cleared - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -2526,14 +2942,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { } // 5. addLiquidity to the position again should not revert and work as expected - nonfungiblePoolManager.increaseLiquidity( + increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: 1, amount0Desired: 1 ether, amount1Desired: 1 ether, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -2584,7 +2999,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); poolManager.initialize(poolKey, sqrtPriceX96, new bytes(0)); - (, uint128 liquidity1,,) = nonfungiblePoolManager.mint( + (, uint128 liquidity1,,) = mint( INonfungiblePositionManager.MintParams({ poolKey: poolKey, tickLower: 46053, @@ -2594,20 +3009,18 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }) ); // decrease liquidity to 1 to better manipulate feeGrowthGlobal { - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 1, liquidity: liquidity1 - 1, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -2636,7 +3049,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { // 4. another position // Consuming 1 ether amount0 & 0 ether amount1 because tick lower > active tick - nonfungiblePoolManager.mint( + mint( INonfungiblePositionManager.MintParams({ poolKey: poolKey, tickLower: 46055, @@ -2646,8 +3059,7 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { amount1Desired: 1 ether, amount0Min: 0, amount1Min: 0, - recipient: address(this), - deadline: type(uint256).max + recipient: address(this) }) ); @@ -2675,13 +3087,12 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { CLPosition.Info memory info = poolManager.getPosition(poolKey.toId(), address(nonfungiblePoolManager), 46055, 46058, bytes32(0)); - nonfungiblePoolManager.decreaseLiquidity( + decreaseLiquidity( INonfungiblePositionManager.DecreaseLiquidityParams({ tokenId: 2, liquidity: info.liquidity - 1, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -2721,14 +3132,13 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { { // add(0) to trigger position info sync - nonfungiblePoolManager.increaseLiquidity( + increaseLiquidity( INonfungiblePositionManager.IncreaseLiquidityParams({ tokenId: 2, amount0Desired: 0 ether, amount1Desired: 0 ether, amount0Min: 0, - amount1Min: 0, - deadline: type(uint256).max + amount1Min: 0 }) ); @@ -2758,4 +3168,741 @@ contract NonFungiblePositionManagerTest is TokenFixture, Test, GasSnapshot { assertEq(tokenOwed1, 1000000000000000000, "Unexpected tokenOwed1"); } } + + function testBatchMint() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + { + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: bytes32(0), + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: makeAddr("someone") + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + nonfungiblePoolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(mintParams) + ) + ); + bytes[] memory data = new bytes[](3); + data[0] = mintData; + // set current close data + data[1] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency0) + ) + ); + data[2] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency1) + ) + ); + snapStart("NonFungiblePositionManagerBatch#mint"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + } + + { + ( + uint96 nonce, + address operator, + PoolId poolId, + Currency _currency0, + Currency _currency1, + uint24 fee, + int24 tickLower, + int24 tickUpper, + uint128 liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1, + bytes32 salt + ) = nonfungiblePoolManager.positions(1); + assertEq(nonce, 0, "Unexpected nonce"); + assertEq(operator, address(0), "Unexpected operator"); + assertEq(PoolId.unwrap(poolId), PoolId.unwrap(key.toId()), "Unexpected poolId"); + assertEq(Currency.unwrap(_currency0), Currency.unwrap(currency0), "Unexpected currency0"); + assertEq(Currency.unwrap(_currency1), Currency.unwrap(currency1), "Unexpected currency1"); + assertEq(fee, 3000, "Unexpected fee"); + assertEq(tickLower, 46053, "Unexpected tickLower"); + assertEq(tickUpper, 46055, "Unexpected tickUpper"); + assertEq(liquidity, 1991375027067913587988, "Unexpected liquidity"); + assertEq(feeGrowthInside0LastX128, 0, "Unexpected feeGrowthInside0LastX128"); + assertEq(feeGrowthInside1LastX128, 0, "Unexpected feeGrowthInside1LastX128"); + assertEq(tokensOwed0, 0, "Unexpected tokensOwed0"); + assertEq(tokensOwed1, 0, "Unexpected tokensOwed1"); + assertEq(salt, bytes32(0), "Unexpected salt"); + string memory expectTokenURI = + string.concat("https://nft.pancakeswap.com/v4/", block.chainid.toString(), "/1"); + string memory realTokenURI = nonfungiblePoolManager.tokenURI(1); + assertEq(expectTokenURI, realTokenURI, "Unexpected tokenURI"); + } + } + + function testBatchMintWithoutCloseCurrency() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + { + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: bytes32(0), + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: makeAddr("someone") + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + + nonfungiblePoolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // generate modifyLiquidities data + bytes memory mintData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(mintParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = mintData; + + snapStart("NonFungiblePositionManagerBatch#mintWithoutCloseCurrency"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + } + + { + ( + uint96 nonce, + address operator, + PoolId poolId, + Currency _currency0, + Currency _currency1, + uint24 fee, + int24 tickLower, + int24 tickUpper, + uint128 liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1, + bytes32 salt + ) = nonfungiblePoolManager.positions(1); + assertEq(nonce, 0, "Unexpected nonce"); + assertEq(operator, address(0), "Unexpected operator"); + assertEq(PoolId.unwrap(poolId), PoolId.unwrap(key.toId()), "Unexpected poolId"); + assertEq(Currency.unwrap(_currency0), Currency.unwrap(currency0), "Unexpected currency0"); + assertEq(Currency.unwrap(_currency1), Currency.unwrap(currency1), "Unexpected currency1"); + assertEq(fee, 3000, "Unexpected fee"); + assertEq(tickLower, 46053, "Unexpected tickLower"); + assertEq(tickUpper, 46055, "Unexpected tickUpper"); + assertEq(liquidity, 1991375027067913587988, "Unexpected liquidity"); + assertEq(feeGrowthInside0LastX128, 0, "Unexpected feeGrowthInside0LastX128"); + assertEq(feeGrowthInside1LastX128, 0, "Unexpected feeGrowthInside1LastX128"); + assertEq(tokensOwed0, 0, "Unexpected tokensOwed0"); + assertEq(tokensOwed1, 0, "Unexpected tokensOwed1"); + assertEq(salt, bytes32(0), "Unexpected salt"); + string memory expectTokenURI = + string.concat("https://nft.pancakeswap.com/v4/", block.chainid.toString(), "/1"); + string memory realTokenURI = nonfungiblePoolManager.tokenURI(1); + assertEq(expectTokenURI, realTokenURI, "Unexpected tokenURI"); + } + } + + function testBatchMintAndIncreaseLiquidity() external { + bytes32 salt = keccak256("salt"); + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: salt, + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // generate multicall data + bytes[] memory data = new bytes[](4); + data[0] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(mintParams) + ) + ); + + INonfungiblePositionManager.IncreaseLiquidityParams memory increaseParams = INonfungiblePositionManager + .IncreaseLiquidityParams({ + tokenId: 1, + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0 + }); + data[1] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.IncreaseLiquidity, abi.encode(increaseParams) + ) + ); + + // set currency close data + data[2] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency0) + ) + ); + data[3] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency1) + ) + ); + + // batch mint and increase liquidity + snapStart("NonFungiblePositionManagerBatch#BatchMintAndIncreaseLiquidity"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + + { + ( + , + , + , + , + , + , + , + , + uint128 _liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1, + ) = nonfungiblePoolManager.positions(1); + + uint128 liquidityExpected = LiquidityAmounts.getLiquidityForAmounts( + sqrtPriceX96, TickMath.getSqrtRatioAtTick(46053), TickMath.getSqrtRatioAtTick(46055), 1 ether, 1 ether + ); + + assertEq(_liquidity, 1991375027067913587988 + liquidityExpected, "Unexpected liquidity"); + assertEq(feeGrowthInside0LastX128, 0, "Unexpected feeGrowthInside0LastX128"); + assertEq(feeGrowthInside1LastX128, 0, "Unexpected feeGrowthInside1LastX128"); + assertEq(tokensOwed0, 0, "Unexpected feesOwed0"); + assertEq(tokensOwed1, 0, "Unexpected feesOwed1"); + } + assertEq(poolManager.getLiquidity(key.toId()), 2 * 1991375027067913587988, "Unexpected liquidity for the pool"); + assertEq( + poolManager.getLiquidity(key.toId(), address(nonfungiblePoolManager), 46053, 46055, salt), + 1991375027067913587988 * 2, + "Unexpected liquidity for current position" + ); + + assertEq( + nonfungiblePoolManager.balanceOf(address(this)), 1, "Unexpected balance of the position owner after mint" + ); + + assertEq(nonfungiblePoolManager.ownerOf(1), address(this), "Unexpected owner of the position"); + + { + ( + , + , + , + , + , + , + , + , + uint128 _liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1, + ) = nonfungiblePoolManager.positions(1); + assertEq(_liquidity, 1991375027067913587988 * 2, "Unexpected liquidity"); + assertEq(feeGrowthInside0LastX128, 0, "Unexpected feeGrowthInside0LastX128"); + assertEq(feeGrowthInside1LastX128, 0, "Unexpected feeGrowthInside1LastX128"); + assertEq(tokensOwed0, 0, "Unexpected tokensOwed0"); + assertEq(tokensOwed1, 0, "Unexpected tokensOwed1"); + } + } + + function testBatchMintIncreaseAndDecreaseLiquidity(bytes32 salt) external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: salt, + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // generate multicall data + bytes[] memory data = new bytes[](5); + data[0] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Mint, abi.encode(mintParams) + ) + ); + + INonfungiblePositionManager.IncreaseLiquidityParams memory increaseParams = INonfungiblePositionManager + .IncreaseLiquidityParams({ + tokenId: 1, + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0 + }); + data[1] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.IncreaseLiquidity, abi.encode(increaseParams) + ) + ); + + INonfungiblePositionManager.DecreaseLiquidityParams memory decreaseParams = INonfungiblePositionManager + .DecreaseLiquidityParams({ + tokenId: 1, + liquidity: 1991375027067913587988 + 1991375027067913587987, + amount0Min: 0, + amount1Min: 0 + }); + + data[2] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.DecreaseLiquidity, abi.encode(decreaseParams) + ) + ); + + // set currency close data + data[3] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency0) + ) + ); + data[4] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency1) + ) + ); + + // batch mint and increase liquidity, then decrease liquidity + snapStart("NonFungiblePositionManagerBatch#batchMintIncreaseAndDecreaseLiquidity"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + + { + ( + , + , + , + , + , + , + , + , + uint128 _liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1, + ) = nonfungiblePoolManager.positions(1); + + assertEq(_liquidity, 1, "Unexpected liquidity"); + assertEq(feeGrowthInside0LastX128, 0, "Unexpected feeGrowthInside0LastX128"); + assertEq(feeGrowthInside1LastX128, 0, "Unexpected feeGrowthInside1LastX128"); + assertEq(tokensOwed0, 0, "Unexpected feesOwed0"); + assertEq(tokensOwed1, 0, "Unexpected feesOwed1"); + } + } + + function testBatchIncreaseLiquidityWithoutCloseCurrency() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: bytes32(0), + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + // nonfungiblePoolManager.mint(mintParams); + mint(mintParams); + + // generate modifyLiquidities data + INonfungiblePositionManager.IncreaseLiquidityParams memory increaseParams = INonfungiblePositionManager + .IncreaseLiquidityParams({ + tokenId: 1, + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0 + }); + bytes memory increaseData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.IncreaseLiquidity, abi.encode(increaseParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = increaseData; + + snapStart("NonFungiblePositionManagerBatch#increaseLiquidityWithoutCloseCurrency"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + } + + function testBatchIncreaseLiquidity() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: bytes32(0), + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + // nonfungiblePoolManager.mint(mintParams); + mint(mintParams); + + // generate modifyLiquidities data + INonfungiblePositionManager.IncreaseLiquidityParams memory increaseParams = INonfungiblePositionManager + .IncreaseLiquidityParams({ + tokenId: 1, + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0 + }); + bytes memory increaseData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.IncreaseLiquidity, abi.encode(increaseParams) + ) + ); + bytes[] memory data = new bytes[](3); + data[0] = increaseData; + // set current close data + data[1] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency0) + ) + ); + data[2] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency1) + ) + ); + + snapStart("NonFungiblePositionManagerBatch#increaseLiquidity"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + } + + function testBatchDecreaseLiquidityWithoutCloseCurrency() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: bytes32(0), + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // nonfungiblePoolManager.mint(mintParams); + mint(mintParams); + + // generate modifyLiquidities data + INonfungiblePositionManager.DecreaseLiquidityParams memory decreaseParams = INonfungiblePositionManager + .DecreaseLiquidityParams({tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, amount1Min: 0}); + bytes memory decreaseData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.DecreaseLiquidity, abi.encode(decreaseParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = decreaseData; + + snapStart("NonFungiblePositionManagerBatch#decreaseLiquidityWithoutCloseCurrency"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + } + + function testBatchDecreaseLiquidity() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: bytes32(0), + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // nonfungiblePoolManager.mint(mintParams); + mint(mintParams); + + // generate modifyLiquidities data + INonfungiblePositionManager.DecreaseLiquidityParams memory decreaseParams = INonfungiblePositionManager + .DecreaseLiquidityParams({tokenId: 1, liquidity: 1991375027067913587988, amount0Min: 0, amount1Min: 0}); + bytes memory decreaseData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.DecreaseLiquidity, abi.encode(decreaseParams) + ) + ); + bytes[] memory data = new bytes[](3); + data[0] = decreaseData; + + // set current close data + data[1] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency0) + ) + ); + data[2] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency1) + ) + ); + + snapStart("NonFungiblePositionManagerBatch#decreaseLiquidity"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + } + + function testBatchCollectWithoutCloseCurrency() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: bytes32(0), + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // nonfungiblePoolManager.mint(mintParams); + mint(mintParams); + // make the LPing balance of the position non-zero + router.donate(key, 1 ether, 1 ether, ""); + + INonfungiblePositionManager.CollectParams memory collectParams = INonfungiblePositionManager.CollectParams({ + tokenId: 1, + recipient: address(this), + amount0Max: 999999999999999999, + amount1Max: 999999999999999999 + }); + // generate modifyLiquidities data + bytes memory collectData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Collect, abi.encode(collectParams) + ) + ); + bytes[] memory data = new bytes[](1); + data[0] = collectData; + snapStart("NonFungiblePositionManagerBatch#collectWithoutCloseCurrency"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + } + + function testBatchCollect() external { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + // 0 ~ 15 hookRegistrationMap = nil + // 16 ~ 24 tickSpacing = 1 + parameters: bytes32(uint256(0x10000)) + }); + + INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({ + poolKey: key, + tickLower: 46053, + tickUpper: 46055, + salt: bytes32(0), + amount0Desired: 1 ether, + amount1Desired: 1 ether, + amount0Min: 0, + amount1Min: 0, + recipient: address(this) + }); + + uint160 sqrtPriceX96 = uint160(10 * FixedPoint96.Q96); + poolManager.initialize(key, sqrtPriceX96, new bytes(0)); + + // nonfungiblePoolManager.mint(mintParams); + mint(mintParams); + // make the LPing balance of the position non-zero + router.donate(key, 1 ether, 1 ether, ""); + + INonfungiblePositionManager.CollectParams memory collectParams = INonfungiblePositionManager.CollectParams({ + tokenId: 1, + recipient: address(this), + amount0Max: 999999999999999999, + amount1Max: 999999999999999999 + }); + // generate modifyLiquidities data + bytes memory collectData = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.Collect, abi.encode(collectParams) + ) + ); + bytes[] memory data = new bytes[](3); + data[0] = collectData; + // set current close data + data[1] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency0) + ) + ); + data[2] = abi.encode( + INonfungiblePositionManager.CallbackData( + INonfungiblePositionManager.CallbackDataType.CloseCurrency, abi.encode(currency1) + ) + ); + snapStart("NonFungiblePositionManagerBatch#collect"); + nonfungiblePoolManager.modifyLiquidities(abi.encode(data), type(uint256).max); + snapEnd(); + } }