From 02e4b3049896beea976b5538fad24a8f47d8a8f0 Mon Sep 17 00:00:00 2001 From: ChefMist <133624774+ChefMist@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:24:51 +0800 Subject: [PATCH] feat: add pausable (#10) * feat: add pausable * refactor: Remove unused imports in IPausableRole * refactor: Tweak to !hasPausableRole[msg.sender] --- .../BinHookTest#testBurnSucceedsWithHook.snap | 2 +- ...inHookTest#testDonateSucceedsWithHook.snap | 2 +- ...okTest#testInitializeSucceedsWithHook.snap | 2 +- .../BinHookTest#testMintSucceedsWithHook.snap | 2 +- .../BinHookTest#testSwapSucceedsWithHook.snap | 2 +- ...oolManagerTest#testBurnNativeCurrency.snap | 2 +- ...oolManagerTest#testFuzz_SetMaxBinStep.snap | 2 +- ...BinPoolManagerTest#testGasBurnHalfBin.snap | 2 +- ...inPoolManagerTest#testGasBurnNineBins.snap | 2 +- .../BinPoolManagerTest#testGasBurnOneBin.snap | 2 +- .../BinPoolManagerTest#testGasDonate.snap | 2 +- ...nPoolManagerTest#testGasMintNneBins-1.snap | 2 +- ...nPoolManagerTest#testGasMintNneBins-2.snap | 2 +- ...inPoolManagerTest#testGasMintOneBin-1.snap | 2 +- ...inPoolManagerTest#testGasMintOneBin-2.snap | 2 +- ...olManagerTest#testGasSwapMultipleBins.snap | 2 +- ...nagerTest#testGasSwapOverBigBinIdGate.snap | 2 +- ...nPoolManagerTest#testGasSwapSingleBin.snap | 2 +- ...oolManagerTest#testMintNativeCurrency.snap | 2 +- .../BinPoolManagerTest#testNoOpGas_Burn.snap | 2 +- ...BinPoolManagerTest#testNoOpGas_Donate.snap | 2 +- ...oolManagerTest#testNoOpGas_Initialize.snap | 2 +- .../BinPoolManagerTest#testNoOpGas_Mint.snap | 2 +- .../BinPoolManagerTest#testNoOpGas_Swap.snap | 2 +- .../BinPoolManagerTest#testSetMasterChef.snap | 2 +- ...BinPoolManagerTest#testSetProtocolFee.snap | 2 +- ...oolManagerTest#addLiquidity_fromEmpty.snap | 2 +- ...ManagerTest#addLiquidity_fromNonEmpty.snap | 2 +- ...lManagerTest#addLiquidity_nativeToken.snap | 2 +- .../CLPoolManagerTest#donateBothTokens.snap | 2 +- .../CLPoolManagerTest#gasDonateOneToken.snap | 2 +- ...oolManagerTest#initializeWithoutHooks.snap | 2 +- ...anagerTest#removeLiquidity_toNonEmpty.snap | 2 +- .../CLPoolManagerTest#setLmPool.snap | 2 +- .../CLPoolManagerTest#setMasterChef.snap | 2 +- ...PoolManagerTest#swap_againstLiquidity.snap | 2 +- ...gerTest#swap_leaveSurplusTokenInVault.snap | 2 +- ...oolManagerTest#swap_runOutOfLiquidity.snap | 2 +- .../CLPoolManagerTest#swap_simple.snap | 2 +- ...nagerTest#swap_useSurplusTokenAsInput.snap | 2 +- .../CLPoolManagerTest#swap_withHooks.snap | 2 +- .../CLPoolManagerTest#swap_withNative.snap | 2 +- ...CLPoolManagerTest#testNoOp_gas_Donate.snap | 2 +- ...olManagerTest#testNoOp_gas_Initialize.snap | 2 +- ...nagerTest#testNoOp_gas_ModifyPosition.snap | 2 +- .../CLPoolManagerTest#testNoOp_gas_Swap.snap | 2 +- .../ExtsloadTest#extsloadInBatch.snap | 2 +- src/Fees.sol | 5 +- src/PausableRole.sol | 36 +++++ src/interfaces/IPausableRole.sol | 22 ++++ src/pool-cl/CLPoolManager.sol | 5 + src/pool-cl/interfaces/ICLPoolManager.sol | 2 + test/Extsload.t.sol | 17 ++- test/PausableRole.t.sol | 97 ++++++++++++++ test/pool-bin/BinPoolManager.t.sol | 2 +- test/pool-cl/CLPoolManager.t.sol | 123 ++++++++++++++++++ 56 files changed, 345 insertions(+), 58 deletions(-) create mode 100644 src/PausableRole.sol create mode 100644 src/interfaces/IPausableRole.sol create mode 100644 test/PausableRole.t.sol diff --git a/.forge-snapshots/BinHookTest#testBurnSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testBurnSucceedsWithHook.snap index dfc76374..e39570f1 100644 --- a/.forge-snapshots/BinHookTest#testBurnSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testBurnSucceedsWithHook.snap @@ -1 +1 @@ -143677 \ No newline at end of file +143699 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap index abc8dadc..fcf5c059 100644 --- a/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testDonateSucceedsWithHook.snap @@ -1 +1 @@ -134868 \ No newline at end of file +134890 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap index 87154c04..f155baf0 100644 --- a/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testInitializeSucceedsWithHook.snap @@ -1 +1 @@ -109021 \ No newline at end of file +109043 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap index 13f37b2e..dedd16e3 100644 --- a/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testMintSucceedsWithHook.snap @@ -1 +1 @@ -298905 \ No newline at end of file +298927 \ No newline at end of file diff --git a/.forge-snapshots/BinHookTest#testSwapSucceedsWithHook.snap b/.forge-snapshots/BinHookTest#testSwapSucceedsWithHook.snap index 9cf209a3..7a7c6ab5 100644 --- a/.forge-snapshots/BinHookTest#testSwapSucceedsWithHook.snap +++ b/.forge-snapshots/BinHookTest#testSwapSucceedsWithHook.snap @@ -1 +1 @@ -141089 \ No newline at end of file +141133 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testBurnNativeCurrency.snap b/.forge-snapshots/BinPoolManagerTest#testBurnNativeCurrency.snap index b6a0d9ab..e1c11457 100644 --- a/.forge-snapshots/BinPoolManagerTest#testBurnNativeCurrency.snap +++ b/.forge-snapshots/BinPoolManagerTest#testBurnNativeCurrency.snap @@ -1 +1 @@ -92245 \ No newline at end of file +92267 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testFuzz_SetMaxBinStep.snap b/.forge-snapshots/BinPoolManagerTest#testFuzz_SetMaxBinStep.snap index b7a8d940..3e7e9aaa 100644 --- a/.forge-snapshots/BinPoolManagerTest#testFuzz_SetMaxBinStep.snap +++ b/.forge-snapshots/BinPoolManagerTest#testFuzz_SetMaxBinStep.snap @@ -1 +1 @@ -9201 \ No newline at end of file +9179 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasBurnHalfBin.snap b/.forge-snapshots/BinPoolManagerTest#testGasBurnHalfBin.snap index e0a09995..5bb924f2 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasBurnHalfBin.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasBurnHalfBin.snap @@ -1 +1 @@ -67437 \ No newline at end of file +67459 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasBurnNineBins.snap b/.forge-snapshots/BinPoolManagerTest#testGasBurnNineBins.snap index d570894d..c0858253 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasBurnNineBins.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasBurnNineBins.snap @@ -1 +1 @@ -150992 \ No newline at end of file +151014 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap b/.forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap index 33434dff..f7e8f0d4 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap @@ -1 +1 @@ -68616 \ No newline at end of file +68638 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap b/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap index 760655c0..2a54c995 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasDonate.snap @@ -1 +1 @@ -53952 \ No newline at end of file +53974 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap index fcedea97..66abbd6e 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-1.snap @@ -1 +1 @@ -968047 \ No newline at end of file +968069 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap index 606a4052..1f192157 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintNneBins-2.snap @@ -1 +1 @@ -121561 \ No newline at end of file +121583 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap index 08f6d723..5bde9de4 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-1.snap @@ -1 +1 @@ -337237 \ No newline at end of file +337259 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap index 46204462..612b90fc 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasMintOneBin-2.snap @@ -1 +1 @@ -56298 \ No newline at end of file +56320 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap index 2013546b..18a4592a 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapMultipleBins.snap @@ -1 +1 @@ -93040 \ No newline at end of file +93084 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap index 0ac0f030..e9bae811 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapOverBigBinIdGate.snap @@ -1 +1 @@ -95025 \ No newline at end of file +95069 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap b/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap index 5752d2a0..70a7b26b 100644 --- a/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap +++ b/.forge-snapshots/BinPoolManagerTest#testGasSwapSingleBin.snap @@ -1 +1 @@ -74469 \ No newline at end of file +74513 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap b/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap index 4a71833f..26f291e2 100644 --- a/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap +++ b/.forge-snapshots/BinPoolManagerTest#testMintNativeCurrency.snap @@ -1 +1 @@ -318945 \ No newline at end of file +318967 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Burn.snap b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Burn.snap index 644edbae..05fa1e86 100644 --- a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Burn.snap +++ b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Burn.snap @@ -1 +1 @@ -41876 \ No newline at end of file +41898 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Donate.snap b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Donate.snap index f17589cd..976fd115 100644 --- a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Donate.snap +++ b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Donate.snap @@ -1 +1 @@ -19493 \ No newline at end of file +19515 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Initialize.snap b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Initialize.snap index 5e68eac6..68e93996 100644 --- a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Initialize.snap +++ b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Initialize.snap @@ -1 +1 @@ -37221 \ No newline at end of file +37243 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Mint.snap b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Mint.snap index 78d7f8e8..5dd23c23 100644 --- a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Mint.snap +++ b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Mint.snap @@ -1 +1 @@ -37345 \ No newline at end of file +37367 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Swap.snap b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Swap.snap index bbdca60d..158be14c 100644 --- a/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Swap.snap +++ b/.forge-snapshots/BinPoolManagerTest#testNoOpGas_Swap.snap @@ -1 +1 @@ -22699 \ No newline at end of file +22743 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testSetMasterChef.snap b/.forge-snapshots/BinPoolManagerTest#testSetMasterChef.snap index aaf501e7..b99a994a 100644 --- a/.forge-snapshots/BinPoolManagerTest#testSetMasterChef.snap +++ b/.forge-snapshots/BinPoolManagerTest#testSetMasterChef.snap @@ -1 +1 @@ -24243 \ No newline at end of file +24265 \ No newline at end of file diff --git a/.forge-snapshots/BinPoolManagerTest#testSetProtocolFee.snap b/.forge-snapshots/BinPoolManagerTest#testSetProtocolFee.snap index f4291507..f40efcf6 100644 --- a/.forge-snapshots/BinPoolManagerTest#testSetProtocolFee.snap +++ b/.forge-snapshots/BinPoolManagerTest#testSetProtocolFee.snap @@ -1 +1 @@ -8126 \ No newline at end of file +8148 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap index a3b6d98b..58094620 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromEmpty.snap @@ -1 +1 @@ -348452 \ No newline at end of file +350592 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap index 4bbcfa2c..eaa8d203 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_fromNonEmpty.snap @@ -1 +1 @@ -61000 \ No newline at end of file +61140 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap b/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap index 9d0018b5..0c339142 100644 --- a/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap +++ b/.forge-snapshots/CLPoolManagerTest#addLiquidity_nativeToken.snap @@ -1 +1 @@ -241390 \ No newline at end of file +243530 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap b/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap index ff632f9d..d791b879 100644 --- a/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap +++ b/.forge-snapshots/CLPoolManagerTest#donateBothTokens.snap @@ -1 +1 @@ -84138 \ No newline at end of file +84302 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap b/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap index ad879fda..531c812a 100644 --- a/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap +++ b/.forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap @@ -1 +1 @@ -53445 \ No newline at end of file +53609 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#initializeWithoutHooks.snap b/.forge-snapshots/CLPoolManagerTest#initializeWithoutHooks.snap index a118ebd3..c852e3e8 100644 --- a/.forge-snapshots/CLPoolManagerTest#initializeWithoutHooks.snap +++ b/.forge-snapshots/CLPoolManagerTest#initializeWithoutHooks.snap @@ -1 +1 @@ -36477 \ No newline at end of file +36544 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#removeLiquidity_toNonEmpty.snap b/.forge-snapshots/CLPoolManagerTest#removeLiquidity_toNonEmpty.snap index beda75cb..b8dc4e62 100644 --- a/.forge-snapshots/CLPoolManagerTest#removeLiquidity_toNonEmpty.snap +++ b/.forge-snapshots/CLPoolManagerTest#removeLiquidity_toNonEmpty.snap @@ -1 +1 @@ -43322 \ No newline at end of file +43462 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#setLmPool.snap b/.forge-snapshots/CLPoolManagerTest#setLmPool.snap index c1269d4e..39deca12 100644 --- a/.forge-snapshots/CLPoolManagerTest#setLmPool.snap +++ b/.forge-snapshots/CLPoolManagerTest#setLmPool.snap @@ -1 +1 @@ -26294 \ No newline at end of file +26272 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#setMasterChef.snap b/.forge-snapshots/CLPoolManagerTest#setMasterChef.snap index 19c0eed1..48345f5c 100644 --- a/.forge-snapshots/CLPoolManagerTest#setMasterChef.snap +++ b/.forge-snapshots/CLPoolManagerTest#setMasterChef.snap @@ -1 +1 @@ -30760 \ No newline at end of file +30715 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap b/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap index 81cd8b6b..03c2391a 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_againstLiquidity.snap @@ -1 +1 @@ -56445 \ No newline at end of file +56609 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap b/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap index cecfca2a..67181a11 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_leaveSurplusTokenInVault.snap @@ -1 +1 @@ -104543 \ No newline at end of file +104707 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap b/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap index 4475c1ca..d28d3b1b 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_runOutOfLiquidity.snap @@ -1 +1 @@ -25044269 \ No newline at end of file +25044433 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_simple.snap b/.forge-snapshots/CLPoolManagerTest#swap_simple.snap index 67c6dee8..9cf1240d 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_simple.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_simple.snap @@ -1 +1 @@ -36336 \ No newline at end of file +36500 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_useSurplusTokenAsInput.snap b/.forge-snapshots/CLPoolManagerTest#swap_useSurplusTokenAsInput.snap index ac44f3fb..d71a9747 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_useSurplusTokenAsInput.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_useSurplusTokenAsInput.snap @@ -1 +1 @@ -103041 \ No newline at end of file +103205 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_withHooks.snap b/.forge-snapshots/CLPoolManagerTest#swap_withHooks.snap index 0fd73b02..060443b8 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_withHooks.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_withHooks.snap @@ -1 +1 @@ -41986 \ No newline at end of file +42150 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#swap_withNative.snap b/.forge-snapshots/CLPoolManagerTest#swap_withNative.snap index 0aecb1ad..907cc5b2 100644 --- a/.forge-snapshots/CLPoolManagerTest#swap_withNative.snap +++ b/.forge-snapshots/CLPoolManagerTest#swap_withNative.snap @@ -1 +1 @@ -36339 \ No newline at end of file +36503 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Donate.snap b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Donate.snap index 7b7d760c..eda3bfd7 100644 --- a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Donate.snap +++ b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Donate.snap @@ -1 +1 @@ -19448 \ No newline at end of file +19612 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Initialize.snap b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Initialize.snap index 023fdc04..b6e10272 100644 --- a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Initialize.snap +++ b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Initialize.snap @@ -1 +1 @@ -37427 \ No newline at end of file +37494 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_ModifyPosition.snap b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_ModifyPosition.snap index 228723ef..9dbad8f8 100644 --- a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_ModifyPosition.snap +++ b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_ModifyPosition.snap @@ -1 +1 @@ -27615 \ No newline at end of file +29755 \ No newline at end of file diff --git a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap index 9e1e60bf..de51cdc1 100644 --- a/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap +++ b/.forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap @@ -1 +1 @@ -21897 \ No newline at end of file +22061 \ No newline at end of file diff --git a/.forge-snapshots/ExtsloadTest#extsloadInBatch.snap b/.forge-snapshots/ExtsloadTest#extsloadInBatch.snap index 4756eda2..6c07f02b 100644 --- a/.forge-snapshots/ExtsloadTest#extsloadInBatch.snap +++ b/.forge-snapshots/ExtsloadTest#extsloadInBatch.snap @@ -1 +1 @@ -11418 \ No newline at end of file +11352 \ No newline at end of file diff --git a/src/Fees.sol b/src/Fees.sol index 18ae2469..095dafba 100644 --- a/src/Fees.sol +++ b/src/Fees.sol @@ -2,15 +2,14 @@ // Copyright (C) 2024 PancakeSwap pragma solidity ^0.8.24; -import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; - +import {PausableRole} from "./PausableRole.sol"; import {Currency} from "./types/Currency.sol"; import {IProtocolFeeController} from "./interfaces/IProtocolFeeController.sol"; import {IFees} from "./interfaces/IFees.sol"; import {PoolKey} from "./types/PoolKey.sol"; import {IVault} from "./interfaces/IVault.sol"; -abstract contract Fees is IFees, Ownable { +abstract contract Fees is IFees, PausableRole { uint8 public constant MIN_PROTOCOL_FEE_DENOMINATOR = 4; mapping(Currency currency => uint256) public protocolFeesAccrued; diff --git a/src/PausableRole.sol b/src/PausableRole.sol new file mode 100644 index 00000000..d9ed433b --- /dev/null +++ b/src/PausableRole.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (C) 2024 PancakeSwap +pragma solidity ^0.8.24; + +import {IPausableRole} from "./interfaces/IPausableRole.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; +import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; + +/// @notice Allow owner and multiple accounts to pause but only owner can unpause +/// @dev Potentially allow security partners to programatically pause() +abstract contract PausableRole is IPausableRole, Ownable, Pausable { + mapping(address => bool) public hasPausableRole; + + modifier onlyPausableRoleOrOwner() { + if (msg.sender != owner() && !hasPausableRole[msg.sender]) revert NoPausableRole(); + _; + } + + function pause() public override onlyPausableRoleOrOwner { + _pause(); + } + + function unpause() public override onlyOwner { + _unpause(); + } + + function grantPausableRole(address account) public override onlyOwner { + hasPausableRole[account] = true; + emit PausableRoleGranted(account); + } + + function revokePausableRole(address account) public override onlyOwner { + hasPausableRole[account] = false; + emit PausableRoleRevoked(account); + } +} diff --git a/src/interfaces/IPausableRole.sol b/src/interfaces/IPausableRole.sol new file mode 100644 index 00000000..39b32220 --- /dev/null +++ b/src/interfaces/IPausableRole.sol @@ -0,0 +1,22 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +interface IPausableRole { + /// @notice Thrown when the caller does not have the pausable role or is not owner + error NoPausableRole(); + + event PausableRoleGranted(address indexed account); + event PausableRoleRevoked(address indexed account); + + /// @notice Pause the contract, called by the owner or an account with the pausable role + function pause() external; + + /// @notice Unpause the contract, called by the owner + function unpause() external; + + /// @notice Grant the pausable role to an account, called by the owner + function grantPausableRole(address account) external; + + /// @notice Revoke the pausable role to an account, called by the owner + function revokePausableRole(address account) external; +} diff --git a/src/pool-cl/CLPoolManager.sol b/src/pool-cl/CLPoolManager.sol index 5b2324d3..49fd0f60 100644 --- a/src/pool-cl/CLPoolManager.sol +++ b/src/pool-cl/CLPoolManager.sol @@ -137,6 +137,9 @@ contract CLPoolManager is ICLPoolManager, Fees, Extsload { ICLPoolManager.ModifyLiquidityParams memory params, bytes calldata hookData ) external override poolManagerMatch(address(key.poolManager)) returns (BalanceDelta delta) { + // Do not allow add liquidity when paused() + if (paused() && params.liquidityDelta > 0) revert PoolPaused(); + PoolId id = key.toId(); _checkPoolInitialized(id); @@ -196,6 +199,7 @@ contract CLPoolManager is ICLPoolManager, Fees, Extsload { external override poolManagerMatch(address(key.poolManager)) + whenNotPaused returns (BalanceDelta delta) { PoolId id = key.toId(); @@ -258,6 +262,7 @@ contract CLPoolManager is ICLPoolManager, Fees, Extsload { external override poolManagerMatch(address(key.poolManager)) + whenNotPaused returns (BalanceDelta delta) { PoolId id = key.toId(); diff --git a/src/pool-cl/interfaces/ICLPoolManager.sol b/src/pool-cl/interfaces/ICLPoolManager.sol index 42fdb41a..59ae6036 100644 --- a/src/pool-cl/interfaces/ICLPoolManager.sol +++ b/src/pool-cl/interfaces/ICLPoolManager.sol @@ -21,6 +21,8 @@ interface ICLPoolManager is IFees, IPoolManager, IExtsload { error TickSpacingTooSmall(); /// @notice Error thrown when Unauthorized caller error UnauthorizedCaller(); + /// @notice Error thrown when add liquidity is called when paused() + error PoolPaused(); /// @notice Emitted when a new pool is initialized /// @param id The abi encoded hash of the pool key struct for the new pool diff --git a/test/Extsload.t.sol b/test/Extsload.t.sol index 45bd7b63..87074fb0 100644 --- a/test/Extsload.t.sol +++ b/test/Extsload.t.sol @@ -11,10 +11,11 @@ import {IProtocolFeeController} from "../src/interfaces/IProtocolFeeController.s contract ExtsloadTest is Test, GasSnapshot { // Slot - // 0 PoolManager#Ownable#_owner - // 1 PooAlManager#Fees#protocolFeesAccrued - // 2 PooAlManager#Fees#protocolFeeController - // 3 PooAlManager#pools + // 0 PoolManager#PausableRole#Pausable#_paused and PooAlManager#PausableRole#Ownable#_owner + // 1 PoolManager#PausableRole#hasPausableRole + // 2 PooAlManager#Fees#protocolFeesAccrued + // 3 PooAlManager#Fees#protocolFeeController + // 4 PooAlManager#pools ICLPoolManager poolManager; function setUp() public { @@ -25,19 +26,21 @@ contract ExtsloadTest is Test, GasSnapshot { } function testExtsload() public { + // as contract is not paused, slot0 is 0x0...0_owner_address, + // if paused, slot0 is 0x0...1_owner_address snapStart("ExtsloadTest#extsload"); bytes32 slot0 = poolManager.extsload(0x00); snapEnd(); assertEq(abi.encode(slot0), abi.encode(address(this))); - bytes32 slot2 = poolManager.extsload(bytes32(uint256(0x02))); - assertEq(abi.encode(slot2), abi.encode(address(0xabcd))); + bytes32 slot3 = poolManager.extsload(bytes32(uint256(0x03))); + assertEq(abi.encode(slot3), abi.encode(address(0xabcd))); } function testExtsloadInBatch() public { bytes32[] memory slots = new bytes32[](2); slots[0] = 0x00; - slots[1] = bytes32(uint256(0x02)); + slots[1] = bytes32(uint256(0x03)); snapStart("ExtsloadTest#extsloadInBatch"); slots = poolManager.extsload(slots); snapEnd(); diff --git a/test/PausableRole.t.sol b/test/PausableRole.t.sol new file mode 100644 index 00000000..0ca8b545 --- /dev/null +++ b/test/PausableRole.t.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +import {GasSnapshot} from "forge-gas-snapshot/GasSnapshot.sol"; +import "forge-std/Test.sol"; + +import {PausableRole} from "../src/PausableRole.sol"; +import {IPausableRole} from "../src/interfaces/IPausableRole.sol"; + +contract MockPausableRole is PausableRole {} + +contract PausableRoleTest is Test, GasSnapshot { + event PausableRoleGranted(address indexed account); + event PausableRoleRevoked(address indexed account); + + MockPausableRole public mockPausableRole; + address alice = makeAddr("alice"); + + function setUp() public { + mockPausableRole = new MockPausableRole(); + } + + function testGrantPausableRole_OnlyOwner() public { + vm.expectEmit(); + emit PausableRoleGranted(alice); + + mockPausableRole.grantPausableRole(alice); + assertEq(mockPausableRole.hasPausableRole(alice), true); + } + + function testGrantPausableRole_NotOwner() public { + vm.prank(alice); + + vm.expectRevert(); + mockPausableRole.grantPausableRole(alice); + } + + function testRevokePausableRole() public { + // pre-req: grant + mockPausableRole.grantPausableRole(alice); + assertEq(mockPausableRole.hasPausableRole(alice), true); + + vm.expectEmit(); + emit PausableRoleRevoked(alice); + + // revoke + mockPausableRole.revokePausableRole(alice); + assertEq(mockPausableRole.hasPausableRole(alice), false); + } + + function testRevokePausableRole_NotOwner() public { + vm.prank(alice); + + vm.expectRevert(); + mockPausableRole.revokePausableRole(alice); + } + + function testPause_Owner() public { + mockPausableRole.pause(); + assertEq(mockPausableRole.paused(), true); + } + + function testPause_PausableRole() public { + vm.expectRevert(IPausableRole.NoPausableRole.selector); + vm.prank(alice); + mockPausableRole.pause(); + + // owner grant alice permission + mockPausableRole.grantPausableRole(alice); + + // alice pause again + vm.prank(alice); + mockPausableRole.pause(); + assertEq(mockPausableRole.paused(), true); + } + + function testUnpause_OnlyOwner() public { + // pre-req: pause + mockPausableRole.pause(); + assertEq(mockPausableRole.paused(), true); + + // unpause + mockPausableRole.unpause(); + assertEq(mockPausableRole.paused(), false); + } + + function testUnpause_NotOwner() public { + // pre-req: pause + mockPausableRole.pause(); + assertEq(mockPausableRole.paused(), true); + + // unpause + vm.expectRevert(); + vm.prank(alice); + mockPausableRole.unpause(); + } +} diff --git a/test/pool-bin/BinPoolManager.t.sol b/test/pool-bin/BinPoolManager.t.sol index 7a3c1660..15e3a6e4 100644 --- a/test/pool-bin/BinPoolManager.t.sol +++ b/test/pool-bin/BinPoolManager.t.sol @@ -635,7 +635,7 @@ contract BinPoolManagerTest is Test, GasSnapshot, BinTestHelper { poolManager.initialize(key, activeId, new bytes(0)); // verify poolId. - uint256 POOL_SLOT = 3; + uint256 POOL_SLOT = 4; snapStart("BinPoolManagerTest#testExtLoadPoolActiveId"); bytes32 slot0Bytes = poolManager.extsload(keccak256(abi.encode(key.toId(), POOL_SLOT))); snapEnd(); diff --git a/test/pool-cl/CLPoolManager.t.sol b/test/pool-cl/CLPoolManager.t.sol index 73be8265..f76f7284 100644 --- a/test/pool-cl/CLPoolManager.t.sol +++ b/test/pool-cl/CLPoolManager.t.sol @@ -2521,6 +2521,129 @@ contract CLPoolManagerTest is Test, Deployers, TokenFixture, GasSnapshot { snapEnd(); } + function testModifyLiquidity_Add_WhenPaused() public { + Currency currency0 = Currency.wrap(address(new ERC20PresetFixedSupply("C0", "C0", 1e10 ether, address(this)))); + Currency currency1 = Currency.wrap(address(new ERC20PresetFixedSupply("C1", "C1", 1e10 ether, address(this)))); + if (currency0 > currency1) { + (currency0, currency1) = (currency1, currency0); + } + + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + parameters: bytes32(uint256(0x10000)) + }); + + poolManager.initialize(key, SQRT_RATIO_1_1, new bytes(0)); + IERC20(Currency.unwrap(currency0)).approve(address(router), 1e10 ether); + IERC20(Currency.unwrap(currency1)).approve(address(router), 1e10 ether); + + // pause + poolManager.pause(); + + vm.expectRevert(ICLPoolManager.PoolPaused.selector); + router.modifyPosition( + key, + ICLPoolManager.ModifyLiquidityParams({ + tickLower: TickMath.MIN_TICK, + tickUpper: TickMath.MAX_TICK, + liquidityDelta: 1e24 + }), + "" + ); + } + + function testModifyLiquidity_Remove_WhenPaused() public { + Currency currency0 = Currency.wrap(address(new ERC20PresetFixedSupply("C0", "C0", 1e10 ether, address(this)))); + Currency currency1 = Currency.wrap(address(new ERC20PresetFixedSupply("C1", "C1", 1e10 ether, address(this)))); + if (currency0 > currency1) { + (currency0, currency1) = (currency1, currency0); + } + + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + parameters: bytes32(uint256(0x10000)) + }); + + poolManager.initialize(key, SQRT_RATIO_1_1, new bytes(0)); + IERC20(Currency.unwrap(currency0)).approve(address(router), 1e10 ether); + IERC20(Currency.unwrap(currency1)).approve(address(router), 1e10 ether); + + // pre-req add liquidity + router.modifyPosition( + key, ICLPoolManager.ModifyLiquidityParams({tickLower: -120, tickUpper: 120, liquidityDelta: 1e24}), "" + ); + + // pause + poolManager.pause(); + + // verify no revert + router.modifyPosition( + key, ICLPoolManager.ModifyLiquidityParams({tickLower: -120, tickUpper: 120, liquidityDelta: -1e24}), "" + ); + } + + function testSwap_WhenPaused() public { + Currency currency0 = Currency.wrap(address(new ERC20PresetFixedSupply("C0", "C0", 1e10 ether, address(this)))); + Currency currency1 = Currency.wrap(address(new ERC20PresetFixedSupply("C1", "C1", 1e10 ether, address(this)))); + if (currency0 > currency1) { + (currency0, currency1) = (currency1, currency0); + } + + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + hooks: IHooks(address(0)), + poolManager: poolManager, + fee: uint24(3000), + parameters: bytes32(uint256(0x10000)) + }); + + poolManager.initialize(key, SQRT_RATIO_1_1, new bytes(0)); + IERC20(Currency.unwrap(currency0)).approve(address(router), 1e10 ether); + IERC20(Currency.unwrap(currency1)).approve(address(router), 1e10 ether); + + // pause + poolManager.pause(); + + vm.expectRevert("Pausable: paused"); + router.swap( + key, + ICLPoolManager.SwapParams({ + zeroForOne: true, + amountSpecified: 0.1 ether, + sqrtPriceLimitX96: TickMath.MIN_SQRT_RATIO + 1 + }), + CLPoolManagerRouter.SwapTestSettings({withdrawTokens: true, settleUsingTransfer: true}), + "" + ); + } + + function testDonate_WhenPaused() public { + PoolKey memory key = PoolKey({ + currency0: currency0, + currency1: currency1, + fee: 100, + hooks: IHooks(address(0)), + poolManager: poolManager, + parameters: bytes32(uint256(10 << 16)) + }); + poolManager.initialize(key, SQRT_RATIO_1_1, ZERO_BYTES); + + // pause + poolManager.pause(); + + vm.expectRevert("Pausable: paused"); + router.donate(key, 100, 200, ZERO_BYTES); + } + function _validateHookConfig(PoolKey memory poolKey) internal view returns (bool) { uint16 bitmapInParameters = poolKey.parameters.getHooksRegistrationBitmap(); if (address(poolKey.hooks) == address(0)) {