From 7998e6c391b77d2a8455f902097b0180b579db1b Mon Sep 17 00:00:00 2001 From: Alice <34962750+hensha256@users.noreply.github.com> Date: Thu, 16 Nov 2023 17:41:01 +0100 Subject: [PATCH] Part 1: Improve forge tests (#407) * Revert messages * Common take and settle contract for tests * improving swap and take tests * add asserts for modify position * extra asserts in modify position * asserts in donate test * More deployment helpers * native set up in deployer * simplify initialize tests * few more corrections * more cleanup * remove console logs * snapshots and test fix post merge * snapshots * accidentally pushed foundry.toml * PR comments --- .../SwapMath_oneForZero_exactInCapped.snap | 2 +- .../SwapMath_oneForZero_exactInPartial.snap | 2 +- .../SwapMath_oneForZero_exactOutCapped.snap | 2 +- .../SwapMath_oneForZero_exactOutPartial.snap | 2 +- .../SwapMath_zeroForOne_exactInCapped.snap | 2 +- .../SwapMath_zeroForOne_exactInPartial.snap | 2 +- .../SwapMath_zeroForOne_exactOutCapped.snap | 2 +- .../SwapMath_zeroForOne_exactOutPartial.snap | 2 +- ...swap hook, already cached dynamic fee.snap | 2 +- .../cached dynamic fee, no hooks.snap | 2 +- .forge-snapshots/donate gas with 1 token.snap | 2 +- .../donate gas with 2 tokens.snap | 2 +- .../erc20 collect protocol fees.snap | 2 +- ..._gasCostForAmount0WhereRoundUpIsFalse.snap | 2 +- ...a_gasCostForAmount0WhereRoundUpIsTrue.snap | 2 +- ...iceFromInput_zeroForOneEqualsFalseGas.snap | 2 +- ...riceFromInput_zeroForOneEqualsTrueGas.snap | 2 +- ...ceFromOutput_zeroForOneEqualsFalseGas.snap | 2 +- ...iceFromOutput_zeroForOneEqualsTrueGas.snap | 2 +- .forge-snapshots/initialize.snap | 2 +- .forge-snapshots/mint with empty hook.snap | 2 +- .forge-snapshots/mint with native token.snap | 2 +- .forge-snapshots/mint.snap | 2 +- .../native collect protocol fees.snap | 2 +- .../poolManager bytecode size.snap | 2 +- .forge-snapshots/simple swap with native.snap | 1 + .forge-snapshots/simple swap.snap | 2 +- ...p against liquidity with native token.snap | 2 +- .forge-snapshots/swap against liquidity.snap | 2 +- .../swap burn claim for input.snap | 2 +- .../swap mint output as claim.snap | 2 +- .forge-snapshots/swap with dynamic fee.snap | 2 +- .forge-snapshots/swap with hooks.snap | 2 +- .../update dynamic fee in before swap.snap | 2 +- .gas-snapshot | 320 +++---- src/libraries/SafeCast.sol | 12 +- src/libraries/SqrtPriceMath.sol | 27 +- ...icFeesTest.sol => DynamicFeesTestHook.sol} | 2 +- src/test/PoolDonateTest.sol | 50 +- src/test/PoolModifyPositionTest.sol | 51 +- src/test/PoolSwapTest.sol | 86 +- src/test/PoolTakeTest.sol | 63 +- src/test/PoolTestBase.sol | 51 ++ src/test/TestInvalidERC20.sol | 6 +- test/Claims.t.sol | 8 +- test/DynamicFees.t.sol | 92 +- test/Fees.t.sol | 67 +- test/Hooks.t.sol | 78 +- test/LockersLibrary.t.sol | 16 +- test/PoolManager.t.sol | 808 +++--------------- test/PoolManagerInitialize.t.sol | 316 +++++++ test/PoolManagerReentrancyTest.t.sol | 8 +- test/SafeCast.t.sol | 8 +- test/SqrtPriceMath.t.sol | 20 +- test/utils/Deployers.sol | 131 ++- test/utils/TokenFixture.sol | 20 - 56 files changed, 995 insertions(+), 1312 deletions(-) create mode 100644 .forge-snapshots/simple swap with native.snap rename src/test/{DynamicFeesTest.sol => DynamicFeesTestHook.sol} (94%) create mode 100644 src/test/PoolTestBase.sol create mode 100644 test/PoolManagerInitialize.t.sol delete mode 100644 test/utils/TokenFixture.sol diff --git a/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap b/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap index 27f29bcd9..f21f32fc4 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactInCapped.snap @@ -1 +1 @@ -2222 \ No newline at end of file +2228 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap b/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap index cef801d00..35365ccc9 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactInPartial.snap @@ -1 +1 @@ -3050 \ No newline at end of file +3058 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap b/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap index 99227d62b..633016cc7 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactOutCapped.snap @@ -1 +1 @@ -1980 \ No newline at end of file +1986 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap b/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap index cef801d00..35365ccc9 100644 --- a/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap +++ b/.forge-snapshots/SwapMath_oneForZero_exactOutPartial.snap @@ -1 +1 @@ -3050 \ No newline at end of file +3058 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap b/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap index 8377f9934..85fe943d8 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactInCapped.snap @@ -1 +1 @@ -2211 \ No newline at end of file +2217 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap b/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap index c290966ea..42edc1656 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactInPartial.snap @@ -1 +1 @@ -3201 \ No newline at end of file +3215 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap b/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap index 14d9c9090..9c5c74bae 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactOutCapped.snap @@ -1 +1 @@ -1969 \ No newline at end of file +1975 \ No newline at end of file diff --git a/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap b/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap index c290966ea..42edc1656 100644 --- a/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap +++ b/.forge-snapshots/SwapMath_zeroForOne_exactOutPartial.snap @@ -1 +1 @@ -3201 \ No newline at end of file +3215 \ No newline at end of file diff --git a/.forge-snapshots/before swap hook, already cached dynamic fee.snap b/.forge-snapshots/before swap hook, already cached dynamic fee.snap index 9f3cc9e0f..065c3c3e8 100644 --- a/.forge-snapshots/before swap hook, already cached dynamic fee.snap +++ b/.forge-snapshots/before swap hook, already cached dynamic fee.snap @@ -1 +1 @@ -172021 \ No newline at end of file +189798 \ No newline at end of file diff --git a/.forge-snapshots/cached dynamic fee, no hooks.snap b/.forge-snapshots/cached dynamic fee, no hooks.snap index b244975aa..d25279b12 100644 --- a/.forge-snapshots/cached dynamic fee, no hooks.snap +++ b/.forge-snapshots/cached dynamic fee, no hooks.snap @@ -1 +1 @@ -158656 \ No newline at end of file +145314 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 1 token.snap b/.forge-snapshots/donate gas with 1 token.snap index bf2422d17..2bd9c1d42 100644 --- a/.forge-snapshots/donate gas with 1 token.snap +++ b/.forge-snapshots/donate gas with 1 token.snap @@ -1 +1 @@ -76947 \ No newline at end of file +134915 \ No newline at end of file diff --git a/.forge-snapshots/donate gas with 2 tokens.snap b/.forge-snapshots/donate gas with 2 tokens.snap index c10ec2110..4fe05417c 100644 --- a/.forge-snapshots/donate gas with 2 tokens.snap +++ b/.forge-snapshots/donate gas with 2 tokens.snap @@ -1 +1 @@ -134745 \ No newline at end of file +186636 \ No newline at end of file diff --git a/.forge-snapshots/erc20 collect protocol fees.snap b/.forge-snapshots/erc20 collect protocol fees.snap index e37d505d3..f6af67bf2 100644 --- a/.forge-snapshots/erc20 collect protocol fees.snap +++ b/.forge-snapshots/erc20 collect protocol fees.snap @@ -1 +1 @@ -25033 \ No newline at end of file +27033 \ No newline at end of file diff --git a/.forge-snapshots/getAmount0Delta_gasCostForAmount0WhereRoundUpIsFalse.snap b/.forge-snapshots/getAmount0Delta_gasCostForAmount0WhereRoundUpIsFalse.snap index 4f9548135..a9d3bc092 100644 --- a/.forge-snapshots/getAmount0Delta_gasCostForAmount0WhereRoundUpIsFalse.snap +++ b/.forge-snapshots/getAmount0Delta_gasCostForAmount0WhereRoundUpIsFalse.snap @@ -1 +1 @@ -517 \ No newline at end of file +523 \ No newline at end of file diff --git a/.forge-snapshots/getAmount0Delta_gasCostForAmount0WhereRoundUpIsTrue.snap b/.forge-snapshots/getAmount0Delta_gasCostForAmount0WhereRoundUpIsTrue.snap index 38017008e..acec591c5 100644 --- a/.forge-snapshots/getAmount0Delta_gasCostForAmount0WhereRoundUpIsTrue.snap +++ b/.forge-snapshots/getAmount0Delta_gasCostForAmount0WhereRoundUpIsTrue.snap @@ -1 +1 @@ -668 \ No newline at end of file +674 \ No newline at end of file diff --git a/.forge-snapshots/getNextSqrtPriceFromInput_zeroForOneEqualsFalseGas.snap b/.forge-snapshots/getNextSqrtPriceFromInput_zeroForOneEqualsFalseGas.snap index 4af6ab79f..73623d101 100644 --- a/.forge-snapshots/getNextSqrtPriceFromInput_zeroForOneEqualsFalseGas.snap +++ b/.forge-snapshots/getNextSqrtPriceFromInput_zeroForOneEqualsFalseGas.snap @@ -1 +1 @@ -598 \ No newline at end of file +600 \ No newline at end of file diff --git a/.forge-snapshots/getNextSqrtPriceFromInput_zeroForOneEqualsTrueGas.snap b/.forge-snapshots/getNextSqrtPriceFromInput_zeroForOneEqualsTrueGas.snap index 83249da5a..bc93f9457 100644 --- a/.forge-snapshots/getNextSqrtPriceFromInput_zeroForOneEqualsTrueGas.snap +++ b/.forge-snapshots/getNextSqrtPriceFromInput_zeroForOneEqualsTrueGas.snap @@ -1 +1 @@ -784 \ No newline at end of file +786 \ No newline at end of file diff --git a/.forge-snapshots/getNextSqrtPriceFromOutput_zeroForOneEqualsFalseGas.snap b/.forge-snapshots/getNextSqrtPriceFromOutput_zeroForOneEqualsFalseGas.snap index fea1e3e73..fb5f8f013 100644 --- a/.forge-snapshots/getNextSqrtPriceFromOutput_zeroForOneEqualsFalseGas.snap +++ b/.forge-snapshots/getNextSqrtPriceFromOutput_zeroForOneEqualsFalseGas.snap @@ -1 +1 @@ -882 \ No newline at end of file +890 \ No newline at end of file diff --git a/.forge-snapshots/getNextSqrtPriceFromOutput_zeroForOneEqualsTrueGas.snap b/.forge-snapshots/getNextSqrtPriceFromOutput_zeroForOneEqualsTrueGas.snap index 28621d35a..064233657 100644 --- a/.forge-snapshots/getNextSqrtPriceFromOutput_zeroForOneEqualsTrueGas.snap +++ b/.forge-snapshots/getNextSqrtPriceFromOutput_zeroForOneEqualsTrueGas.snap @@ -1 +1 @@ -542 \ No newline at end of file +473 \ No newline at end of file diff --git a/.forge-snapshots/initialize.snap b/.forge-snapshots/initialize.snap index 8d294b77c..9ac3a2a48 100644 --- a/.forge-snapshots/initialize.snap +++ b/.forge-snapshots/initialize.snap @@ -1 +1 @@ -38101 \ No newline at end of file +53843 \ No newline at end of file diff --git a/.forge-snapshots/mint with empty hook.snap b/.forge-snapshots/mint with empty hook.snap index 0c1e3b435..d6003b2d1 100644 --- a/.forge-snapshots/mint with empty hook.snap +++ b/.forge-snapshots/mint with empty hook.snap @@ -1 +1 @@ -264055 \ No newline at end of file +319184 \ No newline at end of file diff --git a/.forge-snapshots/mint with native token.snap b/.forge-snapshots/mint with native token.snap index 5625d1c31..783a03fba 100644 --- a/.forge-snapshots/mint with native token.snap +++ b/.forge-snapshots/mint with native token.snap @@ -1 +1 @@ -232085 \ No newline at end of file +198926 \ No newline at end of file diff --git a/.forge-snapshots/mint.snap b/.forge-snapshots/mint.snap index 40a9b9c9b..b81efe3e7 100644 --- a/.forge-snapshots/mint.snap +++ b/.forge-snapshots/mint.snap @@ -1 +1 @@ -250721 \ No newline at end of file +202041 \ No newline at end of file diff --git a/.forge-snapshots/native collect protocol fees.snap b/.forge-snapshots/native collect protocol fees.snap index bcb5ad547..5a35b1041 100644 --- a/.forge-snapshots/native collect protocol fees.snap +++ b/.forge-snapshots/native collect protocol fees.snap @@ -1 +1 @@ -36699 \ No newline at end of file +38699 \ No newline at end of file diff --git a/.forge-snapshots/poolManager bytecode size.snap b/.forge-snapshots/poolManager bytecode size.snap index 9a83da7b1..e2ace385d 100644 --- a/.forge-snapshots/poolManager bytecode size.snap +++ b/.forge-snapshots/poolManager bytecode size.snap @@ -1 +1 @@ -24630 \ No newline at end of file +24805 \ No newline at end of file diff --git a/.forge-snapshots/simple swap with native.snap b/.forge-snapshots/simple swap with native.snap new file mode 100644 index 000000000..38fb428ce --- /dev/null +++ b/.forge-snapshots/simple swap with native.snap @@ -0,0 +1 @@ +190722 \ No newline at end of file diff --git a/.forge-snapshots/simple swap.snap b/.forge-snapshots/simple swap.snap index 16acb3c0d..d46a556a3 100644 --- a/.forge-snapshots/simple swap.snap +++ b/.forge-snapshots/simple swap.snap @@ -1 +1 @@ -129248 \ No newline at end of file +202425 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity with native token.snap b/.forge-snapshots/swap against liquidity with native token.snap index a113b8e41..cdf8b425d 100644 --- a/.forge-snapshots/swap against liquidity with native token.snap +++ b/.forge-snapshots/swap against liquidity with native token.snap @@ -1 +1 @@ -106413 \ No newline at end of file +121318 \ No newline at end of file diff --git a/.forge-snapshots/swap against liquidity.snap b/.forge-snapshots/swap against liquidity.snap index db6d40dd3..d822c9d1b 100644 --- a/.forge-snapshots/swap against liquidity.snap +++ b/.forge-snapshots/swap against liquidity.snap @@ -1 +1 @@ -91101 \ No newline at end of file +109143 \ No newline at end of file diff --git a/.forge-snapshots/swap burn claim for input.snap b/.forge-snapshots/swap burn claim for input.snap index c71be887b..db9426bff 100644 --- a/.forge-snapshots/swap burn claim for input.snap +++ b/.forge-snapshots/swap burn claim for input.snap @@ -1 +1 @@ -107809 \ No newline at end of file +127882 \ No newline at end of file diff --git a/.forge-snapshots/swap mint output as claim.snap b/.forge-snapshots/swap mint output as claim.snap index efa017d3c..470895cbb 100644 --- a/.forge-snapshots/swap mint output as claim.snap +++ b/.forge-snapshots/swap mint output as claim.snap @@ -1 +1 @@ -147831 \ No newline at end of file +212594 \ No newline at end of file diff --git a/.forge-snapshots/swap with dynamic fee.snap b/.forge-snapshots/swap with dynamic fee.snap index 5d7141706..478c6b86a 100644 --- a/.forge-snapshots/swap with dynamic fee.snap +++ b/.forge-snapshots/swap with dynamic fee.snap @@ -1 +1 @@ -171256 \ No newline at end of file +189038 \ No newline at end of file diff --git a/.forge-snapshots/swap with hooks.snap b/.forge-snapshots/swap with hooks.snap index 5cbb8c711..fe90801ad 100644 --- a/.forge-snapshots/swap with hooks.snap +++ b/.forge-snapshots/swap with hooks.snap @@ -1 +1 @@ -91079 \ No newline at end of file +109122 \ No newline at end of file diff --git a/.forge-snapshots/update dynamic fee in before swap.snap b/.forge-snapshots/update dynamic fee in before swap.snap index da48ab18f..7ada52a85 100644 --- a/.forge-snapshots/update dynamic fee in before swap.snap +++ b/.forge-snapshots/update dynamic fee in before swap.snap @@ -1 +1 @@ -177851 \ No newline at end of file +195628 \ No newline at end of file diff --git a/.gas-snapshot b/.gas-snapshot index b2dafc174..9ccb0d944 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,23 +1,23 @@ -ClaimsTest:testCanBurn(uint256) (runs: 100, μ: 32978, ~: 33305) -ClaimsTest:testCanTransfer(uint256) (runs: 100, μ: 50757, ~: 51848) -ClaimsTest:testCanTransferToZeroAddress() (gas: 51105) -ClaimsTest:testCatchesOverflowOnTransfer() (gas: 45569) -ClaimsTest:testCatchesUnderflowOnBurn(uint256) (runs: 100, μ: 39932, ~: 40927) -ClaimsTest:testCatchesUnderflowOnTransfer(uint256) (runs: 100, μ: 40057, ~: 41052) -ClaimsTest:testTransferToClaimsContractFails() (gas: 36413) -FeesTest:testCollectFees() (gas: 622560) -FeesTest:testHookWithdrawFeeProtocolWithdrawFee(uint16,uint16) (runs: 100, μ: 489854, ~: 495940) -FeesTest:testInitializeAllFees(uint16,uint16,uint16,uint16) (runs: 100, μ: 164080, ~: 161213) -FeesTest:testInitializeBothHookFee(uint16,uint16) (runs: 100, μ: 85965, ~: 93440) +ClaimsTest:testCanBurn(uint256) (runs: 100, μ: 32977, ~: 33304) +ClaimsTest:testCanTransfer(uint256) (runs: 100, μ: 50745, ~: 51836) +ClaimsTest:testCanTransferToZeroAddress() (gas: 51088) +ClaimsTest:testCatchesOverflowOnTransfer() (gas: 45550) +ClaimsTest:testCatchesUnderflowOnBurn(uint256) (runs: 100, μ: 39917, ~: 40912) +ClaimsTest:testCatchesUnderflowOnTransfer(uint256) (runs: 100, μ: 40054, ~: 41049) +ClaimsTest:testTransferToClaimsContractFails() (gas: 36392) +FeesTest:testCollectFees() (gas: 609556) +FeesTest:testHookWithdrawFeeProtocolWithdrawFee(uint16,uint16) (runs: 100, μ: 484798, ~: 490490) +FeesTest:testInitializeAllFees(uint16,uint16,uint16,uint16) (runs: 100, μ: 140571, ~: 136939) +FeesTest:testInitializeBothHookFee(uint16,uint16) (runs: 100, μ: 86164, ~: 93440) FeesTest:testInitializeFailsNoHook() (gas: 20233) -FeesTest:testInitializeHookProtocolSwapFee(uint16,uint16) (runs: 100, μ: 126882, ~: 133651) -FeesTest:testInitializeHookSwapFee(uint16) (runs: 100, μ: 68614, ~: 70884) -FeesTest:testInitializeHookWithdrawFee(uint16) (runs: 100, μ: 68200, ~: 70924) -FeesTest:testInitializeWithSwapProtocolFeeAndHookFeeDifferentDirections() (gas: 584240) -FeesTest:testNoHookProtocolFee(uint16,uint16) (runs: 100, μ: 758142, ~: 765926) -FeesTest:testProtocolFeeOnWithdrawalRemainsZeroIfNoHookWithdrawalFeeSet(uint16,uint16) (runs: 100, μ: 470360, ~: 472269) -FeesTest:testProtocolSwapFeeAndHookSwapFeeSameDirection() (gas: 604631) -FeesTest:testSwapWithProtocolFeeAllAndHookFeeAllButOnlySwapFlag() (gas: 643655) +FeesTest:testInitializeHookProtocolSwapFee(uint16,uint16) (runs: 100, μ: 103467, ~: 109343) +FeesTest:testInitializeHookSwapFee(uint16) (runs: 100, μ: 68387, ~: 70884) +FeesTest:testInitializeHookWithdrawFee(uint16) (runs: 100, μ: 68654, ~: 70924) +FeesTest:testInitializeWithSwapProtocolFeeAndHookFeeDifferentDirections() (gas: 569221) +FeesTest:testNoHookProtocolFee(uint16,uint16) (runs: 100, μ: 761575, ~: 767899) +FeesTest:testProtocolFeeOnWithdrawalRemainsZeroIfNoHookWithdrawalFeeSet(uint16,uint16) (runs: 100, μ: 464839, ~: 466748) +FeesTest:testProtocolSwapFeeAndHookSwapFeeSameDirection() (gas: 589612) +FeesTest:testSwapWithProtocolFeeAllAndHookFeeAllButOnlySwapFlag() (gas: 638678) FullMathTest:testResultOverflowsHelper() (gas: 3964) FullMathTest:test_mulDivRounding(uint256,uint256,uint256) (runs: 100, μ: 4728, ~: 4445) FullMathTest:test_mulDivRoundingUp_fuzz(uint256,uint256,uint256) (runs: 100, μ: 4655, ~: 4687) @@ -38,23 +38,23 @@ FullMathTest:test_mulDiv_revertsWithOverflowingNumeratorAndZeroDenominator() (ga FullMathTest:test_mulDiv_validAllMaxInputs() (gas: 736) FullMathTest:test_mulDiv_validWithPhantomOverflow() (gas: 1167) FullMathTest:test_mulDiv_validWithoutPhantomOverflow() (gas: 853) -HooksTest:testAfterDonateInvalidReturn() (gas: 425221) -HooksTest:testAfterInitializeInvalidReturn() (gas: 1581366) -HooksTest:testAfterModifyPositionInvalidReturn() (gas: 274009) -HooksTest:testAfterSwapInvalidReturn() (gas: 130810) -HooksTest:testBeforeDonateInvalidReturn() (gas: 425263) -HooksTest:testBeforeInitializeInvalidReturn() (gas: 1539326) -HooksTest:testBeforeModifyPositionInvalidReturn() (gas: 101298) -HooksTest:testBeforeSwapInvalidReturn() (gas: 102343) -HooksTest:testDonateSucceedsWithHook() (gas: 750830) +HooksTest:testAfterDonateInvalidReturn() (gas: 89120) +HooksTest:testAfterInitializeInvalidReturn() (gas: 100731) +HooksTest:testAfterModifyPositionInvalidReturn() (gas: 168689) +HooksTest:testAfterSwapInvalidReturn() (gas: 162167) +HooksTest:testBeforeDonateInvalidReturn() (gas: 89162) +HooksTest:testBeforeInitializeInvalidReturn() (gas: 49217) +HooksTest:testBeforeModifyPositionInvalidReturn() (gas: 64783) +HooksTest:testBeforeSwapInvalidReturn() (gas: 91596) +HooksTest:testDonateSucceedsWithHook() (gas: 448027) HooksTest:testGas() (gas: 38358) -HooksTest:testInitializeSucceedsWithHook() (gas: 6679073) +HooksTest:testInitializeSucceedsWithHook() (gas: 208231) HooksTest:testInvalidIfNoFlags() (gas: 1108) HooksTest:testIsValidHookAddressAnyFlags() (gas: 2099) HooksTest:testIsValidHookAddressZeroAddress() (gas: 443) HooksTest:testIsValidIfDynamicFee() (gas: 935) -HooksTest:testModifyPositionSucceedsWithHook() (gas: 409271) -HooksTest:testSwapSucceedsWithHook() (gas: 810395) +HooksTest:testModifyPositionSucceedsWithHook() (gas: 301337) +HooksTest:testSwapSucceedsWithHook() (gas: 373007) HooksTest:testValidateHookAddressAfterDonate(uint152) (runs: 100, μ: 1854, ~: 1854) HooksTest:testValidateHookAddressAfterInitialize(uint152) (runs: 100, μ: 1855, ~: 1855) HooksTest:testValidateHookAddressAfterModify(uint152) (runs: 100, μ: 1835, ~: 1835) @@ -69,16 +69,16 @@ HooksTest:testValidateHookAddressBeforeInitialize(uint152) (runs: 100, μ: 1837, HooksTest:testValidateHookAddressBeforeInitializeAfterModify(uint152) (runs: 100, μ: 1784, ~: 1784) HooksTest:testValidateHookAddressBeforeModify(uint152) (runs: 100, μ: 1863, ~: 1863) HooksTest:testValidateHookAddressBeforeSwap(uint152) (runs: 100, μ: 1856, ~: 1856) -HooksTest:testValidateHookAddressFailsAllHooks(uint152,uint8) (runs: 100, μ: 4787, ~: 4729) -HooksTest:testValidateHookAddressFailsNoHooks(uint152,uint8) (runs: 100, μ: 4805, ~: 4729) +HooksTest:testValidateHookAddressFailsAllHooks(uint152,uint8) (runs: 100, μ: 4784, ~: 4729) +HooksTest:testValidateHookAddressFailsNoHooks(uint152,uint8) (runs: 100, μ: 4795, ~: 4729) HooksTest:testValidateHookAddressNoHooks(uint152) (runs: 100, μ: 1845, ~: 1845) LockersLibrary:testLockerLengthAndNonzeroDeltaCount() (gas: 52392) LockersLibrary:test_clear(address[]) (runs: 100, μ: 114024, ~: 111101) LockersLibrary:test_decrementNonzeroDeltaCount() (gas: 1157) -LockersLibrary:test_decrementNonzeroDeltaCountFuzz(uint8) (runs: 100, μ: 121371, ~: 45330) +LockersLibrary:test_decrementNonzeroDeltaCountFuzz(uint8) (runs: 100, μ: 110701, ~: 53214) LockersLibrary:test_getCurrentLocker_multipleAddressesFuzz(address[]) (runs: 100, μ: 273024, ~: 265967) LockersLibrary:test_incrementNonzeroDeltaCount() (gas: 807) -LockersLibrary:test_incrementNonzeroDeltaCountFuzz(uint8) (runs: 100, μ: 72366, ~: 26996) +LockersLibrary:test_incrementNonzeroDeltaCountFuzz(uint8) (runs: 100, μ: 66000, ~: 31700) LockersLibrary:test_pop() (gas: 1770) LockersLibrary:test_pop_multipleAddressesFuzz(address[]) (runs: 100, μ: 240601, ~: 234397) LockersLibrary:test_push() (gas: 1421) @@ -86,135 +86,135 @@ LockersLibrary:test_push_multipleAddressesFuzz(address[]) (runs: 100, μ: 250569 OwnedTest:testConstructor(address) (runs: 100, μ: 166413, ~: 166413) OwnedTest:testSetOwnerFromNonOwner(address,address) (runs: 100, μ: 167372, ~: 167571) OwnedTest:testSetOwnerFromOwner(address,address) (runs: 100, μ: 169642, ~: 169642) +PoolManagerInitializeTest:test_initialize((address,address,uint24,int24,address),uint160) (runs: 100, μ: 21815, ~: 15963) +PoolManagerInitializeTest:test_initialize_failsIfTickSpaceNeg(uint160) (runs: 100, μ: 20764, ~: 20764) +PoolManagerInitializeTest:test_initialize_failsIfTickSpaceTooLarge(uint160) (runs: 100, μ: 21747, ~: 21747) +PoolManagerInitializeTest:test_initialize_failsIfTickSpaceZero(uint160) (runs: 100, μ: 20801, ~: 20801) +PoolManagerInitializeTest:test_initialize_failsWithIncorrectSelectors() (gas: 1016152) +PoolManagerInitializeTest:test_initialize_fetchFeeWhenController(uint160) (runs: 100, μ: 95683, ~: 95796) +PoolManagerInitializeTest:test_initialize_forNativeTokens(uint160) (runs: 100, μ: 72060, ~: 72285) +PoolManagerInitializeTest:test_initialize_gas() (gas: 87067) +PoolManagerInitializeTest:test_initialize_revertsWhenPoolAlreadyInitialized(uint160) (runs: 100, μ: 65008, ~: 65131) +PoolManagerInitializeTest:test_initialize_revertsWithIdenticalTokens(uint160) (runs: 100, μ: 22624, ~: 22624) +PoolManagerInitializeTest:test_initialize_revertsWithSameTokenCombo(uint160) (runs: 100, μ: 27702, ~: 27702) +PoolManagerInitializeTest:test_initialize_succeedsWithCorrectSelectors() (gas: 1012968) +PoolManagerInitializeTest:test_initialize_succeedsWithEmptyHooks(uint160) (runs: 100, μ: 978650, ~: 978753) +PoolManagerInitializeTest:test_initialize_succeedsWithHooks(uint160) (runs: 100, μ: 8937393460516839818, ~: 8937393460516839937) +PoolManagerInitializeTest:test_initialize_succeedsWithMaxTickSpacing(uint160) (runs: 100, μ: 65781, ~: 65915) PoolManagerReentrancyTest:testParallelLocker() (gas: 1928644) PoolManagerReentrancyTest:testSimpleLinearLocker() (gas: 390198) PoolManagerReentrancyTest:testTokenLocker() (gas: 566947) -PoolManagerReentrancyTest:testTokenRevert() (gas: 619841) -PoolManagerTest:test_bytecodeSize() (gas: 12236) -PoolManagerTest:test_collectProtocolFees_ERC20_allowsOwnerToAccumulateFees_gas() (gas: 582958) -PoolManagerTest:test_collectProtocolFees_ERC20_returnsAllFeesIf0IsProvidedAsParameter() (gas: 552030) -PoolManagerTest:test_collectProtocolFees_initializesWithProtocolFeeIfCalled() (gas: 109145) -PoolManagerTest:test_collectProtocolFees_nativeToken_allowsOwnerToAccumulateFees_gas() (gas: 575330) -PoolManagerTest:test_collectProtocolFees_nativeToken_returnsAllFeesIf0IsProvidedAsParameter() (gas: 542397) -PoolManagerTest:test_donate_OneToken_gas() (gas: 452140) -PoolManagerTest:test_donate_failsIfNoLiquidity(uint160) (runs: 100, μ: 64518, ~: 64651) -PoolManagerTest:test_donate_failsIfNotInitialized() (gas: 28042) -PoolManagerTest:test_donate_failsWithIncorrectSelectors() (gas: 1420105) -PoolManagerTest:test_donate_succeedsForNativeTokensWhenPoolHasLiquidity() (gas: 440700) -PoolManagerTest:test_donate_succeedsWhenPoolHasLiquidity() (gas: 493156) -PoolManagerTest:test_donate_succeedsWithCorrectSelectors() (gas: 1393184) -PoolManagerTest:test_feeControllerSet() (gas: 39595) -PoolManagerTest:test_fetchFeeWhenController(uint160) (runs: 100, μ: 112707, ~: 112809) -PoolManagerTest:test_getPosition() (gas: 366837) -PoolManagerTest:test_initialize((address,address,uint24,int24,address),uint160) (runs: 100, μ: 18626, ~: 13942) -PoolManagerTest:test_initialize_failsIfTickSpaceNeg(uint160) (runs: 100, μ: 15866, ~: 15866) -PoolManagerTest:test_initialize_failsIfTickSpaceTooLarge(uint160) (runs: 100, μ: 16790, ~: 16790) -PoolManagerTest:test_initialize_failsIfTickSpaceZero(uint160) (runs: 100, μ: 15888, ~: 15888) -PoolManagerTest:test_initialize_failsWithIncorrectSelectors() (gas: 984435) -PoolManagerTest:test_initialize_forNativeTokens(uint160) (runs: 100, μ: 59139, ~: 59421) -PoolManagerTest:test_initialize_gas() (gas: 73772) -PoolManagerTest:test_initialize_revertsWhenPoolAlreadyInitialized(uint160) (runs: 100, μ: 50365, ~: 50478) -PoolManagerTest:test_initialize_revertsWithIdenticalTokens(uint160) (runs: 100, μ: 13399, ~: 13399) -PoolManagerTest:test_initialize_revertsWithSameTokenCombo(uint160) (runs: 100, μ: 49228, ~: 49341) -PoolManagerTest:test_initialize_succeedsWithCorrectSelectors() (gas: 980799) -PoolManagerTest:test_initialize_succeedsWithEmptyHooks(uint160) (runs: 100, μ: 946581, ~: 946697) -PoolManagerTest:test_initialize_succeedsWithHooks(uint160) (runs: 100, μ: 8937393460516837929, ~: 8937393460516838058) -PoolManagerTest:test_initialize_succeedsWithMaxTickSpacing(uint160) (runs: 100, μ: 50708, ~: 50840) +PoolManagerReentrancyTest:testTokenRevert() (gas: 585641) +PoolManagerTest:test_bytecodeSize() (gas: 12185) +PoolManagerTest:test_collectProtocolFees_ERC20_allowsOwnerToAccumulateFees_gas() (gas: 289458) +PoolManagerTest:test_collectProtocolFees_ERC20_returnsAllFeesIf0IsProvidedAsParameter() (gas: 254611) +PoolManagerTest:test_collectProtocolFees_initializesWithProtocolFeeIfCalled() (gas: 87808) +PoolManagerTest:test_collectProtocolFees_nativeToken_allowsOwnerToAccumulateFees_gas() (gas: 284617) +PoolManagerTest:test_collectProtocolFees_nativeToken_returnsAllFeesIf0IsProvidedAsParameter() (gas: 248486) +PoolManagerTest:test_donate_OneToken_gas() (gas: 148330) +PoolManagerTest:test_donate_failsIfNoLiquidity(uint160) (runs: 100, μ: 114825, ~: 114937) +PoolManagerTest:test_donate_failsIfNotInitialized() (gas: 59830) +PoolManagerTest:test_donate_failsWithIncorrectSelectors() (gas: 1427126) +PoolManagerTest:test_donate_succeedsForNativeTokensWhenPoolHasLiquidity() (gas: 161042) +PoolManagerTest:test_donate_succeedsWhenPoolHasLiquidity() (gas: 208247) +PoolManagerTest:test_donate_succeedsWithCorrectSelectors() (gas: 1395867) +PoolManagerTest:test_feeControllerSet() (gas: 5068465) +PoolManagerTest:test_getPosition() (gas: 24376) PoolManagerTest:test_lock_EmitsCorrectId() (gas: 18848) -PoolManagerTest:test_lock_NoOpIsOk() (gas: 50372) -PoolManagerTest:test_mint_failsIfNotInitialized() (gas: 29346) -PoolManagerTest:test_mint_failsWithIncorrectSelectors() (gas: 1176562) -PoolManagerTest:test_mint_gas() (gas: 304522) -PoolManagerTest:test_mint_succeedsForNativeTokensIfInitialized(uint160) (runs: 100, μ: 250535, ~: 267847) -PoolManagerTest:test_mint_succeedsIfInitialized(uint160) (runs: 100, μ: 254269, ~: 251775) -PoolManagerTest:test_mint_succeedsWithCorrectSelectors() (gas: 1211340) -PoolManagerTest:test_mint_succeedsWithHooksIfInitialized(uint160) (runs: 100, μ: 8937393460517082508, ~: 8937393460517084221) -PoolManagerTest:test_mint_withHooks_gas() (gas: 1209319) -PoolManagerTest:test_mint_withNative_gas() (gas: 285824) -PoolManagerTest:test_setProtocolFee_updatesProtocolFeeForInitializedPool() (gas: 118065) -PoolManagerTest:test_swap_GasMintClaimIfOutputNotTaken() (gas: 509280) -PoolManagerTest:test_swap_GasUseClaimAsInput() (gas: 581487) -PoolManagerTest:test_swap_againstLiqWithNative_gas() (gas: 587762) -PoolManagerTest:test_swap_againstLiq_gas() (gas: 536037) -PoolManagerTest:test_swap_failsIfNotInitialized(uint160) (runs: 100, μ: 31576, ~: 31576) -PoolManagerTest:test_swap_failsWithIncorrectSelectors() (gas: 1278753) -PoolManagerTest:test_swap_gas() (gas: 484642) -PoolManagerTest:test_swap_succeedsIfInitialized() (gas: 480745) -PoolManagerTest:test_swap_succeedsWithCorrectSelectors() (gas: 1412700) -PoolManagerTest:test_swap_succeedsWithHooksIfInitialized() (gas: 8937393460517308918) -PoolManagerTest:test_swap_succeedsWithNativeTokensIfInitialized() (gas: 463563) -PoolManagerTest:test_swap_withHooks_gas() (gas: 1440773) -PoolManagerTest:test_swap_withNative_gas() (gas: 469474) -PoolManagerTest:test_take_failsWithInvalidTokensThatDoNotReturnTrueOnTransfer() (gas: 888847) -PoolManagerTest:test_take_failsWithNoLiquidity() (gas: 53518) -PoolManagerTest:test_take_succeedsWithPoolWithLiquidity() (gas: 469168) -PoolManagerTest:test_take_succeedsWithPoolWithLiquidityWithNativeToken() (gas: 438960) +PoolManagerTest:test_lock_NoOpIsOk() (gas: 50332) +PoolManagerTest:test_mint_failsIfNotInitialized() (gas: 35317) +PoolManagerTest:test_mint_failsWithIncorrectSelectors() (gas: 1266991) +PoolManagerTest:test_mint_gas() (gas: 199973) +PoolManagerTest:test_mint_succeedsForNativeTokensIfInitialized(uint160) (runs: 100, μ: 166868, ~: 166868) +PoolManagerTest:test_mint_succeedsIfInitialized(uint160) (runs: 100, μ: 169963, ~: 169963) +PoolManagerTest:test_mint_succeedsWithCorrectSelectors() (gas: 1287264) +PoolManagerTest:test_mint_succeedsWithHooksIfInitialized(uint160) (runs: 100, μ: 8937393460517122061, ~: 8937393460517123618) +PoolManagerTest:test_mint_withHooks_gas() (gas: 1283808) +PoolManagerTest:test_mint_withNative_gas() (gas: 197549) +PoolManagerTest:test_setProtocolFee_updatesProtocolFeeForInitializedPool() (gas: 70461) +PoolManagerTest:test_swap_GasMintClaimIfOutputNotTaken() (gas: 216651) +PoolManagerTest:test_swap_GasUseClaimAsInput() (gas: 322262) +PoolManagerTest:test_swap_againstLiqWithNative_gas() (gas: 329462) +PoolManagerTest:test_swap_againstLiq_gas() (gas: 287756) +PoolManagerTest:test_swap_failsIfNotInitialized(uint160) (runs: 100, μ: 66369, ~: 66369) +PoolManagerTest:test_swap_failsWithIncorrectSelectors() (gas: 1427217) +PoolManagerTest:test_swap_gas() (gas: 200468) +PoolManagerTest:test_swap_succeedsIfInitialized() (gas: 182286) +PoolManagerTest:test_swap_succeedsWithCorrectSelectors() (gas: 1415774) +PoolManagerTest:test_swap_succeedsWithHooksIfInitialized() (gas: 8937393460517299315) +PoolManagerTest:test_swap_succeedsWithNativeTokensIfInitialized() (gas: 170592) +PoolManagerTest:test_swap_withHooks_gas() (gas: 1460713) +PoolManagerTest:test_swap_withNative_gas() (gas: 191105) +PoolManagerTest:test_take_failsWithInvalidTokensThatDoNotReturnTrueOnTransfer() (gas: 896667) +PoolManagerTest:test_take_failsWithNoLiquidity() (gas: 11038376) +PoolManagerTest:test_take_succeedsWithPoolWithLiquidity() (gas: 124513) +PoolManagerTest:test_take_succeedsWithPoolWithLiquidityWithNativeToken() (gas: 118130) PoolTest:testModifyPosition(uint160,(address,int24,int24,int128,int24)) (runs: 100, μ: 19917, ~: 7542) PoolTest:testPoolInitialize(uint160,uint16,uint16,uint24) (runs: 100, μ: 16461, ~: 6475) -PoolTest:testSwap(uint160,uint24,(int24,bool,int256,uint160)) (runs: 100, μ: 212151, ~: 7852) -SafeCastTest:testToInt128(int256) (runs: 100, μ: 1533, ~: 511) -SafeCastTest:testToInt128(uint256) (runs: 100, μ: 1167, ~: 390) -SafeCastTest:testToInt256(uint256) (runs: 100, μ: 784, ~: 450) -SafeCastTest:testToUint160(uint256) (runs: 100, μ: 1117, ~: 454) -SqrtPriceMathTestTest:test_getAmount0Delta_gasCostForAmount0WhereRoundUpIsFalse() (gas: 78524) -SqrtPriceMathTestTest:test_getAmount0Delta_gasCostForAmount0WhereRoundUpIsTrue() (gas: 78699) -SqrtPriceMathTestTest:test_getAmount0Delta_returns0IfLiquidityIs0() (gas: 916) -SqrtPriceMathTestTest:test_getAmount0Delta_returns0IfPricesAreEqual() (gas: 891) -SqrtPriceMathTestTest:test_getAmount0Delta_returns0_1Amount1ForPriceOf1To1_21() (gas: 1513) -SqrtPriceMathTestTest:test_getAmount0Delta_worksForPricesThatOverflow() (gas: 2048) +PoolTest:testSwap(uint160,uint24,(int24,bool,int256,uint160)) (runs: 100, μ: 212320, ~: 7852) +SafeCastTest:testToInt128(int256) (runs: 100, μ: 1575, ~: 511) +SafeCastTest:testToInt128(uint256) (runs: 100, μ: 1150, ~: 390) +SafeCastTest:testToInt256(uint256) (runs: 100, μ: 760, ~: 450) +SafeCastTest:testToUint160(uint256) (runs: 100, μ: 1098, ~: 454) +SqrtPriceMathTestTest:test_getAmount0Delta_gasCostForAmount0WhereRoundUpIsFalse() (gas: 78530) +SqrtPriceMathTestTest:test_getAmount0Delta_gasCostForAmount0WhereRoundUpIsTrue() (gas: 78705) +SqrtPriceMathTestTest:test_getAmount0Delta_returns0IfLiquidityIs0() (gas: 922) +SqrtPriceMathTestTest:test_getAmount0Delta_returns0IfPricesAreEqual() (gas: 897) +SqrtPriceMathTestTest:test_getAmount0Delta_returns0_1Amount1ForPriceOf1To1_21() (gas: 1525) +SqrtPriceMathTestTest:test_getAmount0Delta_worksForPricesThatOverflow() (gas: 2060) SqrtPriceMathTestTest:test_getAmount1Delta_gasCostForAmount1WhereRoundUpIsFalse() (gas: 78560) SqrtPriceMathTestTest:test_getAmount1Delta_gasCostForAmount1WhereRoundUpIsTrue() (gas: 78668) SqrtPriceMathTestTest:test_getAmount1Delta_returns0IfLiquidityIs0() (gas: 888) SqrtPriceMathTestTest:test_getAmount1Delta_returns0IfPricesAreEqual() (gas: 862) SqrtPriceMathTestTest:test_getAmount1Delta_returns0_1Amount1ForPriceOf1To1_21() (gas: 1563) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_amountInGreaterThanType_uint96_maxAndZeroForOneEqualsTrue() (gas: 1055) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_anyInputAmountCannotUnderflowThePrice() (gas: 1052) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_canReturn1WithEnoughAmountInAndZeroForOneEqualsTrue() (gas: 963) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_inputAmountOf0_1Currency0() (gas: 1079) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_inputAmountOf0_1Currency1() (gas: 893) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_returnsInputPriceIfAmountInIsZeroAndZeroForOneEqualsFalse() (gas: 880) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_returnsInputPriceIfAmountInIsZeroAndZeroForOneEqualsTrue() (gas: 633) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_returnsTheMinimumPriceForMaxInputs() (gas: 1233) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_revertsIfInputAmountOverflowsThePrice() (gas: 3698) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_revertsIfLiquidityIsZero() (gas: 3199) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_revertsIfPriceIsZero() (gas: 3201) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_zeroForOneEqualsFalseGas() (gas: 78625) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_zeroForOneEqualsTrueGas() (gas: 78789) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_outputAmountOf0_1Currency0() (gas: 813) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_outputAmountOf0_1Currency1() (gas: 1156) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_puzzlingEchidnaTest() (gas: 3414) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_returnsInputPriceIfAmountInIsZeroAndZeroForOneEqualsFalse() (gas: 609) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_returnsInputPriceIfAmountInIsZeroAndZeroForOneEqualsTrue() (gas: 876) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfAmountOutIsImpossibleInOneForZeroDirection() (gas: 3447) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfAmountOutIsImpossibleInZeroForOneDirection() (gas: 3511) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfLiquidityIsZero() (gas: 3221) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfOutputAmountIsExactlyTheVirtualReservesOfCurrency0() (gas: 3414) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfOutputAmountIsExactlyTheVirtualReservesOfCurrency1() (gas: 3478) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfOutputAmountIsGreaterThanTheVirtualReservesOfCurrency0() (gas: 3465) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfOutputAmountIsGreaterThanTheVirtualReservesOfCurrency1() (gas: 3478) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfPriceIsZero() (gas: 3158) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_succeedsIfOutputAmountIsJustLessThanTheVirtualReservesOfCurrency1() (gas: 825) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_zeroForOneEqualsFalseGas() (gas: 78887) -SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_zeroForOneEqualsTrueGas() (gas: 78570) -SqrtPriceMathTestTest:test_swapComputation_sqrtPTimessqrtQOverflows() (gas: 2375) -SwapMathTest:test_amountOut_isCappedAtTheDesiredAmountOut() (gas: 3414) -SwapMathTest:test_computeSwapStep_Invariants(uint160,uint160,uint128,int256,uint24) (runs: 100, μ: 7968, ~: 7751) -SwapMathTest:test_entireInputAmountTakenAsFee() (gas: 3490) -SwapMathTest:test_exactAmountIn_oneForZero_thatGetsCappedAtPriceTargetIn() (gas: 3451) -SwapMathTest:test_exactAmountIn_oneForZero_thatIsFullySpentIn() (gas: 4384) -SwapMathTest:test_exactAmountOut_oneForZero_thatGetsCappedAtPriceTargetIn() (gas: 3408) -SwapMathTest:test_exactAmountOut_oneForZero_thatIsFullyReceivedIn() (gas: 4919) -SwapMathTest:test_oneForZero_handlesIntermediateInsufficientLiquidityInExactOutputCase() (gas: 2760) -SwapMathTest:test_swapOneForZero_exactInCapped() (gas: 80113) -SwapMathTest:test_swapOneForZero_exactInPartial() (gas: 80963) -SwapMathTest:test_swapOneForZero_exactOutCapped() (gas: 79892) -SwapMathTest:test_swapOneForZero_exactOutPartial() (gas: 80943) -SwapMathTest:test_swapZeroForOne_exactInCapped() (gas: 80125) -SwapMathTest:test_swapZeroForOne_exactInPartial() (gas: 81157) -SwapMathTest:test_swapZeroForOne_exactOutCapped() (gas: 79860) -SwapMathTest:test_swapZeroForOne_exactOutPartial() (gas: 81161) -SwapMathTest:test_targetPriceOf1UsesPartialInputAmount() (gas: 2781) -SwapMathTest:test_zeroForOne_handlesIntermediateInsufficientLiquidityInExactOutputCase() (gas: 2749) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_amountInGreaterThanType_uint96_maxAndZeroForOneEqualsTrue() (gas: 1057) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_anyInputAmountCannotUnderflowThePrice() (gas: 1054) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_canReturn1WithEnoughAmountInAndZeroForOneEqualsTrue() (gas: 965) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_inputAmountOf0_1Currency0() (gas: 1081) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_inputAmountOf0_1Currency1() (gas: 895) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_returnsInputPriceIfAmountInIsZeroAndZeroForOneEqualsFalse() (gas: 882) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_returnsInputPriceIfAmountInIsZeroAndZeroForOneEqualsTrue() (gas: 635) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_returnsTheMinimumPriceForMaxInputs() (gas: 1235) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_revertsIfInputAmountOverflowsThePrice() (gas: 3739) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_revertsIfLiquidityIsZero() (gas: 3240) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_revertsIfPriceIsZero() (gas: 3257) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_zeroForOneEqualsFalseGas() (gas: 78627) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromInput_zeroForOneEqualsTrueGas() (gas: 78791) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_outputAmountOf0_1Currency0() (gas: 744) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_outputAmountOf0_1Currency1() (gas: 1164) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_puzzlingEchidnaTest() (gas: 3477) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_returnsInputPriceIfAmountInIsZeroAndZeroForOneEqualsFalse() (gas: 611) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_returnsInputPriceIfAmountInIsZeroAndZeroForOneEqualsTrue() (gas: 807) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfAmountOutIsImpossibleInOneForZeroDirection() (gas: 3486) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfAmountOutIsImpossibleInZeroForOneDirection() (gas: 3513) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfLiquidityIsZero() (gas: 3262) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfOutputAmountIsExactlyTheVirtualReservesOfCurrency0() (gas: 3477) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfOutputAmountIsExactlyTheVirtualReservesOfCurrency1() (gas: 3528) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfOutputAmountIsGreaterThanTheVirtualReservesOfCurrency0() (gas: 3521) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfOutputAmountIsGreaterThanTheVirtualReservesOfCurrency1() (gas: 3517) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_revertsIfPriceIsZero() (gas: 3214) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_succeedsIfOutputAmountIsJustLessThanTheVirtualReservesOfCurrency1() (gas: 756) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_zeroForOneEqualsFalseGas() (gas: 78895) +SqrtPriceMathTestTest:test_getNextSqrtPriceFromOutput_zeroForOneEqualsTrueGas() (gas: 78501) +SqrtPriceMathTestTest:test_swapComputation_sqrtPTimessqrtQOverflows() (gas: 2383) +SwapMathTest:test_amountOut_isCappedAtTheDesiredAmountOut() (gas: 3351) +SwapMathTest:test_computeSwapStep_Invariants(uint160,uint160,uint128,int256,uint24) (runs: 100, μ: 8028, ~: 8050) +SwapMathTest:test_entireInputAmountTakenAsFee() (gas: 3498) +SwapMathTest:test_exactAmountIn_oneForZero_thatGetsCappedAtPriceTargetIn() (gas: 3459) +SwapMathTest:test_exactAmountIn_oneForZero_thatIsFullySpentIn() (gas: 4394) +SwapMathTest:test_exactAmountOut_oneForZero_thatGetsCappedAtPriceTargetIn() (gas: 3416) +SwapMathTest:test_exactAmountOut_oneForZero_thatIsFullyReceivedIn() (gas: 4947) +SwapMathTest:test_oneForZero_handlesIntermediateInsufficientLiquidityInExactOutputCase() (gas: 2766) +SwapMathTest:test_swapOneForZero_exactInCapped() (gas: 80119) +SwapMathTest:test_swapOneForZero_exactInPartial() (gas: 80971) +SwapMathTest:test_swapOneForZero_exactOutCapped() (gas: 79898) +SwapMathTest:test_swapOneForZero_exactOutPartial() (gas: 80951) +SwapMathTest:test_swapZeroForOne_exactInCapped() (gas: 80131) +SwapMathTest:test_swapZeroForOne_exactInPartial() (gas: 81171) +SwapMathTest:test_swapZeroForOne_exactOutCapped() (gas: 79866) +SwapMathTest:test_swapZeroForOne_exactOutPartial() (gas: 81175) +SwapMathTest:test_targetPriceOf1UsesPartialInputAmount() (gas: 2787) +SwapMathTest:test_zeroForOne_handlesIntermediateInsufficientLiquidityInExactOutputCase() (gas: 2755) TestBalanceDelta:testAdd(int128,int128,int128,int128) (runs: 100, μ: 4750, ~: 4750) TestBalanceDelta:testSub(int128,int128,int128,int128) (runs: 100, μ: 4724, ~: 4724) TestBalanceDelta:testToBalanceDelta() (gas: 968) @@ -250,13 +250,13 @@ TestDelegateCall:testCannotDelegateCallPrivateMethodWithModifier() (gas: 10215) TestDelegateCall:testDelegateCallNoModifier() (gas: 5734) TestDelegateCall:testDelegateCallWithModifier() (gas: 10149) TestDelegateCall:testGasOverhead() (gas: 13709) -TestDynamicFees:testCacheDynamicFeeAndSwap() (gas: 254137) -TestDynamicFees:testDynamicFeeAndBeforeSwapHook() (gas: 248422) -TestDynamicFees:testDynamicFeesCacheNoOtherHooks() (gas: 200499) -TestDynamicFees:testPoolInitializeFailsWithTooLargeFee() (gas: 1519121) -TestDynamicFees:testSwapWorks() (gas: 205814) -TestDynamicFees:testUpdateFailsWithTooLargeFee() (gas: 27197) -TestDynamicFees:testUpdateRevertsOnStaticFeePool() (gas: 1540150) +TestDynamicFees:testCacheDynamicFeeAndSwap() (gas: 271914) +TestDynamicFees:testDynamicFeeAndBeforeSwapHook() (gas: 266199) +TestDynamicFees:testDynamicFeesCacheNoOtherHooks() (gas: 491901) +TestDynamicFees:testPoolInitializeFailsWithTooLargeFee() (gas: 1741103) +TestDynamicFees:testSwapWorks() (gas: 220615) +TestDynamicFees:testUpdateFailsWithTooLargeFee() (gas: 27209) +TestDynamicFees:testUpdateRevertsOnStaticFeePool() (gas: 65543) TickBitmapTest:test_flipTick_flipsOnlyTheSpecifiedTick() (gas: 16607) TickBitmapTest:test_flipTick_gasCostOfFlippingATickThatResultsInDeletingAWord() (gas: 78619) TickBitmapTest:test_flipTick_gasCostOfFlippingFirstTickInWordToInitialized() (gas: 100560) diff --git a/src/libraries/SafeCast.sol b/src/libraries/SafeCast.sol index 4f797f25f..5eb5bd474 100644 --- a/src/libraries/SafeCast.sol +++ b/src/libraries/SafeCast.sol @@ -4,25 +4,29 @@ pragma solidity ^0.8.20; /// @title Safe casting methods /// @notice Contains methods for safely casting between types library SafeCast { + error SafeCastOverflow(); + /// @notice Cast a uint256 to a uint160, revert on overflow /// @param y The uint256 to be downcasted /// @return z The downcasted integer, now type uint160 function toUint160(uint256 y) internal pure returns (uint160 z) { - require((z = uint160(y)) == y); + z = uint160(y); + if (z != y) revert SafeCastOverflow(); } /// @notice Cast a int256 to a int128, revert on overflow or underflow /// @param y The int256 to be downcasted /// @return z The downcasted integer, now type int128 function toInt128(int256 y) internal pure returns (int128 z) { - require((z = int128(y)) == y); + z = int128(y); + if (z != y) revert SafeCastOverflow(); } /// @notice Cast a uint256 to a int256, revert on overflow /// @param y The uint256 to be casted /// @return z The casted integer, now type int256 function toInt256(uint256 y) internal pure returns (int256 z) { - require(y <= uint256(type(int256).max)); + if (y > uint256(type(int256).max)) revert SafeCastOverflow(); z = int256(y); } @@ -30,7 +34,7 @@ library SafeCast { /// @param y The uint256 to be downcasted /// @return z The downcasted integer, now type int128 function toInt128(uint256 y) internal pure returns (int128 z) { - require(y <= uint128(type(int128).max)); + if (y > uint128(type(int128).max)) revert SafeCastOverflow(); z = int128(int256(y)); } } diff --git a/src/libraries/SqrtPriceMath.sol b/src/libraries/SqrtPriceMath.sol index d36a259e5..a391012c7 100644 --- a/src/libraries/SqrtPriceMath.sol +++ b/src/libraries/SqrtPriceMath.sol @@ -12,6 +12,11 @@ import {FixedPoint96} from "./FixedPoint96.sol"; library SqrtPriceMath { using SafeCast for uint256; + error InvalidPriceOrLiquidity(); + error InvalidPrice(); + error NotEnoughLiquidity(); + error PriceOverflow(); + /// @notice Gets the next sqrt price given a delta of currency0 /// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least /// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the @@ -34,8 +39,8 @@ library SqrtPriceMath { if (add) { unchecked { - uint256 product; - if ((product = amount * sqrtPX96) / amount == sqrtPX96) { + uint256 product = amount * sqrtPX96; + if (product / amount == sqrtPX96) { uint256 denominator = numerator1 + product; if (denominator >= numerator1) { // always fits in 160 bits @@ -47,10 +52,10 @@ library SqrtPriceMath { return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96) + amount)); } else { unchecked { - uint256 product; + uint256 product = amount * sqrtPX96; // if the product overflows, we know the denominator underflows // in addition, we must check that the denominator does not underflow - require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product); + if (product / amount != sqrtPX96 || numerator1 <= product) revert PriceOverflow(); uint256 denominator = numerator1 - product; return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160(); } @@ -89,9 +94,11 @@ library SqrtPriceMath { : FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity) ); - require(sqrtPX96 > quotient); + if (sqrtPX96 <= quotient) revert NotEnoughLiquidity(); // always fits 160 bits - return uint160(sqrtPX96 - quotient); + unchecked { + return uint160(sqrtPX96 - quotient); + } } } @@ -107,8 +114,7 @@ library SqrtPriceMath { pure returns (uint160 sqrtQX96) { - require(sqrtPX96 > 0); - require(liquidity > 0); + if (sqrtPX96 == 0 || liquidity == 0) revert InvalidPriceOrLiquidity(); // round to make sure that we don't pass the target price return zeroForOne @@ -128,8 +134,7 @@ library SqrtPriceMath { pure returns (uint160 sqrtQX96) { - require(sqrtPX96 > 0); - require(liquidity > 0); + if (sqrtPX96 == 0 || liquidity == 0) revert InvalidPriceOrLiquidity(); // round to make sure that we pass the target price return zeroForOne @@ -156,7 +161,7 @@ library SqrtPriceMath { uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION; uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96; - require(sqrtRatioAX96 > 0); + if (sqrtRatioAX96 == 0) revert InvalidPrice(); return roundUp ? UnsafeMath.divRoundingUp(FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), sqrtRatioAX96) diff --git a/src/test/DynamicFeesTest.sol b/src/test/DynamicFeesTestHook.sol similarity index 94% rename from src/test/DynamicFeesTest.sol rename to src/test/DynamicFeesTestHook.sol index 756bab730..f350cc3af 100644 --- a/src/test/DynamicFeesTest.sol +++ b/src/test/DynamicFeesTestHook.sol @@ -7,7 +7,7 @@ import {PoolKey} from "../types/PoolKey.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; import {IHooks} from "../interfaces/IHooks.sol"; -contract DynamicFeesTest is BaseTestHooks, IDynamicFeeManager { +contract DynamicFeesTestHook is BaseTestHooks, IDynamicFeeManager { uint24 internal fee; IPoolManager manager; diff --git a/src/test/PoolDonateTest.sol b/src/test/PoolDonateTest.sol index d3f66a43a..823a5c30c 100644 --- a/src/test/PoolDonateTest.sol +++ b/src/test/PoolDonateTest.sol @@ -2,22 +2,16 @@ pragma solidity ^0.8.20; import {Currency, CurrencyLibrary} from "../types/Currency.sol"; -import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol"; - -import {Currency} from "../types/Currency.sol"; -import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; import {PoolKey} from "../types/PoolKey.sol"; import {BalanceDelta} from "../types/BalanceDelta.sol"; +import {PoolTestBase} from "./PoolTestBase.sol"; +import {Test} from "forge-std/Test.sol"; -contract PoolDonateTest is ILockCallback { +contract PoolDonateTest is PoolTestBase, Test { using CurrencyLibrary for Currency; - IPoolManager public immutable manager; - - constructor(IPoolManager _manager) { - manager = _manager; - } + constructor(IPoolManager _manager) PoolTestBase(_manager) {} struct CallbackData { address sender; @@ -47,28 +41,24 @@ contract PoolDonateTest is ILockCallback { CallbackData memory data = abi.decode(rawData, (CallbackData)); + (,, uint256 reserveBefore0, int256 deltaBefore0) = _fetchBalances(data.key.currency0, data.sender); + (,, uint256 reserveBefore1, int256 deltaBefore1) = _fetchBalances(data.key.currency1, data.sender); + + assertEq(deltaBefore0, 0); + assertEq(deltaBefore1, 0); + BalanceDelta delta = manager.donate(data.key, data.amount0, data.amount1, data.hookData); - if (delta.amount0() > 0) { - if (data.key.currency0.isNative()) { - manager.settle{value: uint128(delta.amount0())}(data.key.currency0); - } else { - IERC20Minimal(Currency.unwrap(data.key.currency0)).transferFrom( - data.sender, address(manager), uint128(delta.amount0()) - ); - manager.settle(data.key.currency0); - } - } - if (delta.amount1() > 0) { - if (data.key.currency1.isNative()) { - manager.settle{value: uint128(delta.amount1())}(data.key.currency1); - } else { - IERC20Minimal(Currency.unwrap(data.key.currency1)).transferFrom( - data.sender, address(manager), uint128(delta.amount1()) - ); - manager.settle(data.key.currency1); - } - } + (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender); + (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender); + + assertEq(reserveBefore0, reserveAfter0); + assertEq(reserveBefore1, reserveAfter1); + assertEq(deltaAfter0, int256(data.amount0)); + assertEq(deltaAfter1, int256(data.amount1)); + + if (data.amount0 > 0) _settle(data.key.currency0, data.sender, delta.amount0(), true); + if (data.amount1 > 0) _settle(data.key.currency1, data.sender, delta.amount1(), true); return abi.encode(delta); } diff --git a/src/test/PoolModifyPositionTest.sol b/src/test/PoolModifyPositionTest.sol index 0fff91ca2..06781e6d9 100644 --- a/src/test/PoolModifyPositionTest.sol +++ b/src/test/PoolModifyPositionTest.sol @@ -2,21 +2,15 @@ pragma solidity ^0.8.20; import {CurrencyLibrary, Currency} from "../types/Currency.sol"; -import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol"; - -import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; import {BalanceDelta} from "../types/BalanceDelta.sol"; import {PoolKey} from "../types/PoolKey.sol"; +import {PoolTestBase} from "./PoolTestBase.sol"; -contract PoolModifyPositionTest is ILockCallback { +contract PoolModifyPositionTest is PoolTestBase { using CurrencyLibrary for Currency; - IPoolManager public immutable manager; - - constructor(IPoolManager _manager) { - manager = _manager; - } + constructor(IPoolManager _manager) PoolTestBase(_manager) {} struct CallbackData { address sender; @@ -45,32 +39,19 @@ contract PoolModifyPositionTest is ILockCallback { BalanceDelta delta = manager.modifyPosition(data.key, data.params, data.hookData); - if (delta.amount0() > 0) { - if (data.key.currency0.isNative()) { - manager.settle{value: uint128(delta.amount0())}(data.key.currency0); - } else { - IERC20Minimal(Currency.unwrap(data.key.currency0)).transferFrom( - data.sender, address(manager), uint128(delta.amount0()) - ); - manager.settle(data.key.currency0); - } - } - if (delta.amount1() > 0) { - if (data.key.currency1.isNative()) { - manager.settle{value: uint128(delta.amount1())}(data.key.currency1); - } else { - IERC20Minimal(Currency.unwrap(data.key.currency1)).transferFrom( - data.sender, address(manager), uint128(delta.amount1()) - ); - manager.settle(data.key.currency1); - } - } - - if (delta.amount0() < 0) { - manager.take(data.key.currency0, data.sender, uint128(-delta.amount0())); - } - if (delta.amount1() < 0) { - manager.take(data.key.currency1, data.sender, uint128(-delta.amount1())); + (,,, int256 delta0) = _fetchBalances(data.key.currency0, data.sender); + (,,, int256 delta1) = _fetchBalances(data.key.currency1, data.sender); + + if (data.params.liquidityDelta > 0) { + assert(delta0 > 0 || delta1 > 0); + assert(!(delta0 < 0 || delta1 < 0)); + if (delta0 > 0) _settle(data.key.currency0, data.sender, delta.amount0(), true); + if (delta1 > 0) _settle(data.key.currency1, data.sender, delta.amount1(), true); + } else { + assert(delta0 < 0 || delta1 < 0); + assert(!(delta0 > 0 || delta1 > 0)); + if (delta0 < 0) _take(data.key.currency0, data.sender, delta.amount0(), true); + if (delta1 < 0) _take(data.key.currency1, data.sender, delta.amount1(), true); } return abi.encode(delta); diff --git a/src/test/PoolSwapTest.sol b/src/test/PoolSwapTest.sol index 663924878..5a7ece328 100644 --- a/src/test/PoolSwapTest.sol +++ b/src/test/PoolSwapTest.sol @@ -2,22 +2,18 @@ pragma solidity ^0.8.20; import {CurrencyLibrary, Currency} from "../types/Currency.sol"; -import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol"; -import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; import {BalanceDelta} from "../types/BalanceDelta.sol"; import {PoolKey} from "../types/PoolKey.sol"; +import {PoolTestBase} from "./PoolTestBase.sol"; +import {Test} from "forge-std/Test.sol"; -contract PoolSwapTest is ILockCallback { +contract PoolSwapTest is Test, PoolTestBase { using CurrencyLibrary for Currency; - error NoSwapOccurred(); - - IPoolManager public immutable manager; + constructor(IPoolManager _manager) PoolTestBase(_manager) {} - constructor(IPoolManager _manager) { - manager = _manager; - } + error NoSwapOccurred(); struct CallbackData { address sender; @@ -51,55 +47,47 @@ contract PoolSwapTest is ILockCallback { CallbackData memory data = abi.decode(rawData, (CallbackData)); + (,, uint256 reserveBefore0, int256 deltaBefore0) = _fetchBalances(data.key.currency0, data.sender); + (,, uint256 reserveBefore1, int256 deltaBefore1) = _fetchBalances(data.key.currency1, data.sender); + + assertEq(deltaBefore0, 0); + assertEq(deltaBefore1, 0); + BalanceDelta delta = manager.swap(data.key, data.params, data.hookData); + (,, uint256 reserveAfter0, int256 deltaAfter0) = _fetchBalances(data.key.currency0, data.sender); + (,, uint256 reserveAfter1, int256 deltaAfter1) = _fetchBalances(data.key.currency1, data.sender); + // Make sure youve added liquidity to the test pool! if (BalanceDelta.unwrap(delta) == 0) revert NoSwapOccurred(); + assertEq(reserveBefore0, reserveAfter0); + assertEq(reserveBefore1, reserveAfter1); + if (data.params.zeroForOne) { - if (delta.amount0() > 0) { - if (data.testSettings.settleUsingTransfer) { - if (data.key.currency0.isNative()) { - manager.settle{value: uint128(delta.amount0())}(data.key.currency0); - } else { - IERC20Minimal(Currency.unwrap(data.key.currency0)).transferFrom( - data.sender, address(manager), uint128(delta.amount0()) - ); - manager.settle(data.key.currency0); - } - } else { - manager.burn(data.key.currency0, uint128(delta.amount0())); - } - } - if (delta.amount1() < 0) { - if (data.testSettings.withdrawTokens) { - manager.take(data.key.currency1, data.sender, uint128(-delta.amount1())); - } else { - manager.mint(data.key.currency1, address(this), uint128(-delta.amount1())); - } + if (data.params.amountSpecified > 0) { + // exact input, 0 for 1 + assertEq(deltaAfter0, data.params.amountSpecified); + assert(deltaAfter1 < 0); + } else { + // exact output, 0 for 1 + assert(deltaAfter0 > 0); + assertEq(deltaAfter1, data.params.amountSpecified); } + _settle(data.key.currency0, data.sender, delta.amount0(), data.testSettings.settleUsingTransfer); + _take(data.key.currency1, data.sender, delta.amount1(), data.testSettings.withdrawTokens); } else { - if (delta.amount1() > 0) { - if (data.testSettings.settleUsingTransfer) { - if (data.key.currency1.isNative()) { - manager.settle{value: uint128(delta.amount1())}(data.key.currency1); - } else { - IERC20Minimal(Currency.unwrap(data.key.currency1)).transferFrom( - data.sender, address(manager), uint128(delta.amount1()) - ); - manager.settle(data.key.currency1); - } - } else { - manager.burn(data.key.currency1, uint128(delta.amount1())); - } - } - if (delta.amount0() < 0) { - if (data.testSettings.withdrawTokens) { - manager.take(data.key.currency0, data.sender, uint128(-delta.amount0())); - } else { - manager.mint(data.key.currency0, address(this), uint128(-delta.amount0())); - } + if (data.params.amountSpecified > 0) { + // exact input, 1 for 0 + assertEq(deltaAfter1, data.params.amountSpecified); + assert(deltaAfter0 < 0); + } else { + // exact output, 1 for 0 + assert(deltaAfter1 > 0); + assertEq(deltaAfter0, data.params.amountSpecified); } + _settle(data.key.currency1, data.sender, delta.amount1(), data.testSettings.settleUsingTransfer); + _take(data.key.currency0, data.sender, delta.amount0(), data.testSettings.withdrawTokens); } return abi.encode(delta); diff --git a/src/test/PoolTakeTest.sol b/src/test/PoolTakeTest.sol index 4d4ae3f11..8f92f1fa5 100644 --- a/src/test/PoolTakeTest.sol +++ b/src/test/PoolTakeTest.sol @@ -2,20 +2,17 @@ pragma solidity ^0.8.20; import {Currency, CurrencyLibrary} from "../types/Currency.sol"; -import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol"; - -import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; import {IPoolManager} from "../interfaces/IPoolManager.sol"; import {PoolKey} from "../types/PoolKey.sol"; +import {PoolTestBase} from "./PoolTestBase.sol"; +import {SafeCast} from "../libraries/SafeCast.sol"; +import {Test} from "forge-std/Test.sol"; -contract PoolTakeTest is ILockCallback { +contract PoolTakeTest is Test, PoolTestBase { using CurrencyLibrary for Currency; + using SafeCast for uint256; - IPoolManager public immutable manager; - - constructor(IPoolManager _manager) { - manager = _manager; - } + constructor(IPoolManager _manager) PoolTestBase(_manager) {} struct CallbackData { address sender; @@ -33,38 +30,28 @@ contract PoolTakeTest is ILockCallback { CallbackData memory data = abi.decode(rawData, (CallbackData)); - if (data.amount0 > 0) { - uint256 balBefore = data.key.currency0.balanceOf(data.sender); - manager.take(data.key.currency0, data.sender, data.amount0); - uint256 balAfter = data.key.currency0.balanceOf(data.sender); - require(balAfter - balBefore == data.amount0); + if (data.amount0 > 0) _testTake(data.key.currency0, data.sender, data.amount0); + if (data.amount1 > 0) _testTake(data.key.currency1, data.sender, data.amount1); + + return abi.encode(0); + } - if (data.key.currency0.isNative()) { - manager.settle{value: uint256(data.amount0)}(data.key.currency0); - } else { - IERC20Minimal(Currency.unwrap(data.key.currency0)).transferFrom( - data.sender, address(manager), uint256(data.amount0) - ); - manager.settle(data.key.currency0); - } - } + function _testTake(Currency currency, address sender, uint256 amount) internal { + (uint256 userBalBefore, uint256 pmBalBefore, uint256 reserveBefore, int256 deltaBefore) = + _fetchBalances(currency, sender); + assertEq(deltaBefore, 0); - if (data.amount1 > 0) { - uint256 balBefore = data.key.currency1.balanceOf(data.sender); - manager.take(data.key.currency1, data.sender, data.amount1); - uint256 balAfter = data.key.currency1.balanceOf(data.sender); - require(balAfter - balBefore == data.amount1); + _take(currency, sender, -(amount.toInt128()), true); - if (data.key.currency1.isNative()) { - manager.settle{value: uint256(data.amount1)}(data.key.currency1); - } else { - IERC20Minimal(Currency.unwrap(data.key.currency1)).transferFrom( - data.sender, address(manager), uint256(data.amount1) - ); - manager.settle(data.key.currency1); - } - } + (uint256 userBalAfter, uint256 pmBalAfter, uint256 reserveAfter, int256 deltaAfter) = + _fetchBalances(currency, sender); + assertEq(deltaAfter, amount.toInt128()); - return abi.encode(0); + assertEq(userBalAfter - userBalBefore, amount); + assertEq(pmBalBefore - pmBalAfter, amount); + assertEq(reserveBefore - reserveAfter, amount); + assertEq(reserveBefore - reserveAfter, amount); + + _settle(currency, sender, amount.toInt128(), true); } } diff --git a/src/test/PoolTestBase.sol b/src/test/PoolTestBase.sol new file mode 100644 index 000000000..2bf1257dc --- /dev/null +++ b/src/test/PoolTestBase.sol @@ -0,0 +1,51 @@ +pragma solidity ^0.8.20; + +import {CurrencyLibrary, Currency} from "../types/Currency.sol"; +import {IERC20Minimal} from "../interfaces/external/IERC20Minimal.sol"; + +import {ILockCallback} from "../interfaces/callback/ILockCallback.sol"; +import {IPoolManager} from "../interfaces/IPoolManager.sol"; + +abstract contract PoolTestBase is ILockCallback { + using CurrencyLibrary for Currency; + + IPoolManager public immutable manager; + + constructor(IPoolManager _manager) { + manager = _manager; + } + + function _take(Currency currency, address recipient, int128 amount, bool withdrawTokens) internal { + assert(amount < 0); + if (withdrawTokens) { + manager.take(currency, recipient, uint128(-amount)); + } else { + manager.mint(currency, address(this), uint128(-amount)); + } + } + + function _settle(Currency currency, address payer, int128 amount, bool settleUsingTransfer) internal { + assert(amount > 0); + if (settleUsingTransfer) { + if (currency.isNative()) { + manager.settle{value: uint128(amount)}(currency); + } else { + IERC20Minimal(Currency.unwrap(currency)).transferFrom(payer, address(manager), uint128(amount)); + manager.settle(currency); + } + } else { + manager.burn(currency, uint128(amount)); + } + } + + function _fetchBalances(Currency currency, address user) + internal + view + returns (uint256 userBalance, uint256 poolBalance, uint256 reserves, int256 delta) + { + userBalance = currency.balanceOf(user); + poolBalance = currency.balanceOf(address(manager)); + reserves = manager.reservesOf(currency); + delta = manager.currencyDelta(address(this), currency); + } +} diff --git a/src/test/TestInvalidERC20.sol b/src/test/TestInvalidERC20.sol index 88942d97e..b0e549a9f 100644 --- a/src/test/TestInvalidERC20.sol +++ b/src/test/TestInvalidERC20.sol @@ -28,13 +28,13 @@ contract TestInvalidERC20 is IERC20Minimal { balanceOf[recipient] = balanceRecipient + amount; emit Transfer(msg.sender, recipient, amount); - return false; + return false; // returns false even though it succeeded } function approve(address spender, uint256 amount) external override returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); - return true; + return false; } function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) { @@ -51,6 +51,6 @@ contract TestInvalidERC20 is IERC20Minimal { balanceOf[sender] = balanceSender - amount; emit Transfer(sender, recipient, amount); - return false; + return false; // returns false even though it succeeded } } diff --git a/test/Claims.t.sol b/test/Claims.t.sol index 90bb47cb5..436c671ea 100644 --- a/test/Claims.t.sol +++ b/test/Claims.t.sol @@ -2,13 +2,13 @@ pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {TokenFixture} from "./utils/TokenFixture.sol"; import {Claims} from "../src/Claims.sol"; import {IClaims} from "../src/interfaces/IClaims.sol"; import {CurrencyLibrary, Currency} from "../src/types/Currency.sol"; import {MockClaims} from "../src/test/MockClaims.sol"; +import {Deployers} from "./utils/Deployers.sol"; -contract ClaimsTest is TokenFixture, Test { +contract ClaimsTest is Test, Deployers { using CurrencyLibrary for Currency; MockClaims claimsImpl = new MockClaims(); @@ -17,7 +17,9 @@ contract ClaimsTest is TokenFixture, Test { event Burn(address indexed from, Currency indexed currency, uint256 amount); event Transfer(address indexed from, address indexed to, Currency indexed currency, uint256 amount); - function setUp() public {} + function setUp() public { + (currency0, currency1) = deployMintAndApprove2Currencies(); + } function testCanBurn(uint256 amount) public { assertEq(claimsImpl.balanceOf(address(this), currency0), 0); diff --git a/test/DynamicFees.t.sol b/test/DynamicFees.t.sol index 56019a7c5..6b4c36dac 100644 --- a/test/DynamicFees.t.sol +++ b/test/DynamicFees.t.sol @@ -15,15 +15,14 @@ import {PoolSwapTest} from "../src/test/PoolSwapTest.sol"; import {Deployers} from "./utils/Deployers.sol"; import {IDynamicFeeManager} from "././../src/interfaces/IDynamicFeeManager.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; -import {DynamicFeesTest} from "../src/test/DynamicFeesTest.sol"; -import {PoolModifyPositionTest} from "../src/test/PoolModifyPositionTest.sol"; +import {DynamicFeesTestHook} from "../src/test/DynamicFeesTestHook.sol"; import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; contract TestDynamicFees is Test, Deployers, GasSnapshot { using PoolIdLibrary for PoolKey; - DynamicFeesTest dynamicFees = DynamicFeesTest( + DynamicFeesTestHook dynamicFeesHook = DynamicFeesTestHook( address( uint160(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF) & uint160( @@ -34,7 +33,7 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { ) ); - DynamicFeesTest dynamicFeesNoHook = DynamicFeesTest( + DynamicFeesTestHook dynamicFeesNoHook = DynamicFeesTestHook( address( uint160(0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF) & uint160( @@ -45,60 +44,43 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { ) ); - PoolManager manager; - PoolKey key; - PoolKey key2; - PoolSwapTest swapRouter; - PoolModifyPositionTest modifyPositionRouter; - function setUp() public { - DynamicFeesTest impl = new DynamicFeesTest(); - vm.etch(address(dynamicFees), address(impl).code); + DynamicFeesTestHook impl = new DynamicFeesTestHook(); + vm.etch(address(dynamicFeesHook), address(impl).code); vm.etch(address(dynamicFeesNoHook), address(impl).code); - (manager, key,) = - Deployers.createAndInitFreshPool(IHooks(address(dynamicFees)), FeeLibrary.DYNAMIC_FEE_FLAG, SQRT_RATIO_1_1); - dynamicFees.setManager(IPoolManager(manager)); - - (key2,) = Deployers.createAndInitPool( - manager, IHooks(address(dynamicFeesNoHook)), FeeLibrary.DYNAMIC_FEE_FLAG, SQRT_RATIO_1_1 - ); + deployFreshManagerAndRouters(); + dynamicFeesHook.setManager(IPoolManager(manager)); dynamicFeesNoHook.setManager(IPoolManager(manager)); - swapRouter = new PoolSwapTest(manager); - modifyPositionRouter = new PoolModifyPositionTest(manager); - - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 10 ether); - MockERC20(Currency.unwrap(key.currency1)).mint(address(this), 10 ether); - MockERC20(Currency.unwrap(key2.currency0)).mint(address(this), 10 ether); - MockERC20(Currency.unwrap(key2.currency1)).mint(address(this), 10 ether); - - MockERC20(Currency.unwrap(key.currency0)).approve(address(swapRouter), 10 ether); - MockERC20(Currency.unwrap(key.currency1)).approve(address(swapRouter), 10 ether); - MockERC20(Currency.unwrap(key2.currency0)).approve(address(swapRouter), 10 ether); - MockERC20(Currency.unwrap(key2.currency1)).approve(address(swapRouter), 10 ether); - - MockERC20(Currency.unwrap(key.currency0)).approve(address(modifyPositionRouter), 10 ether); - MockERC20(Currency.unwrap(key.currency1)).approve(address(modifyPositionRouter), 10 ether); - MockERC20(Currency.unwrap(key2.currency0)).approve(address(modifyPositionRouter), 10 ether); - MockERC20(Currency.unwrap(key2.currency1)).approve(address(modifyPositionRouter), 10 ether); - - // add liquidity for the 2 new pools - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); - modifyPositionRouter.modifyPosition(key, liqParams, ZERO_BYTES); - modifyPositionRouter.modifyPosition(key2, liqParams, ZERO_BYTES); + (currency0, currency1) = deployMintAndApprove2Currencies(); + (key,) = initPoolAndAddLiquidity( + currency0, + currency1, + IHooks(address(dynamicFeesHook)), + FeeLibrary.DYNAMIC_FEE_FLAG, + SQRT_RATIO_1_1, + ZERO_BYTES + ); } function testPoolInitializeFailsWithTooLargeFee() public { - dynamicFees.setFee(1000000); - PoolKey memory key0 = Deployers.createKey(IHooks(address(dynamicFees)), FeeLibrary.DYNAMIC_FEE_FLAG); + (Currency currency2, Currency currency3) = deployMintAndApprove2Currencies(); + key = PoolKey({ + currency0: currency2, + currency1: currency3, + fee: FeeLibrary.DYNAMIC_FEE_FLAG, + hooks: dynamicFeesHook, + tickSpacing: 60 + }); + dynamicFeesHook.setFee(1000000); + vm.expectRevert(IFees.FeeTooLarge.selector); - manager.initialize(key0, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); } function testUpdateFailsWithTooLargeFee() public { - dynamicFees.setFee(1000000); + dynamicFeesHook.setFee(1000000); vm.expectRevert(IFees.FeeTooLarge.selector); manager.updateDynamicSwapFee(key); } @@ -115,7 +97,7 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { ); function testSwapWorks() public { - dynamicFees.setFee(123); + dynamicFeesHook.setFee(123); manager.updateDynamicSwapFee(key); IPoolManager.SwapParams memory params = @@ -132,7 +114,7 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { } function testCacheDynamicFeeAndSwap() public { - dynamicFees.setFee(123); + dynamicFeesHook.setFee(123); manager.updateDynamicSwapFee(key); IPoolManager.SwapParams memory params = @@ -150,7 +132,7 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { } function testDynamicFeeAndBeforeSwapHook() public { - dynamicFees.setFee(123); + dynamicFeesHook.setFee(123); manager.updateDynamicSwapFee(key); IPoolManager.SwapParams memory params = @@ -168,14 +150,18 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { } function testUpdateRevertsOnStaticFeePool() public { - (PoolKey memory staticPoolKey,) = Deployers.createAndInitPool(manager, IHooks(address(0)), 3000, SQRT_RATIO_1_1); + (key,) = initPool(currency0, currency1, IHooks(address(0)), 3000, SQRT_RATIO_1_1, ZERO_BYTES); vm.expectRevert(IFees.FeeNotDynamic.selector); - manager.updateDynamicSwapFee(staticPoolKey); + manager.updateDynamicSwapFee(key); } function testDynamicFeesCacheNoOtherHooks() public { + (key,) = initPoolAndAddLiquidity( + currency0, currency1, dynamicFeesNoHook, FeeLibrary.DYNAMIC_FEE_FLAG, SQRT_RATIO_1_1, ZERO_BYTES + ); + dynamicFeesNoHook.setFee(123); - manager.updateDynamicSwapFee(key2); + manager.updateDynamicSwapFee(key); IPoolManager.SwapParams memory params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); @@ -183,7 +169,7 @@ contract TestDynamicFees is Test, Deployers, GasSnapshot { PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); vm.expectEmit(true, true, true, true, address(manager)); - emit Swap(key.toId(), address(swapRouter), 100, -99, 79228162514264329670727698910, 1e18, -1, 0); + emit Swap(key.toId(), address(swapRouter), 100, -98, 79228162514264329749955861424, 1e18, -1, 123); snapStart("cached dynamic fee, no hooks"); swapRouter.swap(key, params, testSettings, ZERO_BYTES); diff --git a/test/Fees.t.sol b/test/Fees.t.sol index 67235214b..b03b32059 100644 --- a/test/Fees.t.sol +++ b/test/Fees.t.sol @@ -13,7 +13,6 @@ import {TickMath} from "../src/libraries/TickMath.sol"; import {Pool} from "../src/libraries/Pool.sol"; import {PoolIdLibrary} from "../src/types/PoolId.sol"; import {Deployers} from "./utils/Deployers.sol"; -import {TokenFixture} from "./utils/TokenFixture.sol"; import {PoolModifyPositionTest} from "../src/test/PoolModifyPositionTest.sol"; import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; @@ -26,19 +25,12 @@ import {Fees} from "../src/Fees.sol"; import {BalanceDelta} from "../src/types/BalanceDelta.sol"; import {PoolKey} from "../src/types/PoolKey.sol"; -contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { +contract FeesTest is Test, Deployers, GasSnapshot { using Hooks for IHooks; using Pool for Pool.State; using PoolIdLibrary for PoolKey; using CurrencyLibrary for Currency; - Pool.State state; - PoolManager manager; - - PoolModifyPositionTest modifyPositionRouter; - PoolSwapTest swapRouter; - ProtocolFeeControllerTest protocolFeeController; - MockHooks hook; // key0 hook enabled fee on swap @@ -54,18 +46,8 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { bool _oneForZero = false; function setUp() public { - initializeTokens(); - manager = Deployers.createFreshManager(); - - modifyPositionRouter = new PoolModifyPositionTest(manager); - swapRouter = new PoolSwapTest(manager); - protocolFeeController = new ProtocolFeeControllerTest(); - - MockERC20(Currency.unwrap(currency0)).mint(address(this), 10 ether); - MockERC20(Currency.unwrap(currency1)).mint(address(this), 10 ether); - - MockERC20(Currency.unwrap(currency0)).approve(address(modifyPositionRouter), 10 ether); - MockERC20(Currency.unwrap(currency1)).approve(address(modifyPositionRouter), 10 ether); + deployFreshManagerAndRouters(); + (currency0, currency1) = deployMintAndApprove2Currencies(); address hookAddr = address(99); // can't be a zero address, but does not have to have any other hook flags specified MockHooks impl = new MockHooks(); @@ -189,8 +171,7 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { assertEq(getSwapFee(slot0.hookFees), 0); assertEq(getSwapFee(slot0.protocolFees), 0); - protocolFeeController.setSwapFeeForPool(key0.toId(), protocolSwapFee); - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setSwapFeeForPool(key0.toId(), protocolSwapFee); uint16 protocolSwapFee1 = protocolSwapFee >> 6; uint16 protocolSwapFee0 = protocolSwapFee % 64; @@ -229,9 +210,8 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { assertEq(getSwapFee(slot0.protocolFees), 0); assertEq(getWithdrawFee(slot0.protocolFees), 0); - protocolFeeController.setSwapFeeForPool(key2.toId(), protocolSwapFee); - protocolFeeController.setWithdrawFeeForPool(key2.toId(), protocolWithdrawFee); - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setSwapFeeForPool(key2.toId(), protocolSwapFee); + feeController.setWithdrawFeeForPool(key2.toId(), protocolWithdrawFee); uint16 protocolSwapFee1 = protocolSwapFee >> 6; uint16 protocolSwapFee0 = protocolSwapFee % 64; @@ -274,8 +254,7 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { manager.setHookFees(key0); // set fee on the fee controller - protocolFeeController.setWithdrawFeeForPool(key0.toId(), protocolWithdrawFee); - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setWithdrawFeeForPool(key0.toId(), protocolWithdrawFee); manager.setProtocolFees(key0); (Pool.Slot0 memory slot0,,,) = manager.pools(key0.toId()); @@ -308,13 +287,13 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { // vm.expectRevert(abi.encodeWithSelector(IFees.FeeDenominatorOutOfBounds.selector, newFee)); // manager.setHookFees(key0); - // manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + // manager.setProtocolFeeController(IProtocolFeeController(feeController)); - // protocolFeeController.setSwapFeeForPool(key0.toId(), newFee); + // feeController.setSwapFeeForPool(key0.toId(), newFee); // vm.expectRevert(abi.encodeWithSelector(IFees.FeeDenominatorOutOfBounds.selector, newFee)); // manager.setProtocolFees(key0); - // protocolFeeController.setWithdrawFeeForPool(key0.toId(), newFee); + // feeController.setWithdrawFeeForPool(key0.toId(), newFee); // vm.expectRevert(abi.encodeWithSelector(IFees.FeeDenominatorOutOfBounds.selector, newFee)); // manager.setProtocolFees(key0); // } @@ -328,8 +307,7 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { hook.setWithdrawFee(key1, hookWithdrawFee); manager.setHookFees(key1); - protocolFeeController.setWithdrawFeeForPool(key1.toId(), protocolWithdrawFee); - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setWithdrawFeeForPool(key1.toId(), protocolWithdrawFee); manager.setProtocolFees(key1); (Pool.Slot0 memory slot0,,,) = manager.pools(key1.toId()); @@ -385,9 +363,8 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { vm.assume(protocolWithdrawFee >> 6 >= 4); vm.assume(protocolWithdrawFee % 64 >= 4); - protocolFeeController.setSwapFeeForPool(key3.toId(), protocolSwapFee); - protocolFeeController.setWithdrawFeeForPool(key3.toId(), protocolWithdrawFee); - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setSwapFeeForPool(key3.toId(), protocolSwapFee); + feeController.setWithdrawFeeForPool(key3.toId(), protocolWithdrawFee); manager.setProtocolFees(key3); (Pool.Slot0 memory slot0,,,) = manager.pools(key3.toId()); @@ -436,8 +413,7 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { function testProtocolSwapFeeAndHookSwapFeeSameDirection() public { uint16 protocolFee = _computeFee(_oneForZero, 10); // 10% on 1 to 0 swaps - protocolFeeController.setSwapFeeForPool(key0.toId(), protocolFee); - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setSwapFeeForPool(key0.toId(), protocolFee); manager.setProtocolFees(key0); (Pool.Slot0 memory slot0,,,) = manager.pools(key0.toId()); @@ -468,8 +444,7 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { function testInitializeWithSwapProtocolFeeAndHookFeeDifferentDirections() public { uint16 protocolFee = _computeFee(_oneForZero, 10); // 10% fee on 1 to 0 swaps - protocolFeeController.setSwapFeeForPool(key0.toId(), protocolFee); - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setSwapFeeForPool(key0.toId(), protocolFee); manager.setProtocolFees(key0); (Pool.Slot0 memory slot0,,,) = manager.pools(key0.toId()); @@ -502,8 +477,7 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { function testSwapWithProtocolFeeAllAndHookFeeAllButOnlySwapFlag() public { // Protocol should not be able to withdraw since the hook withdraw fee is not set uint16 protocolFee = _computeFee(_oneForZero, 4) | _computeFee(_zeroForOne, 4); // max fees on both amounts - protocolFeeController.setWithdrawFeeForPool(key0.toId(), protocolFee); // - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setWithdrawFeeForPool(key0.toId(), protocolFee); // manager.setProtocolFees(key0); (Pool.Slot0 memory slot0,,,) = manager.pools(key0.toId()); @@ -544,8 +518,7 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { function testCollectFees() public { uint16 protocolFee = _computeFee(_oneForZero, 10); // 10% on 1 to 0 swaps - protocolFeeController.setSwapFeeForPool(key0.toId(), protocolFee); - manager.setProtocolFeeController(IProtocolFeeController(protocolFeeController)); + feeController.setSwapFeeForPool(key0.toId(), protocolFee); manager.setProtocolFees(key0); (Pool.Slot0 memory slot0,,,) = manager.pools(key0.toId()); @@ -570,9 +543,9 @@ contract FeesTest is Test, Deployers, TokenFixture, GasSnapshot { ); uint256 expectedProtocolFees = 3; // 10% of 30 is 3 - vm.prank(address(protocolFeeController)); - manager.collectProtocolFees(address(protocolFeeController), currency1, 0); - assertEq(currency1.balanceOf(address(protocolFeeController)), expectedProtocolFees); + vm.prank(address(feeController)); + manager.collectProtocolFees(address(feeController), currency1, 0); + assertEq(currency1.balanceOf(address(feeController)), expectedProtocolFees); uint256 expectedHookFees = 5; // 20% of 27 (30-3) is 5.4, round down is 5 vm.prank(address(hook)); diff --git a/test/Hooks.t.sol b/test/Hooks.t.sol index a2786893a..cd2e9e557 100644 --- a/test/Hooks.t.sol +++ b/test/Hooks.t.sol @@ -17,32 +17,27 @@ import {PoolSwapTest} from "../src/test/PoolSwapTest.sol"; import {PoolDonateTest} from "../src/test/PoolDonateTest.sol"; import {Deployers} from "./utils/Deployers.sol"; import {Fees} from "../src/Fees.sol"; -import {PoolId} from "../src/types/PoolId.sol"; +import {PoolId, PoolIdLibrary} from "../src/types/PoolId.sol"; import {PoolKey} from "../src/types/PoolKey.sol"; contract HooksTest is Test, Deployers, GasSnapshot { + using PoolIdLibrary for PoolKey; + address payable ALL_HOOKS_ADDRESS = payable(0xfF00000000000000000000000000000000000000); MockHooks mockHooks; - PoolManager manager; - PoolKey key; - PoolModifyPositionTest modifyPositionRouter; - PoolSwapTest swapRouter; - PoolDonateTest donateRouter; function setUp() public { MockHooks impl = new MockHooks(); vm.etch(ALL_HOOKS_ADDRESS, address(impl).code); mockHooks = MockHooks(ALL_HOOKS_ADDRESS); - (manager, key,) = Deployers.createAndInitFreshPool(mockHooks, 3000, SQRT_RATIO_1_1); - modifyPositionRouter = new PoolModifyPositionTest(IPoolManager(address(manager))); - swapRouter = new PoolSwapTest(IPoolManager(address(manager))); - donateRouter = new PoolDonateTest(IPoolManager(address(manager))); + + initializeManagerRoutersAndPoolsWithLiq(mockHooks); } function testInitializeSucceedsWithHook() public { - (PoolManager _manager,, PoolId id) = - Deployers.createAndInitFreshPool(mockHooks, 3000, SQRT_RATIO_1_1, new bytes(123)); - (uint160 sqrtPriceX96,,,) = _manager.getSlot0(id); + manager.initialize(uninitializedKey, SQRT_RATIO_1_1, new bytes(123)); + + (uint160 sqrtPriceX96,,,) = manager.getSlot0(uninitializedKey.toId()); assertEq(sqrtPriceX96, SQRT_RATIO_1_1); assertEq(mockHooks.beforeInitializeData(), new bytes(123)); assertEq(mockHooks.afterInitializeData(), new bytes(123)); @@ -50,62 +45,41 @@ contract HooksTest is Test, Deployers, GasSnapshot { function testBeforeInitializeInvalidReturn() public { mockHooks.setReturnValue(mockHooks.beforeInitialize.selector, bytes4(0xdeadbeef)); - (Currency currency0, Currency currency1) = Deployers.deployCurrencies(2 ** 255); - PoolKey memory _key = PoolKey(currency0, currency1, 3000, int24(3000 / 100 * 2), mockHooks); vm.expectRevert(Hooks.InvalidHookResponse.selector); - manager.initialize(_key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(uninitializedKey, SQRT_RATIO_1_1, ZERO_BYTES); } function testAfterInitializeInvalidReturn() public { mockHooks.setReturnValue(mockHooks.afterInitialize.selector, bytes4(0xdeadbeef)); - (Currency currency0, Currency currency1) = Deployers.deployCurrencies(2 ** 255); - PoolKey memory _key = PoolKey(currency0, currency1, 3000, int24(3000 / 100 * 2), mockHooks); vm.expectRevert(Hooks.InvalidHookResponse.selector); - manager.initialize(_key, SQRT_RATIO_1_1, ZERO_BYTES); + manager.initialize(uninitializedKey, SQRT_RATIO_1_1, ZERO_BYTES); } function testModifyPositionSucceedsWithHook() public { - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency0)).approve(address(modifyPositionRouter), 10 ** 18); - modifyPositionRouter.modifyPosition(key, IPoolManager.ModifyPositionParams(0, 60, 100), new bytes(111)); + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, new bytes(111)); assertEq(mockHooks.beforeModifyPositionData(), new bytes(111)); assertEq(mockHooks.afterModifyPositionData(), new bytes(111)); } function testBeforeModifyPositionInvalidReturn() public { mockHooks.setReturnValue(mockHooks.beforeModifyPosition.selector, bytes4(0xdeadbeef)); - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency0)).approve(address(modifyPositionRouter), 10 ** 18); vm.expectRevert(Hooks.InvalidHookResponse.selector); - modifyPositionRouter.modifyPosition(key, IPoolManager.ModifyPositionParams(0, 60, 100), ZERO_BYTES); + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); } function testAfterModifyPositionInvalidReturn() public { mockHooks.setReturnValue(mockHooks.afterModifyPosition.selector, bytes4(0xdeadbeef)); - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency0)).approve(address(modifyPositionRouter), 10 ** 18); vm.expectRevert(Hooks.InvalidHookResponse.selector); - modifyPositionRouter.modifyPosition(key, IPoolManager.ModifyPositionParams(0, 60, 100), ZERO_BYTES); + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); } function testSwapSucceedsWithHook() public { - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency1)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency0)).approve(address(swapRouter), 10 ** 18); - MockERC20(Currency.unwrap(key.currency1)).approve(address(swapRouter), 10 ** 18); - MockERC20(Currency.unwrap(key.currency0)).approve(address(modifyPositionRouter), 10 ** 18); - MockERC20(Currency.unwrap(key.currency1)).approve(address(modifyPositionRouter), 10 ** 18); - - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); - IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); - modifyPositionRouter.modifyPosition(key, liqParams, new bytes(111)); swapRouter.swap(key, swapParams, testSettings, new bytes(222)); assertEq(mockHooks.beforeSwapData(), new bytes(222)); assertEq(mockHooks.afterSwapData(), new bytes(222)); @@ -113,8 +87,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { function testBeforeSwapInvalidReturn() public { mockHooks.setReturnValue(mockHooks.beforeSwap.selector, bytes4(0xdeadbeef)); - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency0)).approve(address(swapRouter), 10 ** 18); vm.expectRevert(Hooks.InvalidHookResponse.selector); swapRouter.swap( key, @@ -126,8 +98,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { function testAfterSwapInvalidReturn() public { mockHooks.setReturnValue(mockHooks.afterSwap.selector, bytes4(0xdeadbeef)); - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency0)).approve(address(swapRouter), 10 ** 18); vm.expectRevert(Hooks.InvalidHookResponse.selector); swapRouter.swap( key, @@ -138,10 +108,6 @@ contract HooksTest is Test, Deployers, GasSnapshot { } function testDonateSucceedsWithHook() public { - addLiquidity(0, 60, 100); - - MockERC20(Currency.unwrap(key.currency0)).approve(address(donateRouter), 100); - MockERC20(Currency.unwrap(key.currency1)).approve(address(donateRouter), 200); donateRouter.donate(key, 100, 200, new bytes(333)); assertEq(mockHooks.beforeDonateData(), new bytes(333)); assertEq(mockHooks.afterDonateData(), new bytes(333)); @@ -149,20 +115,12 @@ contract HooksTest is Test, Deployers, GasSnapshot { function testBeforeDonateInvalidReturn() public { mockHooks.setReturnValue(mockHooks.beforeDonate.selector, bytes4(0xdeadbeef)); - addLiquidity(0, 60, 100); - - MockERC20(Currency.unwrap(key.currency0)).approve(address(donateRouter), 100); - MockERC20(Currency.unwrap(key.currency1)).approve(address(donateRouter), 200); vm.expectRevert(Hooks.InvalidHookResponse.selector); donateRouter.donate(key, 100, 200, ZERO_BYTES); } function testAfterDonateInvalidReturn() public { mockHooks.setReturnValue(mockHooks.beforeDonate.selector, bytes4(0xdeadbeef)); - addLiquidity(0, 60, 100); - - MockERC20(Currency.unwrap(key.currency0)).approve(address(donateRouter), 100); - MockERC20(Currency.unwrap(key.currency1)).approve(address(donateRouter), 200); vm.expectRevert(Hooks.InvalidHookResponse.selector); donateRouter.donate(key, 100, 200, ZERO_BYTES); } @@ -641,14 +599,4 @@ contract HooksTest is Test, Deployers, GasSnapshot { assertFalse(Hooks.isValidHookAddress(IHooks(0x0040000000000000000000000000000000000001), 3000)); assertFalse(Hooks.isValidHookAddress(IHooks(0x007840A85d5aF5BF1D1762f925bdADDC4201F984), 3000)); } - - function addLiquidity(int24 tickLower, int24 tickUpper, int256 amount) internal { - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency1)).mint(address(this), 10 ** 18); - MockERC20(Currency.unwrap(key.currency0)).approve(address(modifyPositionRouter), 10 ** 18); - MockERC20(Currency.unwrap(key.currency1)).approve(address(modifyPositionRouter), 10 ** 18); - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams(tickLower, tickUpper, amount), ZERO_BYTES - ); - } } diff --git a/test/LockersLibrary.t.sol b/test/LockersLibrary.t.sol index b8ca21715..94c5afac9 100644 --- a/test/LockersLibrary.t.sol +++ b/test/LockersLibrary.t.sol @@ -8,7 +8,6 @@ import {Deployers} from "./utils/Deployers.sol"; import {ILockCallback} from "../src/interfaces/callback/ILockCallback.sol"; import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {PoolModifyPositionTest} from "../src/test/PoolModifyPositionTest.sol"; import {PoolKey} from "../src/types/PoolKey.sol"; import {IHooks} from "../src/interfaces/IHooks.sol"; import {Lockers} from "../src/libraries/Lockers.sol"; @@ -16,23 +15,10 @@ import {Lockers} from "../src/libraries/Lockers.sol"; contract LockersLibrary is Test, Deployers, ILockCallback { using CurrencyLibrary for Currency; - PoolManager manager; - PoolKey key; - Currency currency0; - uint256 constant LOCKERS_OFFSET = uint256(1); function setUp() public { - manager = Deployers.createFreshManager(); - (key,) = createAndInitPool(manager, IHooks(address(0)), 3000, SQRT_RATIO_1_1); - MockERC20(Currency.unwrap(key.currency0)).mint(address(this), 1e18); - MockERC20(Currency.unwrap(key.currency1)).mint(address(this), 1e18); - - PoolModifyPositionTest modifyRouter = new PoolModifyPositionTest(manager); - - MockERC20(Currency.unwrap(key.currency0)).approve(address(modifyRouter), 1e18); - MockERC20(Currency.unwrap(key.currency1)).approve(address(modifyRouter), 1e18); - modifyRouter.modifyPosition(key, IPoolManager.ModifyPositionParams(-60, 60, 1000), ""); + initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); } function testLockerLengthAndNonzeroDeltaCount() public { diff --git a/test/PoolManager.t.sol b/test/PoolManager.t.sol index 58256ccb4..a574c1b2f 100644 --- a/test/PoolManager.t.sol +++ b/test/PoolManager.t.sol @@ -9,16 +9,10 @@ import {IPoolManager} from "../src/interfaces/IPoolManager.sol"; import {IFees} from "../src/interfaces/IFees.sol"; import {IProtocolFeeController} from "../src/interfaces/IProtocolFeeController.sol"; import {PoolManager} from "../src/PoolManager.sol"; -import {PoolDonateTest} from "../src/test/PoolDonateTest.sol"; -import {ProtocolFeeControllerTest} from "../src/test/ProtocolFeeControllerTest.sol"; -import {PoolTakeTest} from "../src/test/PoolTakeTest.sol"; import {TickMath} from "../src/libraries/TickMath.sol"; import {Pool} from "../src/libraries/Pool.sol"; import {Deployers} from "./utils/Deployers.sol"; -import {TokenFixture} from "./utils/TokenFixture.sol"; -import {PoolModifyPositionTest} from "../src/test/PoolModifyPositionTest.sol"; import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; -import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {MockHooks} from "../src/test/MockHooks.sol"; import {MockContract} from "../src/test/MockContract.sol"; import {EmptyTestHooks} from "../src/test/EmptyTestHooks.sol"; @@ -29,27 +23,17 @@ import {TestInvalidERC20} from "../src/test/TestInvalidERC20.sol"; import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; import {PoolLockTest} from "../src/test/PoolLockTest.sol"; import {PoolId, PoolIdLibrary} from "../src/types/PoolId.sol"; -import {ProtocolFeeControllerTest} from "../src/test/ProtocolFeeControllerTest.sol"; import {FeeLibrary} from "../src/libraries/FeeLibrary.sol"; import {Position} from "../src/libraries/Position.sol"; -contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { +contract PoolManagerTest is Test, Deployers, GasSnapshot { using Hooks for IHooks; - using Pool for Pool.State; using PoolIdLibrary for PoolKey; using FeeLibrary for uint24; using CurrencyLibrary for Currency; event LockAcquired(); - event ProtocolFeeControllerUpdated(address protocolFeeController); - event Initialize( - PoolId indexed poolId, - Currency indexed currency0, - Currency indexed currency1, - uint24 fee, - int24 tickSpacing, - IHooks hooks - ); + event ProtocolFeeControllerUpdated(address feeController); event ModifyPosition( PoolId indexed poolId, address indexed sender, int24 tickLower, int24 tickUpper, int256 liquidityDelta ); @@ -67,387 +51,66 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { event Burn(address indexed from, Currency indexed currency, uint256 amount); event ProtocolFeeUpdated(PoolId indexed id, uint24 protocolFees); - Pool.State state; - PoolManager manager; - PoolDonateTest donateRouter; - PoolTakeTest takeRouter; - ProtocolFeeControllerTest feeController; - PoolModifyPositionTest modifyPositionRouter; - PoolSwapTest swapRouter; PoolLockTest lockTest; - ProtocolFeeControllerTest protocolFeeController; address ADDRESS_ZERO = address(0); address EMPTY_HOOKS = address(0xf000000000000000000000000000000000000000); - address ALL_HOOKS = address(0xff00000000000000000000000000000000000001); address MOCK_HOOKS = address(0xfF00000000000000000000000000000000000000); function setUp() public { - initializeTokens(); - manager = Deployers.createFreshManager(); - donateRouter = new PoolDonateTest(manager); - takeRouter = new PoolTakeTest(manager); - modifyPositionRouter = new PoolModifyPositionTest(manager); - feeController = new ProtocolFeeControllerTest(); + initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); lockTest = new PoolLockTest(manager); - swapRouter = new PoolSwapTest(manager); - protocolFeeController = new ProtocolFeeControllerTest(); - - MockERC20(Currency.unwrap(currency0)).mint(address(this), 10 ether); - MockERC20(Currency.unwrap(currency1)).mint(address(this), 10 ether); - - MockERC20(Currency.unwrap(currency0)).approve(address(swapRouter), 10 ether); - MockERC20(Currency.unwrap(currency1)).approve(address(swapRouter), 10 ether); - - MockERC20(Currency.unwrap(currency0)).approve(address(modifyPositionRouter), 10 ether); - MockERC20(Currency.unwrap(currency1)).approve(address(modifyPositionRouter), 10 ether); - - MockERC20(Currency.unwrap(currency0)).approve(address(donateRouter), 10 ether); - MockERC20(Currency.unwrap(currency1)).approve(address(donateRouter), 10 ether); - - MockERC20(Currency.unwrap(currency0)).approve(address(takeRouter), 10 ether); - MockERC20(Currency.unwrap(currency1)).approve(address(takeRouter), 10 ether); } function test_bytecodeSize() public { snapSize("poolManager bytecode size", address(manager)); } - function test_initialize(PoolKey memory key, uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - // tested in Hooks.t.sol - key.hooks = IHooks(address(0)); - - if (key.fee & FeeLibrary.STATIC_FEE_MASK >= 1000000) { - vm.expectRevert(abi.encodeWithSelector(IFees.FeeTooLarge.selector)); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } else if (key.tickSpacing > manager.MAX_TICK_SPACING()) { - vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooLarge.selector)); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } else if (key.tickSpacing < manager.MIN_TICK_SPACING()) { - vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooSmall.selector)); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } else if (key.currency0 > key.currency1) { - vm.expectRevert(abi.encodeWithSelector(IPoolManager.CurrenciesInitializedOutOfOrder.selector)); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } else if (!key.hooks.isValidHookAddress(key.fee)) { - vm.expectRevert(abi.encodeWithSelector(Hooks.HookAddressNotValid.selector, address(key.hooks))); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } else { - vm.expectEmit(true, true, true, true); - emit Initialize(key.toId(), key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); - assertEq(slot0.sqrtPriceX96, sqrtPriceX96); - assertEq(slot0.protocolFees, 0); - } - } - - function test_initialize_forNativeTokens(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - PoolKey memory key = PoolKey({ - currency0: CurrencyLibrary.NATIVE, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 60 - }); - - vm.expectEmit(true, true, true, true); - emit Initialize(key.toId(), key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); - assertEq(slot0.sqrtPriceX96, sqrtPriceX96); - assertEq(slot0.protocolFees >> 12, 0); - assertEq(slot0.tick, TickMath.getTickAtSqrtRatio(sqrtPriceX96)); - } - - function test_initialize_succeedsWithHooks(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - address payable mockAddr = payable(address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG))); - address payable hookAddr = payable(MOCK_HOOKS); - - vm.etch(hookAddr, vm.getDeployedCode("EmptyTestHooks.sol:EmptyTestHooks")); - MockContract mockContract = new MockContract(); - vm.etch(mockAddr, address(mockContract).code); - - MockContract(mockAddr).setImplementation(hookAddr); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(mockAddr), tickSpacing: 60}); - - int24 tick = manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); - assertEq(slot0.sqrtPriceX96, sqrtPriceX96); - - bytes32 beforeSelector = MockHooks.beforeInitialize.selector; - bytes memory beforeParams = abi.encode(address(this), key, sqrtPriceX96, ZERO_BYTES); - - bytes32 afterSelector = MockHooks.afterInitialize.selector; - bytes memory afterParams = abi.encode(address(this), key, sqrtPriceX96, tick, ZERO_BYTES); - - assertEq(MockContract(mockAddr).timesCalledSelector(beforeSelector), 1); - assertTrue(MockContract(mockAddr).calledWithSelector(beforeSelector, beforeParams)); - assertEq(MockContract(mockAddr).timesCalledSelector(afterSelector), 1); - assertTrue(MockContract(mockAddr).calledWithSelector(afterSelector, afterParams)); - } - - function test_initialize_succeedsWithMaxTickSpacing(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - PoolKey memory key = PoolKey({ - currency0: currency0, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: manager.MAX_TICK_SPACING() - }); - - vm.expectEmit(true, true, true, true); - emit Initialize(key.toId(), key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks); - - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } - - function test_initialize_succeedsWithEmptyHooks(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - address hookEmptyAddr = EMPTY_HOOKS; - - MockHooks impl = new MockHooks(); - vm.etch(hookEmptyAddr, address(impl).code); - MockHooks mockHooks = MockHooks(hookEmptyAddr); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: mockHooks, tickSpacing: 60}); - - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); - assertEq(slot0.sqrtPriceX96, sqrtPriceX96); - } - - function test_initialize_revertsWithIdenticalTokens(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - // Both currencies are currency0 - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency0, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - vm.expectRevert(IPoolManager.CurrenciesInitializedOutOfOrder.selector); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } - - function test_initialize_revertsWithSameTokenCombo(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - PoolKey memory keyInvertedCurrency = - PoolKey({currency0: currency1, currency1: currency0, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - vm.expectRevert(IPoolManager.CurrenciesInitializedOutOfOrder.selector); - manager.initialize(keyInvertedCurrency, sqrtPriceX96, ZERO_BYTES); - } - - function test_initialize_revertsWhenPoolAlreadyInitialized(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - vm.expectRevert(Pool.PoolAlreadyInitialized.selector); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } - - function test_initialize_failsWithIncorrectSelectors() public { - address hookAddr = address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG)); - - MockHooks impl = new MockHooks(); - vm.etch(hookAddr, address(impl).code); - MockHooks mockHooks = MockHooks(hookAddr); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: mockHooks, tickSpacing: 10}); - - mockHooks.setReturnValue(mockHooks.beforeInitialize.selector, bytes4(0xdeadbeef)); - mockHooks.setReturnValue(mockHooks.afterInitialize.selector, bytes4(0xdeadbeef)); - - // Fails at beforeInitialize hook. - vm.expectRevert(Hooks.InvalidHookResponse.selector); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - - // Fail at afterInitialize hook. - mockHooks.setReturnValue(mockHooks.beforeInitialize.selector, mockHooks.beforeInitialize.selector); - vm.expectRevert(Hooks.InvalidHookResponse.selector); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - } - - function test_initialize_succeedsWithCorrectSelectors() public { - address hookAddr = address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG)); - - MockHooks impl = new MockHooks(); - vm.etch(hookAddr, address(impl).code); - MockHooks mockHooks = MockHooks(hookAddr); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: mockHooks, tickSpacing: 10}); - - mockHooks.setReturnValue(mockHooks.beforeInitialize.selector, mockHooks.beforeInitialize.selector); - mockHooks.setReturnValue(mockHooks.afterInitialize.selector, mockHooks.afterInitialize.selector); - - vm.expectEmit(true, true, true, true); - emit Initialize(key.toId(), key.currency0, key.currency1, key.fee, key.tickSpacing, key.hooks); - - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - } - - function test_initialize_failsIfTickSpaceTooLarge(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - PoolKey memory key = PoolKey({ - currency0: currency0, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: manager.MAX_TICK_SPACING() + 1 - }); - - vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooLarge.selector)); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } - - function test_initialize_failsIfTickSpaceZero(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 0}); - - vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooSmall.selector)); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } - - function test_initialize_failsIfTickSpaceNeg(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: -1}); - - vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooSmall.selector)); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - } - - function test_initialize_gas() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - snapStart("initialize"); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - snapEnd(); - } - function test_feeControllerSet() public { + deployFreshManager(); assertEq(address(manager.protocolFeeController()), address(0)); vm.expectEmit(false, false, false, true, address(manager)); - emit ProtocolFeeControllerUpdated(address(protocolFeeController)); - manager.setProtocolFeeController(protocolFeeController); - assertEq(address(manager.protocolFeeController()), address(protocolFeeController)); - } - - function test_fetchFeeWhenController(uint160 sqrtPriceX96) public { - // Assumptions tested in Pool.t.sol - vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); - vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - manager.setProtocolFeeController(protocolFeeController); - - uint16 poolProtocolFee = 4; - protocolFeeController.setSwapFeeForPool(key.toId(), poolProtocolFee); - - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); - assertEq(slot0.sqrtPriceX96, sqrtPriceX96); - assertEq(slot0.protocolFees >> 12, poolProtocolFee); + emit ProtocolFeeControllerUpdated(address(feeController)); + manager.setProtocolFeeController(feeController); + assertEq(address(manager.protocolFeeController()), address(feeController)); } function test_mint_failsIfNotInitialized() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - vm.expectRevert(); - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}), ZERO_BYTES - ); + vm.expectRevert(Pool.PoolNotInitialized.selector); + modifyPositionRouter.modifyPosition(uninitializedKey, LIQ_PARAMS, ZERO_BYTES); } function test_mint_succeedsIfInitialized(uint160 sqrtPriceX96) public { vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); - vm.expectEmit(true, true, true, true); - emit ModifyPosition(key.toId(), address(modifyPositionRouter), 0, 60, 100); - - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}), ZERO_BYTES + emit ModifyPosition( + key.toId(), + address(modifyPositionRouter), + LIQ_PARAMS.tickLower, + LIQ_PARAMS.tickUpper, + LIQ_PARAMS.liquidityDelta ); + + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); } function test_mint_succeedsForNativeTokensIfInitialized(uint160 sqrtPriceX96) public { vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - PoolKey memory key = PoolKey({ - currency0: CurrencyLibrary.NATIVE, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 60 - }); - - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); vm.expectEmit(true, true, true, true); - emit ModifyPosition(key.toId(), address(modifyPositionRouter), 0, 60, 100); - - modifyPositionRouter.modifyPosition{value: 100}( - key, IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}), ZERO_BYTES + emit ModifyPosition( + nativeKey.toId(), + address(modifyPositionRouter), + LIQ_PARAMS.tickLower, + LIQ_PARAMS.tickUpper, + LIQ_PARAMS.liquidityDelta ); + + modifyPositionRouter.modifyPosition{value: 1 ether}(nativeKey, LIQ_PARAMS, ZERO_BYTES); } function test_mint_succeedsWithHooksIfInitialized(uint160 sqrtPriceX96) public { @@ -464,20 +127,14 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { MockContract(mockAddr).setImplementation(hookAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(mockAddr), tickSpacing: 60}); - - IPoolManager.ModifyPositionParams memory params = - IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}); - - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); + (key,) = initPool(currency0, currency1, IHooks(mockAddr), 3000, sqrtPriceX96, ZERO_BYTES); - BalanceDelta balanceDelta = modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); + BalanceDelta balanceDelta = modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); bytes32 beforeSelector = MockHooks.beforeModifyPosition.selector; - bytes memory beforeParams = abi.encode(address(modifyPositionRouter), key, params, ZERO_BYTES); + bytes memory beforeParams = abi.encode(address(modifyPositionRouter), key, LIQ_PARAMS, ZERO_BYTES); bytes32 afterSelector = MockHooks.afterModifyPosition.selector; - bytes memory afterParams = abi.encode(address(modifyPositionRouter), key, params, balanceDelta, ZERO_BYTES); + bytes memory afterParams = abi.encode(address(modifyPositionRouter), key, LIQ_PARAMS, balanceDelta, ZERO_BYTES); assertEq(MockContract(mockAddr).timesCalledSelector(beforeSelector), 1); assertTrue(MockContract(mockAddr).calledWithSelector(beforeSelector, beforeParams)); @@ -492,25 +149,19 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.etch(hookAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: mockHooks, tickSpacing: 10}); - - IPoolManager.ModifyPositionParams memory params = - IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}); - - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + (key,) = initPool(currency0, currency1, mockHooks, 100, SQRT_RATIO_1_1, ZERO_BYTES); mockHooks.setReturnValue(mockHooks.beforeModifyPosition.selector, bytes4(0xdeadbeef)); mockHooks.setReturnValue(mockHooks.afterModifyPosition.selector, bytes4(0xdeadbeef)); // Fails at beforeModifyPosition hook. vm.expectRevert(Hooks.InvalidHookResponse.selector); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); // Fail at afterModifyPosition hook. mockHooks.setReturnValue(mockHooks.beforeModifyPosition.selector, mockHooks.beforeModifyPosition.selector); vm.expectRevert(Hooks.InvalidHookResponse.selector); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); } function test_mint_succeedsWithCorrectSelectors() public { @@ -520,51 +171,32 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.etch(hookAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: mockHooks, tickSpacing: 10}); - - IPoolManager.ModifyPositionParams memory params = - IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}); - - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + (key,) = initPool(currency0, currency1, mockHooks, 100, SQRT_RATIO_1_1, ZERO_BYTES); mockHooks.setReturnValue(mockHooks.beforeModifyPosition.selector, mockHooks.beforeModifyPosition.selector); mockHooks.setReturnValue(mockHooks.afterModifyPosition.selector, mockHooks.afterModifyPosition.selector); vm.expectEmit(true, true, true, true); - emit ModifyPosition(key.toId(), address(modifyPositionRouter), 0, 60, 100); + emit ModifyPosition( + key.toId(), + address(modifyPositionRouter), + LIQ_PARAMS.tickLower, + LIQ_PARAMS.tickUpper, + LIQ_PARAMS.liquidityDelta + ); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); } function test_mint_gas() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - snapStart("mint"); - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}), ZERO_BYTES - ); + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); snapEnd(); } function test_mint_withNative_gas() public { - PoolKey memory key = PoolKey({ - currency0: CurrencyLibrary.NATIVE, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 60 - }); - - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - snapStart("mint with native token"); - modifyPositionRouter.modifyPosition{value: 100}( - key, IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}), ZERO_BYTES - ); + modifyPositionRouter.modifyPosition{value: 1 ether}(nativeKey, LIQ_PARAMS, ZERO_BYTES); snapEnd(); } @@ -574,15 +206,10 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.etch(hookEmptyAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookEmptyAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: mockHooks, tickSpacing: 60}); - - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + (key,) = initPool(currency0, currency1, mockHooks, 3000, SQRT_RATIO_1_1, ZERO_BYTES); snapStart("mint with empty hook"); - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}), ZERO_BYTES - ); + modifyPositionRouter.modifyPosition(key, LIQ_PARAMS, ZERO_BYTES); snapEnd(); } @@ -590,35 +217,24 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - + key.fee = 100; IPoolManager.SwapParams memory params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: sqrtPriceX96}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); - vm.expectRevert(); + vm.expectRevert(Pool.PoolNotInitialized.selector); swapRouter.swap(key, params, testSettings, ZERO_BYTES); } function test_swap_succeedsIfInitialized() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); - IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition(key, liqParams, ZERO_BYTES); - vm.expectEmit(true, true, true, true); emit Swap( key.toId(), address(swapRouter), int128(100), int128(-98), 79228162514264329749955861424, 1e18, -1, 3000 @@ -628,32 +244,25 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_swap_succeedsWithNativeTokensIfInitialized() public { - PoolKey memory key = PoolKey({ - currency0: CurrencyLibrary.NATIVE, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 60 - }); - - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); - IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition{value: 1 ether}(key, liqParams, ZERO_BYTES); - vm.expectEmit(true, true, true, true); emit Swap( - key.toId(), address(swapRouter), int128(100), int128(-98), 79228162514264329749955861424, 1e18, -1, 3000 + nativeKey.toId(), + address(swapRouter), + int128(100), + int128(-98), + 79228162514264329749955861424, + 1e18, + -1, + 3000 ); - swapRouter.swap{value: 100}(key, swapParams, testSettings, ZERO_BYTES); + swapRouter.swap{value: 100}(nativeKey, swapParams, testSettings, ZERO_BYTES); } function test_swap_succeedsWithHooksIfInitialized() public { @@ -666,11 +275,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { MockContract(mockAddr).setImplementation(hookAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(mockAddr), tickSpacing: 60}); - - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); + (key,) = initPoolAndAddLiquidity(currency0, currency1, IHooks(mockAddr), 3000, SQRT_RATIO_1_1, ZERO_BYTES); IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); @@ -678,9 +283,6 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition(key, liqParams, ZERO_BYTES); - BalanceDelta balanceDelta = swapRouter.swap(key, swapParams, testSettings, ZERO_BYTES); bytes32 beforeSelector = MockHooks.beforeSwap.selector; @@ -702,11 +304,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.etch(hookAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: mockHooks, tickSpacing: 10}); - - IPoolManager.ModifyPositionParams memory params = - IPoolManager.ModifyPositionParams({tickLower: 0, tickUpper: 60, liquidityDelta: 100}); + (key,) = initPoolAndAddLiquidity(currency0, currency1, mockHooks, 100, SQRT_RATIO_1_1, ZERO_BYTES); IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 10, sqrtPriceLimitX96: SQRT_RATIO_1_2}); @@ -714,9 +312,6 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); - mockHooks.setReturnValue(mockHooks.beforeSwap.selector, bytes4(0xdeadbeef)); mockHooks.setReturnValue(mockHooks.afterSwap.selector, bytes4(0xdeadbeef)); @@ -737,11 +332,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.etch(hookAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: mockHooks, tickSpacing: 10}); - - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); + (key,) = initPoolAndAddLiquidity(currency0, currency1, mockHooks, 100, SQRT_RATIO_1_1, ZERO_BYTES); IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 10, sqrtPriceLimitX96: SQRT_RATIO_1_2}); @@ -749,9 +340,6 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition(key, liqParams, ZERO_BYTES); - mockHooks.setReturnValue(mockHooks.beforeSwap.selector, mockHooks.beforeSwap.selector); mockHooks.setReturnValue(mockHooks.afterSwap.selector, mockHooks.afterSwap.selector); @@ -762,49 +350,26 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_swap_gas() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); - IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition(key, liqParams, ZERO_BYTES); - snapStart("simple swap"); swapRouter.swap(key, swapParams, testSettings, ZERO_BYTES); snapEnd(); } function test_swap_withNative_gas() public { - PoolKey memory key = PoolKey({ - currency0: CurrencyLibrary.NATIVE, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 60 - }); - - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); - IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition{value: 1 ether}(key, liqParams, ZERO_BYTES); - - snapStart("simple swap native"); - swapRouter.swap{value: 100}(key, swapParams, testSettings, ZERO_BYTES); + snapStart("simple swap with native"); + swapRouter.swap{value: 100}(nativeKey, swapParams, testSettings, ZERO_BYTES); snapEnd(); } @@ -815,11 +380,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.etch(hookEmptyAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookEmptyAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: mockHooks, tickSpacing: 60}); - - IPoolManager.ModifyPositionParams memory liqParams = - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); + (key,) = initPoolAndAddLiquidity(currency0, currency1, mockHooks, 3000, SQRT_RATIO_1_1, ZERO_BYTES); IPoolManager.SwapParams memory swapParams = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); @@ -827,8 +388,6 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition(key, liqParams, ZERO_BYTES); swapRouter.swap(key, swapParams, testSettings, ZERO_BYTES); swapParams = @@ -841,20 +400,12 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_swap_GasMintClaimIfOutputNotTaken() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - IPoolManager.SwapParams memory params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}), ZERO_BYTES - ); - vm.expectEmit(true, true, true, false); emit Mint(address(swapRouter), currency1, 98); snapStart("swap mint output as claim"); @@ -866,19 +417,12 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_swap_GasUseClaimAsInput() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - IPoolManager.SwapParams memory params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: false, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}), ZERO_BYTES - ); vm.expectEmit(true, true, true, false); emit Mint(address(swapRouter), currency1, 98); swapRouter.swap(key, params, testSettings, ZERO_BYTES); @@ -902,20 +446,12 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_swap_againstLiq_gas() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); - IPoolManager.SwapParams memory params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition( - key, IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}), ZERO_BYTES - ); - swapRouter.swap(key, params, testSettings, ZERO_BYTES); params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_4}); @@ -926,90 +462,61 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_swap_againstLiqWithNative_gas() public { - PoolKey memory key = PoolKey({ - currency0: CurrencyLibrary.NATIVE, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 60 - }); - IPoolManager.SwapParams memory params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_2}); PoolSwapTest.TestSettings memory testSettings = PoolSwapTest.TestSettings({withdrawTokens: true, settleUsingTransfer: true}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - modifyPositionRouter.modifyPosition{value: 1 ether}( - key, - IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1 ether}), - ZERO_BYTES - ); - - swapRouter.swap{value: 1 ether}(key, params, testSettings, ZERO_BYTES); + swapRouter.swap{value: 1 ether}(nativeKey, params, testSettings, ZERO_BYTES); params = IPoolManager.SwapParams({zeroForOne: true, amountSpecified: 100, sqrtPriceLimitX96: SQRT_RATIO_1_4}); snapStart("swap against liquidity with native token"); - swapRouter.swap{value: 1 ether}(key, params, testSettings, ZERO_BYTES); + swapRouter.swap{value: 1 ether}(nativeKey, params, testSettings, ZERO_BYTES); snapEnd(); } function test_donate_failsIfNotInitialized() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: IHooks(address(0)), tickSpacing: 10}); vm.expectRevert(abi.encodeWithSelector(Pool.NoLiquidityToReceiveFees.selector)); - donateRouter.donate(key, 100, 100, ZERO_BYTES); + donateRouter.donate(uninitializedKey, 100, 100, ZERO_BYTES); } function test_donate_failsIfNoLiquidity(uint160 sqrtPriceX96) public { vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.initialize(key, sqrtPriceX96, ZERO_BYTES); + (key,) = initPool(currency0, currency1, IHooks(address(0)), 100, sqrtPriceX96, ZERO_BYTES); + vm.expectRevert(abi.encodeWithSelector(Pool.NoLiquidityToReceiveFees.selector)); donateRouter.donate(key, 100, 100, ZERO_BYTES); } // test successful donation if pool has liquidity function test_donate_succeedsWhenPoolHasLiquidity() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + (, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128,) = manager.pools(key.toId()); + assertEq(feeGrowthGlobal0X128, 0); + assertEq(feeGrowthGlobal1X128, 0); - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-60, 60, 100); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); snapStart("donate gas with 2 tokens"); donateRouter.donate(key, 100, 200, ZERO_BYTES); snapEnd(); - (, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128,) = manager.pools(key.toId()); - assertEq(feeGrowthGlobal0X128, 340282366920938463463374607431768211456); - assertEq(feeGrowthGlobal1X128, 680564733841876926926749214863536422912); + (, feeGrowthGlobal0X128, feeGrowthGlobal1X128,) = manager.pools(key.toId()); + assertEq(feeGrowthGlobal0X128, 34028236692093846346337); + assertEq(feeGrowthGlobal1X128, 68056473384187692692674); } function test_donate_succeedsForNativeTokensWhenPoolHasLiquidity() public { - vm.deal(address(this), 1 ether); + (, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128,) = manager.pools(nativeKey.toId()); + assertEq(feeGrowthGlobal0X128, 0); + assertEq(feeGrowthGlobal1X128, 0); - PoolKey memory key = PoolKey({ - currency0: CurrencyLibrary.NATIVE, - currency1: currency1, - fee: 100, - hooks: IHooks(address(0)), - tickSpacing: 10 - }); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + donateRouter.donate{value: 100}(nativeKey, 100, 200, ZERO_BYTES); - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-60, 60, 100); - modifyPositionRouter.modifyPosition{value: 1}(key, params, ZERO_BYTES); - donateRouter.donate{value: 100}(key, 100, 200, ZERO_BYTES); - - (, uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128,) = manager.pools(key.toId()); - assertEq(feeGrowthGlobal0X128, 340282366920938463463374607431768211456); - assertEq(feeGrowthGlobal1X128, 680564733841876926926749214863536422912); + (, feeGrowthGlobal0X128, feeGrowthGlobal1X128,) = manager.pools(nativeKey.toId()); + assertEq(feeGrowthGlobal0X128, 34028236692093846346337); + assertEq(feeGrowthGlobal1X128, 68056473384187692692674); } function test_donate_failsWithIncorrectSelectors() public { @@ -1019,12 +526,8 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.etch(hookAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: mockHooks, tickSpacing: 10}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + (key,) = initPoolAndAddLiquidity(currency0, currency1, mockHooks, 100, SQRT_RATIO_1_1, ZERO_BYTES); - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-60, 60, 100); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); mockHooks.setReturnValue(mockHooks.beforeDonate.selector, bytes4(0xdeadbeef)); mockHooks.setReturnValue(mockHooks.afterDonate.selector, bytes4(0xdeadbeef)); @@ -1045,12 +548,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { vm.etch(hookAddr, address(impl).code); MockHooks mockHooks = MockHooks(hookAddr); - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: mockHooks, tickSpacing: 10}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-60, 60, 100); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); + (key,) = initPoolAndAddLiquidity(currency0, currency1, mockHooks, 100, SQRT_RATIO_1_1, ZERO_BYTES); mockHooks.setReturnValue(mockHooks.beforeDonate.selector, mockHooks.beforeDonate.selector); mockHooks.setReturnValue(mockHooks.afterDonate.selector, mockHooks.afterDonate.selector); @@ -1059,21 +557,13 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_donate_OneToken_gas() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-60, 60, 100); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); - snapStart("donate gas with 1 token"); donateRouter.donate(key, 100, 0, ZERO_BYTES); snapEnd(); } function test_take_failsWithNoLiquidity() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 60}); + deployFreshManagerAndRouters(); vm.expectRevert(); takeRouter.take(key, 100, 0); @@ -1082,25 +572,22 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { function test_take_failsWithInvalidTokensThatDoNotReturnTrueOnTransfer() public { TestInvalidERC20 invalidToken = new TestInvalidERC20(2**255); Currency invalidCurrency = Currency.wrap(address(invalidToken)); - bool currency0Invalid = invalidCurrency < currency0; - PoolKey memory key = PoolKey({ - currency0: currency0Invalid ? invalidCurrency : currency0, - currency1: currency0Invalid ? currency0 : invalidCurrency, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 60 - }); - invalidToken.approve(address(modifyPositionRouter), type(uint256).max); invalidToken.approve(address(takeRouter), type(uint256).max); - MockERC20(Currency.unwrap(currency0)).approve(address(takeRouter), type(uint256).max); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-60, 60, 1000); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); + bool currency0Invalid = invalidCurrency < currency0; + + (key,) = initPoolAndAddLiquidity( + (currency0Invalid ? invalidCurrency : currency0), + (currency0Invalid ? currency0 : invalidCurrency), + IHooks(address(0)), + 3000, + SQRT_RATIO_1_1, + ZERO_BYTES + ); (uint256 amount0, uint256 amount1) = currency0Invalid ? (1, 0) : (0, 1); - vm.expectRevert(); + vm.expectRevert(CurrencyLibrary.ERC20TransferFailed.selector); takeRouter.take(key, amount0, amount1); // should not revert when non zero amount passed in for valid currency @@ -1110,40 +597,18 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } function test_take_succeedsWithPoolWithLiquidity() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-60, 60, 100); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); takeRouter.take(key, 1, 1); // assertions inside takeRouter because it takes then settles } function test_take_succeedsWithPoolWithLiquidityWithNativeToken() public { - PoolKey memory key = PoolKey({ - currency0: CurrencyLibrary.NATIVE, - currency1: currency1, - fee: 100, - hooks: IHooks(address(0)), - tickSpacing: 10 - }); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-60, 60, 100); - modifyPositionRouter.modifyPosition{value: 100}(key, params, ZERO_BYTES); - takeRouter.take{value: 1}(key, 1, 1); // assertions inside takeRouter because it takes then settles + takeRouter.take{value: 1}(nativeKey, 1, 1); // assertions inside takeRouter because it takes then settles } function test_setProtocolFee_updatesProtocolFeeForInitializedPool() public { uint24 protocolFee = 4; - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); assertEq(slot0.protocolFees, 0); - manager.setProtocolFeeController(IProtocolFeeController(address(feeController))); feeController.setSwapFeeForPool(key.toId(), uint16(protocolFee)); vm.expectEmit(false, false, false, true); @@ -1153,14 +618,12 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { function test_collectProtocolFees_initializesWithProtocolFeeIfCalled() public { uint24 protocolFee = 260; // 0001 00 00 0100 - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.setProtocolFeeController(IProtocolFeeController(address(feeController))); + // sets the upper 12 bits - feeController.setSwapFeeForPool(key.toId(), uint16(protocolFee)); + feeController.setSwapFeeForPool(uninitializedKey.toId(), uint16(protocolFee)); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); + manager.initialize(uninitializedKey, SQRT_RATIO_1_1, ZERO_BYTES); + (Pool.Slot0 memory slot0,,,) = manager.pools(uninitializedKey.toId()); assertEq(slot0.protocolFees, protocolFee << 12); } @@ -1168,17 +631,12 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { uint24 protocolFee = 260; // 0001 00 00 0100 uint256 expectedFees = 7; - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.setProtocolFeeController(IProtocolFeeController(address(feeController))); feeController.setSwapFeeForPool(key.toId(), uint16(protocolFee)); + manager.setProtocolFees(key); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); assertEq(slot0.protocolFees, protocolFee << 12); - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-120, 120, 10 ether); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); swapRouter.swap( key, IPoolManager.SwapParams(true, 10000, SQRT_RATIO_1_2), PoolSwapTest.TestSettings(true, true), ZERO_BYTES ); @@ -1197,17 +655,12 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { uint24 protocolFee = 260; // 0001 00 00 0100 uint256 expectedFees = 7; - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 3000, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.setProtocolFeeController(IProtocolFeeController(address(feeController))); feeController.setSwapFeeForPool(key.toId(), uint16(protocolFee)); + manager.setProtocolFees(key); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); assertEq(slot0.protocolFees, protocolFee << 12); - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-120, 120, 10 ether); - modifyPositionRouter.modifyPosition(key, params, ZERO_BYTES); swapRouter.swap( key, IPoolManager.SwapParams(true, 10000, SQRT_RATIO_1_2), PoolSwapTest.TestSettings(true, true), ZERO_BYTES ); @@ -1225,24 +678,18 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { uint256 expectedFees = 7; Currency nativeCurrency = CurrencyLibrary.NATIVE; - PoolKey memory key = PoolKey({ - currency0: nativeCurrency, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 10 - }); - manager.setProtocolFeeController(IProtocolFeeController(address(feeController))); - feeController.setSwapFeeForPool(key.toId(), uint16(protocolFee)); + // set protocol fee before initializing the pool as it is fetched on initialization + feeController.setSwapFeeForPool(nativeKey.toId(), uint16(protocolFee)); + manager.setProtocolFees(nativeKey); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); + (Pool.Slot0 memory slot0,,,) = manager.pools(nativeKey.toId()); assertEq(slot0.protocolFees, protocolFee << 12); - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-120, 120, 10 ether); - modifyPositionRouter.modifyPosition{value: 10 ether}(key, params, ZERO_BYTES); swapRouter.swap{value: 10000}( - key, IPoolManager.SwapParams(true, 10000, SQRT_RATIO_1_2), PoolSwapTest.TestSettings(true, true), ZERO_BYTES + nativeKey, + IPoolManager.SwapParams(true, 10000, SQRT_RATIO_1_2), + PoolSwapTest.TestSettings(true, true), + ZERO_BYTES ); assertEq(manager.protocolFeesAccrued(nativeCurrency), expectedFees); @@ -1260,24 +707,17 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { uint256 expectedFees = 7; Currency nativeCurrency = CurrencyLibrary.NATIVE; - PoolKey memory key = PoolKey({ - currency0: nativeCurrency, - currency1: currency1, - fee: 3000, - hooks: IHooks(address(0)), - tickSpacing: 10 - }); - manager.setProtocolFeeController(IProtocolFeeController(address(feeController))); - feeController.setSwapFeeForPool(key.toId(), uint16(protocolFee)); + feeController.setSwapFeeForPool(nativeKey.toId(), uint16(protocolFee)); + manager.setProtocolFees(nativeKey); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - (Pool.Slot0 memory slot0,,,) = manager.pools(key.toId()); + (Pool.Slot0 memory slot0,,,) = manager.pools(nativeKey.toId()); assertEq(slot0.protocolFees, protocolFee << 12); - IPoolManager.ModifyPositionParams memory params = IPoolManager.ModifyPositionParams(-120, 120, 10 ether); - modifyPositionRouter.modifyPosition{value: 10 ether}(key, params, ZERO_BYTES); swapRouter.swap{value: 10000}( - key, IPoolManager.SwapParams(true, 10000, SQRT_RATIO_1_2), PoolSwapTest.TestSettings(true, true), ZERO_BYTES + nativeKey, + IPoolManager.SwapParams(true, 10000, SQRT_RATIO_1_2), + PoolSwapTest.TestSettings(true, true), + ZERO_BYTES ); assertEq(manager.protocolFeesAccrued(nativeCurrency), expectedFees); @@ -1301,7 +741,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { } // function testExtsloadForPoolPrice() public { - // IPoolManager.PoolKey memory key = IPoolManager.PoolKey({ + // IPoolManager.key = IPoolManager.PoolKey({ // currency0: currency0, // currency1: currency1, // fee: 100, @@ -1326,7 +766,7 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { // } // function testExtsloadMultipleSlots() public { - // IPoolManager.PoolKey memory key = IPoolManager.PoolKey({ + // IPoolManager.key = IPoolManager.PoolKey({ // currency0: currency0, // currency1: currency1, // fee: 100, @@ -1365,19 +805,11 @@ contract PoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { // } function test_getPosition() public { - PoolKey memory key = - PoolKey({currency0: currency0, currency1: currency1, fee: 100, hooks: IHooks(address(0)), tickSpacing: 10}); - manager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); - - modifyPositionRouter.modifyPosition(key, IPoolManager.ModifyPositionParams(-120, 120, 5 ether), ZERO_BYTES); - Position.Info memory managerPosition = manager.getPosition(key.toId(), address(modifyPositionRouter), -120, 120); - - assertEq(managerPosition.liquidity, 5 ether); + assert(LIQ_PARAMS.liquidityDelta > 0); + assertEq(managerPosition.liquidity, uint128(uint256(LIQ_PARAMS.liquidityDelta))); } - receive() external payable {} - function supportsInterface(bytes4) external pure returns (bool) { return true; } diff --git a/test/PoolManagerInitialize.t.sol b/test/PoolManagerInitialize.t.sol new file mode 100644 index 000000000..b6512c123 --- /dev/null +++ b/test/PoolManagerInitialize.t.sol @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {Test} from "forge-std/Test.sol"; +import {IHooks} from "../src/interfaces/IHooks.sol"; +import {Hooks} from "../src/libraries/Hooks.sol"; +import {IPoolManager} from "../src/interfaces/IPoolManager.sol"; +import {IFees} from "../src/interfaces/IFees.sol"; +import {PoolManager} from "../src/PoolManager.sol"; +import {TickMath} from "../src/libraries/TickMath.sol"; +import {Pool} from "../src/libraries/Pool.sol"; +import {Deployers} from "./utils/Deployers.sol"; +import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; +import {MockHooks} from "../src/test/MockHooks.sol"; +import {MockContract} from "../src/test/MockContract.sol"; +import {EmptyTestHooks} from "../src/test/EmptyTestHooks.sol"; +import {PoolKey} from "../src/types/PoolKey.sol"; +import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; +import {PoolId, PoolIdLibrary} from "../src/types/PoolId.sol"; +import {FeeLibrary} from "../src/libraries/FeeLibrary.sol"; + +contract PoolManagerInitializeTest is Test, Deployers, GasSnapshot { + using Hooks for IHooks; + using PoolIdLibrary for PoolKey; + using FeeLibrary for uint24; + + event Initialize( + PoolId indexed poolId, + Currency indexed currency0, + Currency indexed currency1, + uint24 fee, + int24 tickSpacing, + IHooks hooks + ); + + address ADDRESS_ZERO = address(0); + address EMPTY_HOOKS = address(0xf000000000000000000000000000000000000000); + address ALL_HOOKS = address(0xff00000000000000000000000000000000000001); + address MOCK_HOOKS = address(0xfF00000000000000000000000000000000000000); + + function setUp() public { + deployFreshManagerAndRouters(); + (currency0, currency1) = deployMintAndApprove2Currencies(); + + uninitializedKey = PoolKey({ + currency0: currency0, + currency1: currency1, + fee: 3000, + hooks: IHooks(ADDRESS_ZERO), + tickSpacing: 60 + }); + } + + function test_initialize(PoolKey memory key0, uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + // tested in Hooks.t.sol + key0.hooks = IHooks(ADDRESS_ZERO); + + if (key0.fee & FeeLibrary.STATIC_FEE_MASK >= 1000000) { + vm.expectRevert(abi.encodeWithSelector(IFees.FeeTooLarge.selector)); + manager.initialize(key0, sqrtPriceX96, ZERO_BYTES); + } else if (key0.tickSpacing > manager.MAX_TICK_SPACING()) { + vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooLarge.selector)); + manager.initialize(key0, sqrtPriceX96, ZERO_BYTES); + } else if (key0.tickSpacing < manager.MIN_TICK_SPACING()) { + vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooSmall.selector)); + manager.initialize(key0, sqrtPriceX96, ZERO_BYTES); + } else if (key0.currency0 > key0.currency1) { + vm.expectRevert(abi.encodeWithSelector(IPoolManager.CurrenciesInitializedOutOfOrder.selector)); + manager.initialize(key0, sqrtPriceX96, ZERO_BYTES); + } else if (!key0.hooks.isValidHookAddress(key0.fee)) { + vm.expectRevert(abi.encodeWithSelector(Hooks.HookAddressNotValid.selector, address(key0.hooks))); + manager.initialize(key0, sqrtPriceX96, ZERO_BYTES); + } else { + vm.expectEmit(true, true, true, true); + emit Initialize(key0.toId(), key0.currency0, key0.currency1, key0.fee, key0.tickSpacing, key0.hooks); + manager.initialize(key0, sqrtPriceX96, ZERO_BYTES); + + (Pool.Slot0 memory slot0,,,) = manager.pools(key0.toId()); + assertEq(slot0.sqrtPriceX96, sqrtPriceX96); + assertEq(slot0.protocolFees, 0); + } + } + + function test_initialize_forNativeTokens(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + uninitializedKey.currency0 = CurrencyLibrary.NATIVE; + + vm.expectEmit(true, true, true, true); + emit Initialize( + uninitializedKey.toId(), + uninitializedKey.currency0, + uninitializedKey.currency1, + uninitializedKey.fee, + uninitializedKey.tickSpacing, + uninitializedKey.hooks + ); + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + + (Pool.Slot0 memory slot0,,,) = manager.pools(uninitializedKey.toId()); + assertEq(slot0.sqrtPriceX96, sqrtPriceX96); + assertEq(slot0.protocolFees >> 12, 0); + assertEq(slot0.tick, TickMath.getTickAtSqrtRatio(sqrtPriceX96)); + } + + function test_initialize_succeedsWithHooks(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + address payable mockAddr = payable(address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG))); + address payable hookAddr = payable(MOCK_HOOKS); + + vm.etch(hookAddr, vm.getDeployedCode("EmptyTestHooks.sol:EmptyTestHooks")); + MockContract mockContract = new MockContract(); + vm.etch(mockAddr, address(mockContract).code); + + MockContract(mockAddr).setImplementation(hookAddr); + + uninitializedKey.hooks = IHooks(mockAddr); + + int24 tick = manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + (Pool.Slot0 memory slot0,,,) = manager.pools(uninitializedKey.toId()); + assertEq(slot0.sqrtPriceX96, sqrtPriceX96); + + bytes32 beforeSelector = MockHooks.beforeInitialize.selector; + bytes memory beforeParams = abi.encode(address(this), uninitializedKey, sqrtPriceX96, ZERO_BYTES); + + bytes32 afterSelector = MockHooks.afterInitialize.selector; + bytes memory afterParams = abi.encode(address(this), uninitializedKey, sqrtPriceX96, tick, ZERO_BYTES); + + assertEq(MockContract(mockAddr).timesCalledSelector(beforeSelector), 1); + assertTrue(MockContract(mockAddr).calledWithSelector(beforeSelector, beforeParams)); + assertEq(MockContract(mockAddr).timesCalledSelector(afterSelector), 1); + assertTrue(MockContract(mockAddr).calledWithSelector(afterSelector, afterParams)); + } + + function test_initialize_succeedsWithMaxTickSpacing(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + uninitializedKey.tickSpacing = manager.MAX_TICK_SPACING(); + + vm.expectEmit(true, true, true, true); + emit Initialize( + uninitializedKey.toId(), + uninitializedKey.currency0, + uninitializedKey.currency1, + uninitializedKey.fee, + uninitializedKey.tickSpacing, + uninitializedKey.hooks + ); + + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + } + + function test_initialize_succeedsWithEmptyHooks(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + address hookEmptyAddr = EMPTY_HOOKS; + + MockHooks impl = new MockHooks(); + vm.etch(hookEmptyAddr, address(impl).code); + MockHooks mockHooks = MockHooks(hookEmptyAddr); + + uninitializedKey.hooks = mockHooks; + + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + (Pool.Slot0 memory slot0,,,) = manager.pools(uninitializedKey.toId()); + assertEq(slot0.sqrtPriceX96, sqrtPriceX96); + } + + function test_initialize_revertsWithIdenticalTokens(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + // Both currencies are currency0 + uninitializedKey.currency1 = currency0; + + vm.expectRevert(IPoolManager.CurrenciesInitializedOutOfOrder.selector); + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + } + + function test_initialize_revertsWithSameTokenCombo(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + uninitializedKey.currency1 = currency0; + uninitializedKey.currency0 = currency1; + + vm.expectRevert(IPoolManager.CurrenciesInitializedOutOfOrder.selector); + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + } + + function test_initialize_fetchFeeWhenController(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + manager.setProtocolFeeController(feeController); + uint16 poolProtocolFee = 4; + feeController.setSwapFeeForPool(uninitializedKey.toId(), poolProtocolFee); + + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + + (Pool.Slot0 memory slot0,,,) = manager.pools(uninitializedKey.toId()); + assertEq(slot0.sqrtPriceX96, sqrtPriceX96); + assertEq(slot0.protocolFees >> 12, poolProtocolFee); + } + + function test_initialize_revertsWhenPoolAlreadyInitialized(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + vm.expectRevert(Pool.PoolAlreadyInitialized.selector); + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + } + + function test_initialize_failsWithIncorrectSelectors() public { + address hookAddr = address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG)); + + MockHooks impl = new MockHooks(); + vm.etch(hookAddr, address(impl).code); + MockHooks mockHooks = MockHooks(hookAddr); + + uninitializedKey.hooks = mockHooks; + + mockHooks.setReturnValue(mockHooks.beforeInitialize.selector, bytes4(0xdeadbeef)); + mockHooks.setReturnValue(mockHooks.afterInitialize.selector, bytes4(0xdeadbeef)); + + // Fails at beforeInitialize hook. + vm.expectRevert(Hooks.InvalidHookResponse.selector); + manager.initialize(uninitializedKey, SQRT_RATIO_1_1, ZERO_BYTES); + + // Fail at afterInitialize hook. + mockHooks.setReturnValue(mockHooks.beforeInitialize.selector, mockHooks.beforeInitialize.selector); + vm.expectRevert(Hooks.InvalidHookResponse.selector); + manager.initialize(uninitializedKey, SQRT_RATIO_1_1, ZERO_BYTES); + } + + function test_initialize_succeedsWithCorrectSelectors() public { + address hookAddr = address(uint160(Hooks.BEFORE_INITIALIZE_FLAG | Hooks.AFTER_INITIALIZE_FLAG)); + + MockHooks impl = new MockHooks(); + vm.etch(hookAddr, address(impl).code); + MockHooks mockHooks = MockHooks(hookAddr); + + uninitializedKey.hooks = mockHooks; + + mockHooks.setReturnValue(mockHooks.beforeInitialize.selector, mockHooks.beforeInitialize.selector); + mockHooks.setReturnValue(mockHooks.afterInitialize.selector, mockHooks.afterInitialize.selector); + + vm.expectEmit(true, true, true, true); + emit Initialize( + uninitializedKey.toId(), + uninitializedKey.currency0, + uninitializedKey.currency1, + uninitializedKey.fee, + uninitializedKey.tickSpacing, + uninitializedKey.hooks + ); + + manager.initialize(uninitializedKey, SQRT_RATIO_1_1, ZERO_BYTES); + } + + function test_initialize_failsIfTickSpaceTooLarge(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + uninitializedKey.tickSpacing = manager.MAX_TICK_SPACING() + 1; + + vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooLarge.selector)); + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + } + + function test_initialize_failsIfTickSpaceZero(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + uninitializedKey.tickSpacing = 0; + + vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooSmall.selector)); + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + } + + function test_initialize_failsIfTickSpaceNeg(uint160 sqrtPriceX96) public { + // Assumptions tested in Pool.t.sol + vm.assume(sqrtPriceX96 >= TickMath.MIN_SQRT_RATIO); + vm.assume(sqrtPriceX96 < TickMath.MAX_SQRT_RATIO); + + uninitializedKey.tickSpacing = -1; + + vm.expectRevert(abi.encodeWithSelector(IPoolManager.TickSpacingTooSmall.selector)); + manager.initialize(uninitializedKey, sqrtPriceX96, ZERO_BYTES); + } + + function test_initialize_gas() public { + snapStart("initialize"); + manager.initialize(uninitializedKey, SQRT_RATIO_1_1, ZERO_BYTES); + snapEnd(); + } +} diff --git a/test/PoolManagerReentrancyTest.t.sol b/test/PoolManagerReentrancyTest.t.sol index df6cd93e5..ea578b2cf 100644 --- a/test/PoolManagerReentrancyTest.t.sol +++ b/test/PoolManagerReentrancyTest.t.sol @@ -5,10 +5,10 @@ import {Test} from "forge-std/Test.sol"; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {Currency, CurrencyLibrary} from "../src/types/Currency.sol"; import {IPoolManager} from "../src/interfaces/IPoolManager.sol"; +import {IHooks} from "../src/interfaces/IHooks.sol"; import {ILockCallback} from "../src/interfaces/callback/ILockCallback.sol"; import {PoolManager} from "../src/PoolManager.sol"; import {Deployers} from "./utils/Deployers.sol"; -import {TokenFixture} from "./utils/TokenFixture.sol"; contract TokenLocker is ILockCallback { using CurrencyLibrary for Currency; @@ -128,13 +128,11 @@ contract ParallelLocker is ILockCallback { } } -contract PoolManagerReentrancyTest is Test, Deployers, TokenFixture { +contract PoolManagerReentrancyTest is Test, Deployers { uint256 constant INDEX_OFFSET = 1; - PoolManager manager; function setUp() public { - initializeTokens(); - manager = Deployers.createFreshManager(); + initializeManagerRoutersAndPoolsWithLiq(IHooks(address(0))); } function testTokenLocker() public { diff --git a/test/SafeCast.t.sol b/test/SafeCast.t.sol index 116aed046..a0e0b6a98 100644 --- a/test/SafeCast.t.sol +++ b/test/SafeCast.t.sol @@ -10,7 +10,7 @@ contract SafeCastTest is Test { if (x <= type(uint160).max) { assertEq(uint256(SafeCast.toUint160(x)), x); } else { - vm.expectRevert(); + vm.expectRevert(SafeCast.SafeCastOverflow.selector); SafeCast.toUint160(x); } } @@ -19,7 +19,7 @@ contract SafeCastTest is Test { if (x <= type(int128).max && x >= type(int128).min) { assertEq(int256(SafeCast.toInt128(x)), x); } else { - vm.expectRevert(); + vm.expectRevert(SafeCast.SafeCastOverflow.selector); SafeCast.toInt128(x); } } @@ -28,7 +28,7 @@ contract SafeCastTest is Test { if (x <= uint256(type(int256).max)) { assertEq(uint256(SafeCast.toInt256(x)), x); } else { - vm.expectRevert(); + vm.expectRevert(SafeCast.SafeCastOverflow.selector); SafeCast.toInt256(x); } } @@ -37,7 +37,7 @@ contract SafeCastTest is Test { if (x <= uint128(type(int128).max)) { assertEq(uint128(SafeCast.toInt128(x)), x); } else { - vm.expectRevert(); + vm.expectRevert(SafeCast.SafeCastOverflow.selector); SafeCast.toInt128(x); } } diff --git a/test/SqrtPriceMath.t.sol b/test/SqrtPriceMath.t.sol index f4348a8fd..68a8edeca 100644 --- a/test/SqrtPriceMath.t.sol +++ b/test/SqrtPriceMath.t.sol @@ -10,12 +10,12 @@ import {Constants} from "./utils/Constants.sol"; contract SqrtPriceMathTestTest is Test, GasSnapshot { function test_getNextSqrtPriceFromInput_revertsIfPriceIsZero() public { - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.InvalidPriceOrLiquidity.selector); SqrtPriceMath.getNextSqrtPriceFromInput(0, 0, 0.1 ether, false); } function test_getNextSqrtPriceFromInput_revertsIfLiquidityIsZero() public { - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.InvalidPriceOrLiquidity.selector); SqrtPriceMath.getNextSqrtPriceFromInput(1, 0, 0.1 ether, true); } @@ -109,12 +109,12 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { } function test_getNextSqrtPriceFromOutput_revertsIfPriceIsZero() public { - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.InvalidPriceOrLiquidity.selector); SqrtPriceMath.getNextSqrtPriceFromOutput(0, 0, 0.1 ether, false); } function test_getNextSqrtPriceFromOutput_revertsIfLiquidityIsZero() public { - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.InvalidPriceOrLiquidity.selector); SqrtPriceMath.getNextSqrtPriceFromOutput(1, 0, 0.1 ether, true); } @@ -123,7 +123,7 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { uint128 liquidity = 1024; uint256 amountOut = 4; - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.PriceOverflow.selector); SqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, false); } @@ -132,7 +132,7 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { uint128 liquidity = 1024; uint256 amountOut = 5; - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.PriceOverflow.selector); SqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, false); } @@ -141,7 +141,7 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { uint128 liquidity = 1024; uint256 amountOut = 262145; - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.NotEnoughLiquidity.selector); SqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, true); } @@ -150,7 +150,7 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { uint128 liquidity = 1024; uint256 amountOut = 262144; - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.NotEnoughLiquidity.selector); SqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, true); } @@ -171,7 +171,7 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { uint128 liquidity = 1024; uint256 amountOut = 4; - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.PriceOverflow.selector); SqrtPriceMath.getNextSqrtPriceFromOutput(price, liquidity, amountOut, false); } @@ -217,7 +217,7 @@ contract SqrtPriceMathTestTest is Test, GasSnapshot { function test_getNextSqrtPriceFromOutput_revertsIfAmountOutIsImpossibleInOneForZeroDirection() public { uint160 sqrtP = Constants.SQRT_RATIO_1_1; - vm.expectRevert(); + vm.expectRevert(SqrtPriceMath.PriceOverflow.selector); SqrtPriceMath.getNextSqrtPriceFromOutput(sqrtP, 1, Constants.MAX_UINT256, false); } diff --git a/test/utils/Deployers.sol b/test/utils/Deployers.sol index 3f773de25..a1cc4266c 100644 --- a/test/utils/Deployers.sol +++ b/test/utils/Deployers.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.20; import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; import {Hooks} from "../../src/libraries/Hooks.sol"; -import {Currency} from "../../src/types/Currency.sol"; +import {Currency, CurrencyLibrary} from "../../src/types/Currency.sol"; import {IHooks} from "../../src/interfaces/IHooks.sol"; import {IPoolManager} from "../../src/interfaces/IPoolManager.sol"; import {PoolManager} from "../../src/PoolManager.sol"; @@ -12,20 +12,66 @@ import {FeeLibrary} from "../../src/libraries/FeeLibrary.sol"; import {PoolKey} from "../../src/types/PoolKey.sol"; import {Constants} from "../utils/Constants.sol"; import {SortTokens} from "./SortTokens.sol"; +import {PoolModifyPositionTest} from "../../src/test/PoolModifyPositionTest.sol"; +import {PoolSwapTest} from "../../src/test/PoolSwapTest.sol"; +import {PoolDonateTest} from "../../src/test/PoolDonateTest.sol"; +import {PoolTakeTest} from "../../src/test/PoolTakeTest.sol"; +import {ProtocolFeeControllerTest} from "../../src/test/ProtocolFeeControllerTest.sol"; contract Deployers { using FeeLibrary for uint24; using PoolIdLibrary for PoolKey; + // Helpful test constants bytes constant ZERO_BYTES = new bytes(0); - uint160 constant SQRT_RATIO_1_1 = Constants.SQRT_RATIO_1_1; uint160 constant SQRT_RATIO_1_2 = Constants.SQRT_RATIO_1_2; uint160 constant SQRT_RATIO_1_4 = Constants.SQRT_RATIO_1_4; uint160 constant SQRT_RATIO_4_1 = Constants.SQRT_RATIO_4_1; - function deployCurrencies(uint256 totalSupply) internal returns (Currency currency0, Currency currency1) { - MockERC20[] memory tokens = deployTokens(2, totalSupply); + IPoolManager.ModifyPositionParams internal LIQ_PARAMS = + IPoolManager.ModifyPositionParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e18}); + + // Global variables + Currency internal currency0; + Currency internal currency1; + PoolManager manager; + PoolModifyPositionTest modifyPositionRouter; + PoolSwapTest swapRouter; + PoolDonateTest donateRouter; + PoolTakeTest takeRouter; + ProtocolFeeControllerTest feeController; + + PoolKey key; + PoolKey nativeKey; + PoolKey uninitializedKey; + PoolKey uninitializedNativeKey; + + function deployFreshManager() internal { + manager = new PoolManager(500000); + } + + function deployFreshManagerAndRouters() internal { + deployFreshManager(); + swapRouter = new PoolSwapTest(manager); + modifyPositionRouter = new PoolModifyPositionTest(manager); + donateRouter = new PoolDonateTest(manager); + takeRouter = new PoolTakeTest(manager); + feeController = new ProtocolFeeControllerTest(); + manager.setProtocolFeeController(feeController); + } + + function deployMintAndApprove2Currencies() internal returns (Currency, Currency) { + MockERC20[] memory tokens = deployTokens(2, 1000 ether); + + address[4] memory toApprove = + [address(swapRouter), address(modifyPositionRouter), address(donateRouter), address(takeRouter)]; + + for (uint256 i = 0; i < toApprove.length; i++) { + tokens[0].approve(toApprove[i], 1000 ether); + tokens[1].approve(toApprove[i], 1000 ether); + } + return SortTokens.sort(tokens[0], tokens[1]); } @@ -37,50 +83,59 @@ contract Deployers { } } - function createAndInitPool(PoolManager manager, IHooks hooks, uint24 fee, uint160 sqrtPriceX96) - internal - returns (PoolKey memory key, PoolId id) - { - (key, id) = createAndInitPool(manager, hooks, fee, sqrtPriceX96, ZERO_BYTES); - } - - function createAndInitPool( - PoolManager manager, + function initPool( + Currency _currency0, + Currency _currency1, IHooks hooks, uint24 fee, uint160 sqrtPriceX96, bytes memory initData - ) private returns (PoolKey memory key, PoolId id) { - MockERC20[] memory tokens = deployTokens(2, 2 ** 255); - (Currency currency0, Currency currency1) = SortTokens.sort(tokens[0], tokens[1]); - key = PoolKey(currency0, currency1, fee, fee.isDynamicFee() ? int24(60) : int24(fee / 100 * 2), hooks); - id = key.toId(); - manager.initialize(key, sqrtPriceX96, initData); + ) internal returns (PoolKey memory _key, PoolId id) { + _key = PoolKey(_currency0, _currency1, fee, fee.isDynamicFee() ? int24(60) : int24(fee / 100 * 2), hooks); + id = _key.toId(); + manager.initialize(_key, sqrtPriceX96, initData); } - function createKey(IHooks hooks, uint24 fee) internal returns (PoolKey memory key) { - MockERC20[] memory tokens = deployTokens(2, 2 ** 255); - (Currency currency0, Currency currency1) = SortTokens.sort(tokens[0], tokens[1]); - key = PoolKey(currency0, currency1, fee, fee.isDynamicFee() ? int24(60) : int24(fee / 100 * 2), hooks); + function initPoolAndAddLiquidity( + Currency _currency0, + Currency _currency1, + IHooks hooks, + uint24 fee, + uint160 sqrtPriceX96, + bytes memory initData + ) internal returns (PoolKey memory _key, PoolId id) { + (_key, id) = initPool(_currency0, _currency1, hooks, fee, sqrtPriceX96, initData); + modifyPositionRouter.modifyPosition{value: msg.value}(_key, LIQ_PARAMS, ZERO_BYTES); } - function createAndInitFreshPool(IHooks hooks, uint24 fee, uint160 sqrtPriceX96) - internal - returns (PoolManager manager, PoolKey memory key, PoolId id) - { - (manager, key, id) = createAndInitFreshPool(hooks, fee, sqrtPriceX96, ZERO_BYTES); + function initPoolAndAddLiquidityETH( + Currency _currency0, + Currency _currency1, + IHooks hooks, + uint24 fee, + uint160 sqrtPriceX96, + bytes memory initData, + uint256 msgValue + ) internal returns (PoolKey memory _key, PoolId id) { + (_key, id) = initPool(_currency0, _currency1, hooks, fee, sqrtPriceX96, initData); + modifyPositionRouter.modifyPosition{value: msgValue}(_key, LIQ_PARAMS, ZERO_BYTES); } - function createAndInitFreshPool(IHooks hooks, uint24 fee, uint160 sqrtPriceX96, bytes memory initData) - internal - returns (PoolManager manager, PoolKey memory key, PoolId id) - { - manager = createFreshManager(); - (key, id) = createAndInitPool(manager, hooks, fee, sqrtPriceX96, initData); - return (manager, key, id); + // Deploys the manager, all test routers, and sets up 2 pools: with and without native + function initializeManagerRoutersAndPoolsWithLiq(IHooks hooks) internal { + deployFreshManagerAndRouters(); + // sets the global currencyies and key + (currency0, currency1) = deployMintAndApprove2Currencies(); + (key,) = initPoolAndAddLiquidity(currency0, currency1, hooks, 3000, SQRT_RATIO_1_1, ZERO_BYTES); + (nativeKey,) = initPoolAndAddLiquidityETH( + CurrencyLibrary.NATIVE, currency1, hooks, 3000, SQRT_RATIO_1_1, ZERO_BYTES, 1 ether + ); + uninitializedKey = key; + uninitializedNativeKey = nativeKey; + uninitializedKey.fee = 100; + uninitializedNativeKey.fee = 100; } - function createFreshManager() internal returns (PoolManager manager) { - manager = new PoolManager(500000); - } + // to receive refunds of spare eth from test helpers + receive() external payable {} } diff --git a/test/utils/TokenFixture.sol b/test/utils/TokenFixture.sol deleted file mode 100644 index 439e930b7..000000000 --- a/test/utils/TokenFixture.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.20; - -import {MockERC20} from "solmate/test/utils/mocks/MockERC20.sol"; -import {Currency} from "../../src/types/Currency.sol"; -import {SortTokens} from "./SortTokens.sol"; - -contract TokenFixture { - Currency internal currency1; - Currency internal currency0; - - function initializeTokens() internal { - MockERC20 tokenA = new MockERC20("TestA", "A", 18); - tokenA.mint(address(this), 1000 ether); - MockERC20 tokenB = new MockERC20("TestB", "B", 18); - tokenB.mint(address(this), 1000 ether); - - (currency0, currency1) = SortTokens.sort(tokenA, tokenB); - } -}