Skip to content

Commit 65d409f

Browse files
authored
feat: update MultiPause contract (#32)
Compared to the previous implementation: - works with new governance framework - actually pauses all contracts in a suite instead of just pools/facades - removed `unpause...` functions as they were meaningless - added metadata to be compatible with BCR
1 parent 4384741 commit 65d409f

File tree

3 files changed

+100
-180
lines changed

3 files changed

+100
-180
lines changed

contracts/emergency/MultiPause.sol

Lines changed: 88 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,120 @@
11
// SPDX-License-Identifier: BUSL-1.1
22
// Gearbox Protocol. Generalized leverage for DeFi protocols
3-
// (c) Gearbox Foundation, 2024.
4-
pragma solidity ^0.8.17;
3+
// (c) Gearbox Foundation, 2025.
4+
pragma solidity ^0.8.23;
55

6-
import {ICreditManagerV3} from "@gearbox-protocol/core-v3/contracts/interfaces/ICreditManagerV3.sol";
7-
import {IContractsRegister} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IContractsRegister.sol";
6+
import {IStateSerializer} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IStateSerializer.sol";
7+
import {IVersion} from "@gearbox-protocol/core-v3/contracts/interfaces/base/IVersion.sol";
88
import {ACLTrait} from "@gearbox-protocol/core-v3/contracts/traits/ACLTrait.sol";
99
import {ContractsRegisterTrait} from "@gearbox-protocol/core-v3/contracts/traits/ContractsRegisterTrait.sol";
10-
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
1110

12-
enum PausableAction {
13-
Pause,
14-
Unpause
15-
}
16-
17-
interface PausableContract {
18-
/// @notice Pauses contract, can only be called by an account with pausable admin role
19-
/// @dev Reverts if contract is already paused
20-
function pause() external;
11+
import {IContractsRegister} from "@gearbox-protocol/permissionless/contracts/interfaces/IContractsRegister.sol";
12+
import {IMarketConfigurator} from "@gearbox-protocol/permissionless/contracts/interfaces/IMarketConfigurator.sol";
13+
import {MarketFactories} from "@gearbox-protocol/permissionless/contracts/interfaces/Types.sol";
2114

22-
/// @notice Unpauses contract, can only be called by an account with unpausable admin role
23-
/// @dev Reverts if contract is already unpaused
24-
function unpause() external;
25-
}
15+
import {IPausable} from "../interfaces/IPausable.sol";
2616

2717
/// @title MultiPause
2818
/// @author Gearbox Foundation
29-
/// @notice Allows pausable admins to pause multiple contracts in a single transaction
30-
/// @dev This contract is expected to be one of pausable admins in the ACL contract
31-
contract MultiPause is ACLTrait, ContractsRegisterTrait {
32-
constructor(address acl_, address contractsRegister_) ACLTrait(acl_) ContractsRegisterTrait(contractsRegister_) {}
19+
/// @notice Allows pausable admins to pause multiple contracts via single call.
20+
/// This contract itself is expected to have the `PAUSABLE_ADMIN` role in the ACL contract.
21+
contract MultiPause is IVersion, IStateSerializer, ACLTrait, ContractsRegisterTrait {
22+
/// @notice Contract type
23+
bytes32 public constant override contractType = "MULTI_PAUSE";
24+
25+
/// @notice Contract version
26+
uint256 public constant override version = 3_10;
27+
28+
/// @notice Market configurator address
29+
address public immutable marketConfigurator;
30+
31+
/// @notice Constructor
32+
/// @param marketConfigurator_ Market configurator address
33+
constructor(address marketConfigurator_)
34+
ACLTrait(IMarketConfigurator(marketConfigurator_).acl())
35+
ContractsRegisterTrait(IMarketConfigurator(marketConfigurator_).contractsRegister())
36+
{
37+
marketConfigurator = marketConfigurator_;
38+
}
39+
40+
/// @notice Empty state serialization
41+
function serialize() external pure override returns (bytes memory) {}
3342

34-
/// @notice Pauses contracts from the given list
35-
/// @dev Ignores contracts that are already paused
43+
/// @notice Pauses given contracts
3644
/// @dev Reverts if caller is not a pausable admin
37-
function pauseContracts(address[] memory contracts) external pausableAdminsOnly {
38-
_pauseContracts(contracts, PausableAction.Pause);
45+
function pauseContracts(address[] calldata contracts) external pausableAdminsOnly {
46+
uint256 numContracts = contracts.length;
47+
for (uint256 i; i < numContracts; ++i) {
48+
_pause(contracts[i]);
49+
}
3950
}
4051

41-
/// @notice Pauses all registered pools
42-
/// @dev Ignores contracts that are already paused
52+
/// @notice Pauses all contracts within all registered markets and credit suites
4353
/// @dev Reverts if caller is not a pausable admin
44-
function pauseAllPools() external pausableAdminsOnly {
45-
_pauseAllPools(PausableAction.Pause);
54+
function pauseAllContracts() external pausableAdminsOnly {
55+
address[] memory pools = IContractsRegister(contractsRegister).getPools();
56+
uint256 numPools = pools.length;
57+
for (uint256 i; i < numPools; ++i) {
58+
_pauseMarket(pools[i]);
59+
}
4660
}
4761

48-
/// @notice Pauses all registered credit managers
49-
/// @dev For V3, credit facades are paused instead
50-
/// @dev Ignores contracts that are already paused
62+
/// @notice Pauses all contracts within a given market and connected credit suites
5163
/// @dev Reverts if caller is not a pausable admin
52-
function pauseAllCreditManagers() external pausableAdminsOnly {
53-
_pauseAllCreditManagers(PausableAction.Pause);
64+
/// @dev Reverts if `pool` is not registered
65+
function pauseMarket(address pool) external pausableAdminsOnly registeredPoolOnly(pool) {
66+
_pauseMarket(pool);
5467
}
5568

56-
/// @notice Pauses all registered credit managers and pools
57-
/// @dev Ignores contracts that are already paused
69+
/// @notice Pauses all contracts within a given credit suite
5870
/// @dev Reverts if caller is not a pausable admin
59-
function pauseAllContracts() external pausableAdminsOnly {
60-
_pauseAllPools(PausableAction.Pause);
61-
_pauseAllCreditManagers(PausableAction.Pause);
71+
/// @dev Reverts if `creditManager` is not registered
72+
function pauseCreditSuite(address creditManager)
73+
external
74+
pausableAdminsOnly
75+
registeredCreditManagerOnly(creditManager)
76+
{
77+
_pauseCreditSuite(creditManager);
6278
}
6379

64-
/// @notice Unpauses all registered credit managers and pools
65-
/// @dev Ignores contracts that aren't paused
66-
/// @dev Reverts if caller is not a unpausable admin
67-
function unpauseAllContracts() external unpausableAdminsOnly {
68-
_pauseAllPools(PausableAction.Unpause);
69-
_pauseAllCreditManagers(PausableAction.Unpause);
80+
// --------- //
81+
// INTERNALS //
82+
// --------- //
83+
84+
/// @dev Pauses all contracts within a given market and connected credit suites
85+
function _pauseMarket(address pool) internal {
86+
MarketFactories memory factories = IMarketConfigurator(marketConfigurator).getMarketFactories(pool);
87+
_pauseFactoryTargets(factories.poolFactory, pool);
88+
_pauseFactoryTargets(factories.priceOracleFactory, pool);
89+
_pauseFactoryTargets(factories.interestRateModelFactory, pool);
90+
_pauseFactoryTargets(factories.rateKeeperFactory, pool);
91+
_pauseFactoryTargets(factories.lossPolicyFactory, pool);
92+
address[] memory creditManagers = IContractsRegister(contractsRegister).getCreditManagers(pool);
93+
uint256 numCreditManagers = creditManagers.length;
94+
for (uint256 i; i < numCreditManagers; ++i) {
95+
_pauseCreditSuite(creditManagers[i]);
96+
}
7097
}
7198

72-
/// @dev Internal function to pause all pools
73-
function _pauseAllPools(PausableAction action) internal {
74-
_pauseContracts(IContractsRegister(contractsRegister).getPools(), action);
99+
/// @dev Pauses all contracts within a given credit suite
100+
function _pauseCreditSuite(address creditManager) internal {
101+
address factory = IMarketConfigurator(marketConfigurator).getCreditFactory(creditManager);
102+
_pauseFactoryTargets(factory, creditManager);
75103
}
76104

77-
/// @dev Internal function to pause all credit managers
78-
function _pauseAllCreditManagers(PausableAction action) internal {
79-
address[] memory contracts = IContractsRegister(contractsRegister).getCreditManagers();
80-
uint256 len = contracts.length;
81-
unchecked {
82-
for (uint256 i; i < len; ++i) {
83-
if (ICreditManagerV3(contracts[i]).version() < 3_00) continue;
84-
contracts[i] = ICreditManagerV3(contracts[i]).creditFacade();
85-
}
105+
/// @dev Pauses all contracts configurable by `factory` in a given market or credit `suite`
106+
function _pauseFactoryTargets(address factory, address suite) internal {
107+
address[] memory targets = IMarketConfigurator(marketConfigurator).getFactoryTargets(factory, suite);
108+
uint256 numTargets = targets.length;
109+
for (uint256 i; i < numTargets; ++i) {
110+
_pause(targets[i]);
86111
}
87-
_pauseContracts(contracts, action);
88112
}
89113

90-
/// @dev Internal function to pause/unpause contracts from the given list
91-
function _pauseContracts(address[] memory contracts, PausableAction action) internal {
92-
uint256 len = contracts.length;
93-
unchecked {
94-
for (uint256 i; i < len; ++i) {
95-
if (action == PausableAction.Pause) {
96-
if (Pausable(contracts[i]).paused()) continue;
97-
PausableContract(contracts[i]).pause();
98-
} else {
99-
if (!Pausable(contracts[i]).paused()) continue;
100-
PausableContract(contracts[i]).unpause();
101-
}
102-
}
103-
}
114+
/// @dev Pauses `target` if it's pausable and not already paused
115+
function _pause(address target) internal {
116+
try IPausable(target).paused() returns (bool paused) {
117+
if (!paused) try IPausable(target).pause{gas: 30_000}() {} catch {}
118+
} catch {}
104119
}
105120
}

contracts/interfaces/IPausable.sol

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// SPDX-License-Identifier: MIT
2+
// Gearbox Protocol. Generalized leverage for DeFi protocols
3+
// (c) Gearbox Foundation, 2025.
4+
pragma solidity ^0.8.23;
5+
6+
/// @title Pausable interface
7+
/// @notice Generic interface of a contract that can be paused and unpaused
8+
interface IPausable {
9+
function paused() external view returns (bool);
10+
function pause() external;
11+
function unpause() external;
12+
}

contracts/test/MultiPause.t.sol

Lines changed: 0 additions & 107 deletions
This file was deleted.

0 commit comments

Comments
 (0)