mirror of
https://github.com/vacp2p/linea-monorepo.git
synced 2026-01-09 04:08:01 -05:00
[Feat] Pause cooldown (#723)
* changes to pausemanager * add space * working pausemanager tests * npx hardhat test working for existing suite * more pausemanager tests * more tests and comments * minor typo fix * revert pauseTypes.ts changes * fix PauseManager test cases * small reverts * more test adjustments * Update contracts/src/security/pausing/PauseManager.sol Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com> Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> * Update contracts/src/security/pausing/interfaces/IPauseManager.sol Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com> Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> * Update contracts/src/security/pausing/PauseManager.sol Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com> Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> * Update contracts/src/security/pausing/PauseManager.sol Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com> Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> * fix unchecked * Update contracts/src/security/pausing/PauseManager.sol Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com> Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> * Update contracts/src/security/pausing/PauseManager.sol Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com> Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> * Update contracts/src/security/pausing/PauseManager.sol Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com> Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> * more comment fixes * match interface and contract natspec comments * add *.mdx changes * change pauseExpiry to pauseExpiryTimestamp * doc change * tests passing with new pause expiry value after security council pause * add new overflow test to pausemanager * expand unchecked block * indent unchecked block * unPauseDueToExpiry -> unPauseByExpiredType * Update contracts/src/security/pausing/PauseManager.sol Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com> Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> * added unpausebytype comment --------- Signed-off-by: kyzooghost <73516204+kyzooghost@users.noreply.github.com> Co-authored-by: The Dark Jester <thedarkjester@users.noreply.github.com>
This commit is contained in:
@@ -18,14 +18,6 @@ bytes32 SET_MESSAGE_SERVICE_ROLE
|
||||
|
||||
Role used for setting the message service address.
|
||||
|
||||
### SET_REMOTE_TOKENBRIDGE_ROLE
|
||||
|
||||
```solidity
|
||||
bytes32 SET_REMOTE_TOKENBRIDGE_ROLE
|
||||
```
|
||||
|
||||
Role used for setting the remote token bridge address.
|
||||
|
||||
### SET_RESERVED_TOKEN_ROLE
|
||||
|
||||
```solidity
|
||||
@@ -204,25 +196,6 @@ _Contract will be used as proxy implementation._
|
||||
| ---- | ---- | ----------- |
|
||||
| _initializationData | struct ITokenBridge.InitializationData | The initial data used for initializing the TokenBridge contract. |
|
||||
|
||||
### reinitializePauseTypesAndPermissions
|
||||
|
||||
```solidity
|
||||
function reinitializePauseTypesAndPermissions(address _defaultAdmin, struct IPermissionsManager.RoleAddress[] _roleAddresses, struct IPauseManager.PauseTypeRole[] _pauseTypeRoles, struct IPauseManager.PauseTypeRole[] _unpauseTypeRoles) external
|
||||
```
|
||||
|
||||
Sets permissions for a list of addresses and their roles as well as initialises the PauseManager pauseType:role mappings.
|
||||
|
||||
_This function is a reinitializer and can only be called once per version. Should be called using an upgradeAndCall transaction to the ProxyAdmin._
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| _defaultAdmin | address | The default admin account's address. |
|
||||
| _roleAddresses | struct IPermissionsManager.RoleAddress[] | The list of addresses and roles to assign permissions to. |
|
||||
| _pauseTypeRoles | struct IPauseManager.PauseTypeRole[] | The list of pause types to associate with roles. |
|
||||
| _unpauseTypeRoles | struct IPauseManager.PauseTypeRole[] | The list of unpause types to associate with roles. |
|
||||
|
||||
### bridgeToken
|
||||
|
||||
```solidity
|
||||
@@ -345,21 +318,6 @@ _Change the status of tokens to DEPLOYED. New bridge transaction will not
|
||||
| ---- | ---- | ----------- |
|
||||
| _nativeTokens | address[] | Array of native tokens for which the DEPLOYED status must be set. |
|
||||
|
||||
### setRemoteTokenBridge
|
||||
|
||||
```solidity
|
||||
function setRemoteTokenBridge(address _remoteTokenBridge) external
|
||||
```
|
||||
|
||||
_Sets the address of the remote token bridge. Can only be called once.
|
||||
SET_REMOTE_TOKENBRIDGE_ROLE is required to execute._
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| _remoteTokenBridge | address | The address of the remote token bridge to be set. |
|
||||
|
||||
### deployBridgedToken
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -9,6 +9,7 @@ struct InitializationData {
|
||||
address tokenBeacon;
|
||||
uint256 sourceChainId;
|
||||
uint256 targetChainId;
|
||||
address remoteSender;
|
||||
address[] reservedTokens;
|
||||
struct IPermissionsManager.RoleAddress[] roleAddresses;
|
||||
struct IPauseManager.PauseTypeRole[] pauseTypeRoles;
|
||||
@@ -455,20 +456,6 @@ _Linea can reserve tokens. In this case, the token cannot be bridged.
|
||||
| ---- | ---- | ----------- |
|
||||
| _token | address | The address of the token to be set as reserved. |
|
||||
|
||||
### setRemoteTokenBridge
|
||||
|
||||
```solidity
|
||||
function setRemoteTokenBridge(address _remoteTokenBridge) external
|
||||
```
|
||||
|
||||
_Sets the address of the remote token bridge. Can only be called once._
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| _remoteTokenBridge | address | The address of the remote token bridge to be set. |
|
||||
|
||||
### removeReserved
|
||||
|
||||
```solidity
|
||||
|
||||
@@ -16,12 +16,45 @@ bytes32 UNPAUSE_ALL_ROLE
|
||||
|
||||
This is used to unpause all unpausable functions.
|
||||
|
||||
### pauseTypeStatuses
|
||||
### SECURITY_COUNCIL_ROLE
|
||||
|
||||
```solidity
|
||||
mapping(bytes32 => bool) pauseTypeStatuses
|
||||
bytes32 SECURITY_COUNCIL_ROLE
|
||||
```
|
||||
|
||||
Role assigned to the security council that enables indefinite pausing and bypassing the cooldown period.
|
||||
|
||||
_Is not a pause or unpause role; a specific pause/unpause role is still required for specific pause/unpause types._
|
||||
|
||||
### PAUSE_DURATION
|
||||
|
||||
```solidity
|
||||
uint256 PAUSE_DURATION
|
||||
```
|
||||
|
||||
Duration of pauses, after which pauses will expire (except by the SECURITY_COUNCIL_ROLE).
|
||||
|
||||
### COOLDOWN_DURATION
|
||||
|
||||
```solidity
|
||||
uint256 COOLDOWN_DURATION
|
||||
```
|
||||
|
||||
Duration of cooldown after a pause expires, during which no pauses (except by the SECURITY_COUNCIL_ROLE) can be enacted.
|
||||
|
||||
_This prevents indefinite pause chaining by a non-SECURITY_COUNCIL_ROLE._
|
||||
|
||||
### pauseExpiryTimestamp
|
||||
|
||||
```solidity
|
||||
uint256 pauseExpiryTimestamp
|
||||
```
|
||||
|
||||
Unix timestamp of pause expiry.
|
||||
|
||||
_pauseExpiryTimestamp applies to all pause types. Pausing with one pause type blocks other pause types from being enacted (unless the SECURITY_COUNCIL_ROLE is used).
|
||||
This prevents indefinite pause chaining by a non-SECURITY_COUNCIL_ROLE._
|
||||
|
||||
### onlyUsedPausedTypes
|
||||
|
||||
```solidity
|
||||
@@ -120,7 +153,9 @@ function pauseByType(enum IPauseManager.PauseType _pauseType) external
|
||||
Pauses functionality by specific type.
|
||||
|
||||
_Throws if UNUSED pause type is used.
|
||||
Requires the role mapped in `_pauseTypeRoles` for the pauseType._
|
||||
Requires the role mapped in `_pauseTypeRoles` for the pauseType.
|
||||
Non-SECURITY_COUNCIL_ROLE can only pause after cooldown has passed.
|
||||
SECURITY_COUNCIL_ROLE can pause without cooldown or expiry restrictions._
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -137,7 +172,25 @@ function unPauseByType(enum IPauseManager.PauseType _pauseType) external
|
||||
Unpauses functionality by specific type.
|
||||
|
||||
_Throws if UNUSED pause type is used.
|
||||
Requires the role mapped in `_unPauseTypeRoles` for the pauseType._
|
||||
Requires the role mapped in `_unPauseTypeRoles` for the pauseType.
|
||||
SECURITY_COUNCIL_ROLE unpause will reset the cooldown, enabling non-SECURITY_COUNCIL_ROLE pausing._
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| _pauseType | enum IPauseManager.PauseType | The pause type value. |
|
||||
|
||||
### unPauseByExpiredType
|
||||
|
||||
```solidity
|
||||
function unPauseByExpiredType(enum IPauseManager.PauseType _pauseType) external
|
||||
```
|
||||
|
||||
Unpauses a specific pause type when the pause has expired.
|
||||
|
||||
_Can be called by anyone.
|
||||
Throws if UNUSED pause type is used, or the pause expiry period has not passed._
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -175,7 +228,7 @@ Update the pause type role mapping.
|
||||
|
||||
_Throws if UNUSED pause type is used.
|
||||
Throws if role not different.
|
||||
PAUSE_ALL_ROLE role is required to execute this function._
|
||||
SECURITY_COUNCIL_ROLE role is required to execute this function._
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -194,7 +247,7 @@ Update the unpause type role mapping.
|
||||
|
||||
_Throws if UNUSED pause type is used.
|
||||
Throws if role not different.
|
||||
UNPAUSE_ALL_ROLE role is required to execute this function._
|
||||
SECURITY_COUNCIL_ROLE role is required to execute this function._
|
||||
|
||||
#### Parameters
|
||||
|
||||
|
||||
@@ -55,6 +55,20 @@ Emitted when a pause type is unpaused.
|
||||
| messageSender | address | The address performing the unpause. |
|
||||
| pauseType | enum IPauseManager.PauseType | The indexed pause type that was unpaused. |
|
||||
|
||||
### UnPausedDueToExpiry
|
||||
|
||||
```solidity
|
||||
event UnPausedDueToExpiry(enum IPauseManager.PauseType pauseType)
|
||||
```
|
||||
|
||||
Emitted when a pause type is unpaused due to pause expiry passing.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| pauseType | enum IPauseManager.PauseType | The pause type that was unpaused. |
|
||||
|
||||
### PauseTypeRoleSet
|
||||
|
||||
```solidity
|
||||
@@ -125,6 +139,14 @@ error IsPaused(enum IPauseManager.PauseType pauseType)
|
||||
|
||||
_Thrown when a specific pause type is paused._
|
||||
|
||||
### PauseNotExpired
|
||||
|
||||
```solidity
|
||||
error PauseNotExpired(uint256 expiryEnd)
|
||||
```
|
||||
|
||||
_Thrown when unpauseDueToExpiry is attempted before a pause has expired._
|
||||
|
||||
### IsNotPaused
|
||||
|
||||
```solidity
|
||||
@@ -133,6 +155,14 @@ error IsNotPaused(enum IPauseManager.PauseType pauseType)
|
||||
|
||||
_Thrown when a specific pause type is not paused and expected to be._
|
||||
|
||||
### PauseUnavailableDueToCooldown
|
||||
|
||||
```solidity
|
||||
error PauseUnavailableDueToCooldown(uint256 cooldownEnd)
|
||||
```
|
||||
|
||||
_Thrown when pausing is attempted during the cooldown period by a non-SECURITY_COUNCIL_ROLE._
|
||||
|
||||
### PauseTypeNotUsed
|
||||
|
||||
```solidity
|
||||
@@ -158,7 +188,9 @@ function pauseByType(enum IPauseManager.PauseType _pauseType) external
|
||||
Pauses functionality by specific type.
|
||||
|
||||
_Throws if UNUSED pause type is used.
|
||||
Requires the role mapped in pauseTypeRoles for the pauseType._
|
||||
Requires the role mapped in `_pauseTypeRoles` for the pauseType.
|
||||
Non-SECURITY_COUNCIL_ROLE can only pause after cooldown has passed.
|
||||
SECURITY_COUNCIL_ROLE can pause without cooldown or expiry restrictions._
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -175,7 +207,25 @@ function unPauseByType(enum IPauseManager.PauseType _pauseType) external
|
||||
Unpauses functionality by specific type.
|
||||
|
||||
_Throws if UNUSED pause type is used.
|
||||
Requires the role mapped in unPauseTypeRoles for the pauseType._
|
||||
Requires the role mapped in `_unPauseTypeRoles` for the pauseType.
|
||||
SECURITY_COUNCIL_ROLE unpause will reset the cooldown, enabling non-SECURITY_COUNCIL_ROLE pausing._
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| _pauseType | enum IPauseManager.PauseType | The pause type value. |
|
||||
|
||||
### unPauseByExpiredType
|
||||
|
||||
```solidity
|
||||
function unPauseByExpiredType(enum IPauseManager.PauseType _pauseType) external
|
||||
```
|
||||
|
||||
Unpauses a specific pause type when the pause has expired.
|
||||
|
||||
_Can be called by anyone.
|
||||
Throws if UNUSED pause type is used, or the pause expiry period has not passed._
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -213,7 +263,7 @@ Update the pause type role mapping.
|
||||
|
||||
_Throws if UNUSED pause type is used.
|
||||
Throws if role not different.
|
||||
PAUSE_ALL_ROLE role is required to execute this function._
|
||||
SECURITY_COUNCIL_ROLE role is required to execute this function._
|
||||
|
||||
#### Parameters
|
||||
|
||||
@@ -232,7 +282,7 @@ Update the unpause type role mapping.
|
||||
|
||||
_Throws if UNUSED pause type is used.
|
||||
Throws if role not different.
|
||||
UNPAUSE_ALL_ROLE role is required to execute this function._
|
||||
SECURITY_COUNCIL_ROLE role is required to execute this function._
|
||||
|
||||
#### Parameters
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/ac
|
||||
import { IPauseManager } from "./interfaces/IPauseManager.sol";
|
||||
|
||||
/**
|
||||
* @title Contract to manage cross-chain function pausing.
|
||||
* @title Contract to manage cross-chain function pausing with limited duration and cooldown mechanic.
|
||||
* @author ConsenSys Software Inc.
|
||||
* @custom:security-contact security-report@linea.build
|
||||
*/
|
||||
@@ -16,8 +16,19 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
|
||||
/// @notice This is used to unpause all unpausable functions.
|
||||
bytes32 public constant UNPAUSE_ALL_ROLE = keccak256("UNPAUSE_ALL_ROLE");
|
||||
|
||||
/// @notice Role assigned to the security council that enables indefinite pausing and bypassing the cooldown period.
|
||||
/// @dev Is not a pause or unpause role; a specific pause/unpause role is still required for specific pause/unpause types.
|
||||
bytes32 public constant SECURITY_COUNCIL_ROLE = keccak256("SECURITY_COUNCIL_ROLE");
|
||||
|
||||
/// @notice Duration of pauses, after which pauses will expire (except by the SECURITY_COUNCIL_ROLE).
|
||||
uint256 public constant PAUSE_DURATION = 72 hours;
|
||||
|
||||
/// @notice Duration of cooldown after a pause expires, during which no pauses (except by the SECURITY_COUNCIL_ROLE) can be enacted.
|
||||
/// @dev This prevents indefinite pause chaining by a non-SECURITY_COUNCIL_ROLE.
|
||||
uint256 public constant COOLDOWN_DURATION = 24 hours;
|
||||
|
||||
// @dev DEPRECATED. USE _pauseTypeStatusesBitMap INSTEAD
|
||||
mapping(bytes32 pauseType => bool pauseStatus) public pauseTypeStatuses;
|
||||
mapping(bytes32 pauseType => bool pauseStatus) private pauseTypeStatuses;
|
||||
|
||||
/// @dev The bitmap containing the pause statuses mapped by type.
|
||||
uint256 private _pauseTypeStatusesBitMap;
|
||||
@@ -28,10 +39,15 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
|
||||
/// @dev This maps the unpause type to the role that is allowed to unpause it.
|
||||
mapping(PauseType unPauseType => bytes32 role) private _unPauseTypeRoles;
|
||||
|
||||
/// @dev Total contract storage is 11 slots with the gap below.
|
||||
/// @dev Keep 7 free storage slots for future implementation updates to avoid storage collision.
|
||||
/// @notice Unix timestamp of pause expiry.
|
||||
/// @dev pauseExpiryTimestamp applies to all pause types. Pausing with one pause type blocks other pause types from being enacted (unless the SECURITY_COUNCIL_ROLE is used).
|
||||
/// @dev This prevents indefinite pause chaining by a non-SECURITY_COUNCIL_ROLE.
|
||||
uint256 public pauseExpiryTimestamp;
|
||||
|
||||
/// @dev Total contract storage is 12 slots with the gap below.
|
||||
/// @dev Keep 6 free storage slots for future implementation updates to avoid storage collision.
|
||||
/// @dev Note: This was reduced previously to cater for new functionality.
|
||||
uint256[7] private __gap;
|
||||
uint256[6] private __gap;
|
||||
|
||||
/**
|
||||
* @dev Modifier to prevent usage of unused PauseType.
|
||||
@@ -124,6 +140,8 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
|
||||
* @notice Pauses functionality by specific type.
|
||||
* @dev Throws if UNUSED pause type is used.
|
||||
* @dev Requires the role mapped in `_pauseTypeRoles` for the pauseType.
|
||||
* @dev Non-SECURITY_COUNCIL_ROLE can only pause after cooldown has passed.
|
||||
* @dev SECURITY_COUNCIL_ROLE can pause without cooldown or expiry restrictions.
|
||||
* @param _pauseType The pause type value.
|
||||
*/
|
||||
function pauseByType(
|
||||
@@ -132,6 +150,17 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
|
||||
if (isPaused(_pauseType)) {
|
||||
revert IsPaused(_pauseType);
|
||||
}
|
||||
|
||||
unchecked {
|
||||
if (hasRole(SECURITY_COUNCIL_ROLE, _msgSender())) {
|
||||
pauseExpiryTimestamp = type(uint256).max - COOLDOWN_DURATION;
|
||||
} else {
|
||||
if (block.timestamp < pauseExpiryTimestamp + COOLDOWN_DURATION) {
|
||||
revert PauseUnavailableDueToCooldown(pauseExpiryTimestamp + COOLDOWN_DURATION);
|
||||
}
|
||||
pauseExpiryTimestamp = block.timestamp + PAUSE_DURATION;
|
||||
}
|
||||
}
|
||||
|
||||
_pauseTypeStatusesBitMap |= 1 << uint256(_pauseType);
|
||||
emit Paused(_msgSender(), _pauseType);
|
||||
@@ -141,6 +170,7 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
|
||||
* @notice Unpauses functionality by specific type.
|
||||
* @dev Throws if UNUSED pause type is used.
|
||||
* @dev Requires the role mapped in `_unPauseTypeRoles` for the pauseType.
|
||||
* @dev SECURITY_COUNCIL_ROLE unpause will reset the cooldown, enabling non-SECURITY_COUNCIL_ROLE pausing.
|
||||
* @param _pauseType The pause type value.
|
||||
*/
|
||||
function unPauseByType(
|
||||
@@ -150,10 +180,33 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
|
||||
revert IsNotPaused(_pauseType);
|
||||
}
|
||||
|
||||
if (hasRole(SECURITY_COUNCIL_ROLE, _msgSender())) {
|
||||
pauseExpiryTimestamp = block.timestamp - COOLDOWN_DURATION;
|
||||
}
|
||||
_pauseTypeStatusesBitMap &= ~(1 << uint256(_pauseType));
|
||||
emit UnPaused(_msgSender(), _pauseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Unpauses a specific pause type when the pause has expired.
|
||||
* @dev Can be called by anyone.
|
||||
* @dev Throws if UNUSED pause type is used, or the pause expiry period has not passed.
|
||||
* @param _pauseType The pause type value.
|
||||
*/
|
||||
function unPauseByExpiredType(
|
||||
PauseType _pauseType
|
||||
) external onlyUsedPausedTypes(_pauseType) {
|
||||
if (!isPaused(_pauseType)) {
|
||||
revert IsNotPaused(_pauseType);
|
||||
}
|
||||
if (block.timestamp < pauseExpiryTimestamp) {
|
||||
revert PauseNotExpired(pauseExpiryTimestamp);
|
||||
}
|
||||
|
||||
_pauseTypeStatusesBitMap &= ~(1 << uint256(_pauseType));
|
||||
emit UnPausedDueToExpiry(_pauseType);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Check if a pause type is enabled.
|
||||
* @param _pauseType The pause type value.
|
||||
@@ -167,14 +220,14 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
|
||||
* @notice Update the pause type role mapping.
|
||||
* @dev Throws if UNUSED pause type is used.
|
||||
* @dev Throws if role not different.
|
||||
* @dev PAUSE_ALL_ROLE role is required to execute this function.
|
||||
* @dev SECURITY_COUNCIL_ROLE role is required to execute this function.
|
||||
* @param _pauseType The pause type value to update.
|
||||
* @param _newRole The role to update to.
|
||||
*/
|
||||
function updatePauseTypeRole(
|
||||
PauseType _pauseType,
|
||||
bytes32 _newRole
|
||||
) external onlyUsedPausedTypes(_pauseType) onlyRole(PAUSE_ALL_ROLE) {
|
||||
) external onlyUsedPausedTypes(_pauseType) onlyRole(SECURITY_COUNCIL_ROLE) {
|
||||
bytes32 previousRole = _pauseTypeRoles[_pauseType];
|
||||
if (previousRole == _newRole) {
|
||||
revert RolesNotDifferent();
|
||||
@@ -188,14 +241,14 @@ abstract contract PauseManager is IPauseManager, AccessControlUpgradeable {
|
||||
* @notice Update the unpause type role mapping.
|
||||
* @dev Throws if UNUSED pause type is used.
|
||||
* @dev Throws if role not different.
|
||||
* @dev UNPAUSE_ALL_ROLE role is required to execute this function.
|
||||
* @dev SECURITY_COUNCIL_ROLE role is required to execute this function.
|
||||
* @param _pauseType The pause type value to update.
|
||||
* @param _newRole The role to update to.
|
||||
*/
|
||||
function updateUnpauseTypeRole(
|
||||
PauseType _pauseType,
|
||||
bytes32 _newRole
|
||||
) external onlyUsedPausedTypes(_pauseType) onlyRole(UNPAUSE_ALL_ROLE) {
|
||||
) external onlyUsedPausedTypes(_pauseType) onlyRole(SECURITY_COUNCIL_ROLE) {
|
||||
bytes32 previousRole = _unPauseTypeRoles[_pauseType];
|
||||
if (previousRole == _newRole) {
|
||||
revert RolesNotDifferent();
|
||||
|
||||
@@ -50,6 +50,12 @@ interface IPauseManager {
|
||||
*/
|
||||
event UnPaused(address messageSender, PauseType indexed pauseType);
|
||||
|
||||
/**
|
||||
* @notice Emitted when a pause type is unpaused due to pause expiry passing.
|
||||
* @param pauseType The pause type that was unpaused.
|
||||
*/
|
||||
event UnPausedDueToExpiry(PauseType pauseType);
|
||||
|
||||
/**
|
||||
* @notice Emitted when a pause type and its associated role are set in the `_pauseTypeRoles` mapping.
|
||||
* @param pauseType The indexed type of pause.
|
||||
@@ -85,11 +91,21 @@ interface IPauseManager {
|
||||
*/
|
||||
error IsPaused(PauseType pauseType);
|
||||
|
||||
/**
|
||||
* @dev Thrown when unpauseDueToExpiry is attempted before a pause has expired.
|
||||
*/
|
||||
error PauseNotExpired(uint256 expiryEnd);
|
||||
|
||||
/**
|
||||
* @dev Thrown when a specific pause type is not paused and expected to be.
|
||||
*/
|
||||
error IsNotPaused(PauseType pauseType);
|
||||
|
||||
/**
|
||||
* @dev Thrown when pausing is attempted during the cooldown period by a non-SECURITY_COUNCIL_ROLE.
|
||||
*/
|
||||
error PauseUnavailableDueToCooldown(uint256 cooldownEnd);
|
||||
|
||||
/**
|
||||
* @dev Thrown when the unused paused type is used.
|
||||
*/
|
||||
@@ -103,7 +119,9 @@ interface IPauseManager {
|
||||
/**
|
||||
* @notice Pauses functionality by specific type.
|
||||
* @dev Throws if UNUSED pause type is used.
|
||||
* @dev Requires the role mapped in pauseTypeRoles for the pauseType.
|
||||
* @dev Requires the role mapped in `_pauseTypeRoles` for the pauseType.
|
||||
* @dev Non-SECURITY_COUNCIL_ROLE can only pause after cooldown has passed.
|
||||
* @dev SECURITY_COUNCIL_ROLE can pause without cooldown or expiry restrictions.
|
||||
* @param _pauseType The pause type value.
|
||||
*/
|
||||
function pauseByType(PauseType _pauseType) external;
|
||||
@@ -111,11 +129,20 @@ interface IPauseManager {
|
||||
/**
|
||||
* @notice Unpauses functionality by specific type.
|
||||
* @dev Throws if UNUSED pause type is used.
|
||||
* @dev Requires the role mapped in unPauseTypeRoles for the pauseType.
|
||||
* @dev Requires the role mapped in `_unPauseTypeRoles` for the pauseType.
|
||||
* @dev SECURITY_COUNCIL_ROLE unpause will reset the cooldown, enabling non-SECURITY_COUNCIL_ROLE pausing.
|
||||
* @param _pauseType The pause type value.
|
||||
*/
|
||||
function unPauseByType(PauseType _pauseType) external;
|
||||
|
||||
/**
|
||||
* @notice Unpauses a specific pause type when the pause has expired.
|
||||
* @dev Can be called by anyone.
|
||||
* @dev Throws if UNUSED pause type is used, or the pause expiry period has not passed.
|
||||
* @param _pauseType The pause type value.
|
||||
*/
|
||||
function unPauseByExpiredType(PauseType _pauseType) external;
|
||||
|
||||
/**
|
||||
* @notice Check if a pause type is enabled.
|
||||
* @param _pauseType The pause type value.
|
||||
@@ -127,7 +154,7 @@ interface IPauseManager {
|
||||
* @notice Update the pause type role mapping.
|
||||
* @dev Throws if UNUSED pause type is used.
|
||||
* @dev Throws if role not different.
|
||||
* @dev PAUSE_ALL_ROLE role is required to execute this function.
|
||||
* @dev SECURITY_COUNCIL_ROLE role is required to execute this function.
|
||||
* @param _pauseType The pause type value to update.
|
||||
* @param _newRole The role to update to.
|
||||
*/
|
||||
@@ -137,7 +164,7 @@ interface IPauseManager {
|
||||
* @notice Update the unpause type role mapping.
|
||||
* @dev Throws if UNUSED pause type is used.
|
||||
* @dev Throws if role not different.
|
||||
* @dev UNPAUSE_ALL_ROLE role is required to execute this function.
|
||||
* @dev SECURITY_COUNCIL_ROLE role is required to execute this function.
|
||||
* @param _pauseType The pause type value to update.
|
||||
* @param _newRole The role to update to.
|
||||
*/
|
||||
|
||||
@@ -33,6 +33,7 @@ export const VERIFIER_SETTER_ROLE = generateKeccak256(["string"], ["VERIFIER_SET
|
||||
export const VERIFIER_UNSETTER_ROLE = generateKeccak256(["string"], ["VERIFIER_UNSETTER_ROLE"], true);
|
||||
export const L1_MERKLE_ROOTS_SETTER_ROLE = generateKeccak256(["string"], ["L1_MERKLE_ROOTS_SETTER_ROLE"], true);
|
||||
export const L2_MERKLE_ROOTS_SETTER_ROLE = generateKeccak256(["string"], ["L2_MERKLE_ROOTS_SETTER_ROLE"], true);
|
||||
export const SECURITY_COUNCIL_ROLE = generateKeccak256(["string"], ["SECURITY_COUNCIL_ROLE"], true);
|
||||
export const BAD_STARTING_HASH = generateKeccak256(["string"], ["BAD_STARTING_HASH"], true);
|
||||
|
||||
// TokenBridge roles
|
||||
|
||||
@@ -5,3 +5,4 @@ export * from "./hashing";
|
||||
export * from "./dataGeneration";
|
||||
export * from "./dataLoader";
|
||||
export * from "./expectations";
|
||||
export * from "./time";
|
||||
|
||||
12
contracts/test/hardhat/common/helpers/time.ts
Normal file
12
contracts/test/hardhat/common/helpers/time.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { time } from "@nomicfoundation/hardhat-network-helpers";
|
||||
|
||||
export const getLastBlockTimestamp = async (): Promise<bigint> => {
|
||||
return BigInt(await time.latest());
|
||||
};
|
||||
|
||||
export const setFutureTimestampForNextBlock = async (secondsInTheFuture: number | bigint = 1): Promise<bigint> => {
|
||||
const lastBlockTimestamp: number = await time.latest();
|
||||
const futureTimestamp: bigint = BigInt(lastBlockTimestamp) + BigInt(secondsInTheFuture);
|
||||
await time.setNextBlockTimestamp(futureTimestamp);
|
||||
return futureTimestamp;
|
||||
};
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
PAUSE_BLOB_SUBMISSION_ROLE,
|
||||
UNPAUSE_FINALIZATION_ROLE,
|
||||
UNPAUSE_BLOB_SUBMISSION_ROLE,
|
||||
SECURITY_COUNCIL_ROLE,
|
||||
BLOB_SUBMISSION_PAUSE_TYPE,
|
||||
CALLDATA_SUBMISSION_PAUSE_TYPE,
|
||||
FINALIZATION_PAUSE_TYPE,
|
||||
@@ -32,6 +33,8 @@ import {
|
||||
expectEvent,
|
||||
expectRevertWithCustomError,
|
||||
expectRevertWithReason,
|
||||
getLastBlockTimestamp,
|
||||
setFutureTimestampForNextBlock,
|
||||
} from "../common/helpers";
|
||||
|
||||
async function deployTestPauseManagerFixture(): Promise<TestPauseManager> {
|
||||
@@ -46,13 +49,15 @@ describe("PauseManager", () => {
|
||||
let defaultAdmin: SignerWithAddress;
|
||||
let pauseManagerAccount: SignerWithAddress;
|
||||
let nonManager: SignerWithAddress;
|
||||
let securityCouncil: SignerWithAddress;
|
||||
let pauseManager: TestPauseManager;
|
||||
|
||||
beforeEach(async () => {
|
||||
[defaultAdmin, pauseManagerAccount, nonManager] = await ethers.getSigners();
|
||||
[defaultAdmin, pauseManagerAccount, nonManager, securityCouncil] = await ethers.getSigners();
|
||||
pauseManager = await loadFixture(deployTestPauseManagerFixture);
|
||||
|
||||
await Promise.all([
|
||||
// Roles for pauseManagerAccount
|
||||
pauseManager.grantRole(PAUSE_ALL_ROLE, pauseManagerAccount.address),
|
||||
pauseManager.grantRole(UNPAUSE_ALL_ROLE, pauseManagerAccount.address),
|
||||
pauseManager.grantRole(PAUSE_L1_L2_ROLE, pauseManagerAccount.address),
|
||||
@@ -63,6 +68,14 @@ describe("PauseManager", () => {
|
||||
pauseManager.grantRole(UNPAUSE_BLOB_SUBMISSION_ROLE, pauseManagerAccount.address),
|
||||
pauseManager.grantRole(PAUSE_FINALIZATION_ROLE, pauseManagerAccount.address),
|
||||
pauseManager.grantRole(UNPAUSE_FINALIZATION_ROLE, pauseManagerAccount.address),
|
||||
// Roles for securityCouncil
|
||||
pauseManager.grantRole(PAUSE_ALL_ROLE, securityCouncil.address),
|
||||
pauseManager.grantRole(UNPAUSE_ALL_ROLE, securityCouncil.address),
|
||||
pauseManager.grantRole(UNPAUSE_L1_L2_ROLE, securityCouncil.address),
|
||||
pauseManager.grantRole(UNPAUSE_L2_L1_ROLE, securityCouncil.address),
|
||||
pauseManager.grantRole(UNPAUSE_BLOB_SUBMISSION_ROLE, securityCouncil.address),
|
||||
pauseManager.grantRole(UNPAUSE_FINALIZATION_ROLE, securityCouncil.address),
|
||||
pauseManager.grantRole(SECURITY_COUNCIL_ROLE, securityCouncil.address),
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -74,6 +87,10 @@ describe("PauseManager", () => {
|
||||
return pauseManager.connect(account).unPauseByType(pauseType);
|
||||
}
|
||||
|
||||
async function unPauseByExpiredType(pauseType: number, account: SignerWithAddress) {
|
||||
return pauseManager.connect(account).unPauseByExpiredType(pauseType);
|
||||
}
|
||||
|
||||
describe("Initialization checks", () => {
|
||||
it("Deployer has default admin role", async () => {
|
||||
expect(await pauseManager.hasRole(DEFAULT_ADMIN_ROLE, defaultAdmin.address)).to.be.true;
|
||||
@@ -98,32 +115,34 @@ describe("PauseManager", () => {
|
||||
});
|
||||
|
||||
it("should fail updatePauseTypeRole if correct role not used", async () => {
|
||||
const updateCall = pauseManager.connect(nonManager).updatePauseTypeRole(GENERAL_PAUSE_TYPE, DEFAULT_ADMIN_ROLE);
|
||||
await expectRevertWithReason(updateCall, buildAccessErrorMessage(nonManager, PAUSE_ALL_ROLE));
|
||||
const updateCall = pauseManager
|
||||
.connect(pauseManagerAccount)
|
||||
.updatePauseTypeRole(GENERAL_PAUSE_TYPE, DEFAULT_ADMIN_ROLE);
|
||||
await expectRevertWithReason(updateCall, buildAccessErrorMessage(pauseManagerAccount, SECURITY_COUNCIL_ROLE));
|
||||
});
|
||||
|
||||
it("should fail updateUnpauseTypeRole if correct role not used", async () => {
|
||||
const updateCall = pauseManager.connect(nonManager).updateUnpauseTypeRole(GENERAL_PAUSE_TYPE, DEFAULT_ADMIN_ROLE);
|
||||
await expectRevertWithReason(updateCall, buildAccessErrorMessage(nonManager, UNPAUSE_ALL_ROLE));
|
||||
const updateCall = pauseManager
|
||||
.connect(pauseManagerAccount)
|
||||
.updateUnpauseTypeRole(GENERAL_PAUSE_TYPE, DEFAULT_ADMIN_ROLE);
|
||||
await expectRevertWithReason(updateCall, buildAccessErrorMessage(pauseManagerAccount, SECURITY_COUNCIL_ROLE));
|
||||
});
|
||||
|
||||
it("should fail updateUnpauseTypeRole if roles are not different", async () => {
|
||||
const updateCall = pauseManager
|
||||
.connect(pauseManagerAccount)
|
||||
.updatePauseTypeRole(GENERAL_PAUSE_TYPE, PAUSE_ALL_ROLE);
|
||||
const updateCall = pauseManager.connect(securityCouncil).updatePauseTypeRole(GENERAL_PAUSE_TYPE, PAUSE_ALL_ROLE);
|
||||
await expectRevertWithCustomError(pauseManager, updateCall, "RolesNotDifferent");
|
||||
});
|
||||
|
||||
it("should fail updateUnpauseTypeRole if roles are not different", async () => {
|
||||
const updateCall = pauseManager
|
||||
.connect(pauseManagerAccount)
|
||||
.connect(securityCouncil)
|
||||
.updateUnpauseTypeRole(GENERAL_PAUSE_TYPE, UNPAUSE_ALL_ROLE);
|
||||
await expectRevertWithCustomError(pauseManager, updateCall, "RolesNotDifferent");
|
||||
});
|
||||
|
||||
it("should update pause type role with pausing working", async () => {
|
||||
const updateCall = pauseManager
|
||||
.connect(pauseManagerAccount)
|
||||
.connect(securityCouncil)
|
||||
.updatePauseTypeRole(GENERAL_PAUSE_TYPE, DEFAULT_ADMIN_ROLE);
|
||||
await expectEvent(pauseManager, updateCall, "PauseTypeRoleUpdated", [
|
||||
GENERAL_PAUSE_TYPE,
|
||||
@@ -137,7 +156,7 @@ describe("PauseManager", () => {
|
||||
|
||||
it("should fail to pause with old role", async () => {
|
||||
const updateCall = pauseManager
|
||||
.connect(pauseManagerAccount)
|
||||
.connect(securityCouncil)
|
||||
.updatePauseTypeRole(GENERAL_PAUSE_TYPE, DEFAULT_ADMIN_ROLE);
|
||||
await expectEvent(pauseManager, updateCall, "PauseTypeRoleUpdated", [
|
||||
GENERAL_PAUSE_TYPE,
|
||||
@@ -146,14 +165,14 @@ describe("PauseManager", () => {
|
||||
]);
|
||||
|
||||
await expectRevertWithReason(
|
||||
pauseManager.connect(pauseManagerAccount).pauseByType(GENERAL_PAUSE_TYPE),
|
||||
buildAccessErrorMessage(pauseManagerAccount, DEFAULT_ADMIN_ROLE),
|
||||
pauseManager.connect(securityCouncil).pauseByType(GENERAL_PAUSE_TYPE),
|
||||
buildAccessErrorMessage(securityCouncil, DEFAULT_ADMIN_ROLE),
|
||||
);
|
||||
});
|
||||
|
||||
it("should update unpause type role with unpausing working", async () => {
|
||||
const updateCall = pauseManager
|
||||
.connect(pauseManagerAccount)
|
||||
.connect(securityCouncil)
|
||||
.updateUnpauseTypeRole(GENERAL_PAUSE_TYPE, DEFAULT_ADMIN_ROLE);
|
||||
await expectEvent(pauseManager, updateCall, "UnPauseTypeRoleUpdated", [
|
||||
GENERAL_PAUSE_TYPE,
|
||||
@@ -162,7 +181,7 @@ describe("PauseManager", () => {
|
||||
]);
|
||||
|
||||
// pause with non-modified pausing account
|
||||
await pauseManager.connect(pauseManagerAccount).pauseByType(GENERAL_PAUSE_TYPE);
|
||||
await pauseManager.connect(securityCouncil).pauseByType(GENERAL_PAUSE_TYPE);
|
||||
expect(await pauseManager.isPaused(GENERAL_PAUSE_TYPE)).to.be.true;
|
||||
|
||||
await pauseManager.connect(defaultAdmin).unPauseByType(GENERAL_PAUSE_TYPE);
|
||||
@@ -171,7 +190,7 @@ describe("PauseManager", () => {
|
||||
|
||||
it("should fail to unpause with old role", async () => {
|
||||
const updateCall = pauseManager
|
||||
.connect(pauseManagerAccount)
|
||||
.connect(securityCouncil)
|
||||
.updateUnpauseTypeRole(GENERAL_PAUSE_TYPE, DEFAULT_ADMIN_ROLE);
|
||||
await expectEvent(pauseManager, updateCall, "UnPauseTypeRoleUpdated", [
|
||||
GENERAL_PAUSE_TYPE,
|
||||
@@ -180,17 +199,17 @@ describe("PauseManager", () => {
|
||||
]);
|
||||
|
||||
// pause with non-modified pausing account
|
||||
await pauseManager.connect(pauseManagerAccount).pauseByType(GENERAL_PAUSE_TYPE);
|
||||
await pauseManager.connect(securityCouncil).pauseByType(GENERAL_PAUSE_TYPE);
|
||||
expect(await pauseManager.isPaused(GENERAL_PAUSE_TYPE)).to.be.true;
|
||||
|
||||
await expectRevertWithReason(
|
||||
pauseManager.connect(pauseManagerAccount).unPauseByType(GENERAL_PAUSE_TYPE),
|
||||
buildAccessErrorMessage(pauseManagerAccount, DEFAULT_ADMIN_ROLE),
|
||||
pauseManager.connect(securityCouncil).unPauseByType(GENERAL_PAUSE_TYPE),
|
||||
buildAccessErrorMessage(securityCouncil, DEFAULT_ADMIN_ROLE),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("General pausing", () => {
|
||||
describe("Pausing and unpausing with GENERAL_PAUSE_TYPE", () => {
|
||||
// can pause as PAUSE_ALL_ROLE
|
||||
it("should pause the contract if PAUSE_ALL_ROLE", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE);
|
||||
@@ -222,24 +241,6 @@ describe("PauseManager", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pause and unpause event emitting", () => {
|
||||
it("should pause the L1_L2_PAUSE_TYPE", async () => {
|
||||
await expectEvent(pauseManager, pauseByType(L1_L2_PAUSE_TYPE), "Paused", [
|
||||
pauseManagerAccount.address,
|
||||
L1_L2_PAUSE_TYPE,
|
||||
]);
|
||||
});
|
||||
|
||||
it("should unpause the L1_L2_PAUSE_TYPE", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
|
||||
await expectEvent(pauseManager, unPauseByType(L1_L2_PAUSE_TYPE), "UnPaused", [
|
||||
pauseManagerAccount.address,
|
||||
L1_L2_PAUSE_TYPE,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Specific type pausing", () => {
|
||||
describe("Unused pause type", () => {
|
||||
it("should revert when pausing with the unused pause type", async () => {
|
||||
@@ -257,9 +258,17 @@ describe("PauseManager", () => {
|
||||
"PauseTypeNotUsed",
|
||||
);
|
||||
});
|
||||
|
||||
it("should revert when unPauseByExpiredType with the unused pause type", async () => {
|
||||
await expectRevertWithCustomError(
|
||||
pauseManager,
|
||||
pauseManager.unPauseByExpiredType(UNUSED_PAUSE_TYPE),
|
||||
"PauseTypeNotUsed",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("With permissions as PAUSE_ALL_ROLE", () => {
|
||||
describe("With permissions granted by granular pause role", () => {
|
||||
it("should pause the L1_L2_PAUSE_TYPE", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
expect(await pauseManager.isPaused(L1_L2_PAUSE_TYPE)).to.be.true;
|
||||
@@ -320,7 +329,8 @@ describe("PauseManager", () => {
|
||||
expect(await pauseManager.isPaused(FINALIZATION_PAUSE_TYPE)).to.be.false;
|
||||
});
|
||||
});
|
||||
describe("Without permissions - non-PAUSE_ALL_ROLE", () => {
|
||||
|
||||
describe("Without permissions granted by granular pause role", () => {
|
||||
it("cannot pause the L1_L2_PAUSE_TYPE as non-manager", async () => {
|
||||
await expect(pauseByType(L1_L2_PAUSE_TYPE, nonManager)).to.be.revertedWith(
|
||||
buildAccessErrorMessage(nonManager, PAUSE_L1_L2_ROLE),
|
||||
@@ -391,27 +401,198 @@ describe("PauseManager", () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
describe("Incorrect states for pausing and unpausing", () => {
|
||||
|
||||
describe("Incorrect pausing and unpausing", () => {
|
||||
it("Should pause and fail to pause when paused", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
|
||||
await expect(pauseByType(L1_L2_PAUSE_TYPE)).to.be.revertedWithCustomError(pauseManager, "IsPaused");
|
||||
});
|
||||
|
||||
it("Should allow other types to pause if one is paused", async () => {
|
||||
it("Should not allow other types to pause if one is already paused", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
|
||||
await expect(pauseByType(L1_L2_PAUSE_TYPE)).to.be.revertedWithCustomError(pauseManager, "IsPaused");
|
||||
|
||||
await expectEvent(pauseManager, pauseByType(BLOB_SUBMISSION_PAUSE_TYPE), "Paused", [
|
||||
pauseManagerAccount.address,
|
||||
BLOB_SUBMISSION_PAUSE_TYPE,
|
||||
]);
|
||||
const expectedCooldown = (await pauseManager.pauseExpiryTimestamp()) + (await pauseManager.COOLDOWN_DURATION());
|
||||
await expectRevertWithCustomError(
|
||||
pauseManager,
|
||||
pauseManager.connect(pauseManagerAccount).pauseByType(L2_L1_PAUSE_TYPE),
|
||||
"PauseUnavailableDueToCooldown",
|
||||
[expectedCooldown],
|
||||
);
|
||||
});
|
||||
|
||||
it("Should fail to unpause if not paused", async () => {
|
||||
await expect(unPauseByType(L1_L2_PAUSE_TYPE)).to.be.revertedWithCustomError(pauseManager, "IsNotPaused");
|
||||
});
|
||||
|
||||
it("Should fail to unPauseByExpiredType if not paused", async () => {
|
||||
await expect(unPauseByExpiredType(L1_L2_PAUSE_TYPE, nonManager)).to.be.revertedWithCustomError(
|
||||
pauseManager,
|
||||
"IsNotPaused",
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pause and unpause event emitting", () => {
|
||||
it("should pause the L1_L2_PAUSE_TYPE", async () => {
|
||||
await expectEvent(pauseManager, pauseByType(L1_L2_PAUSE_TYPE), "Paused", [
|
||||
pauseManagerAccount.address,
|
||||
L1_L2_PAUSE_TYPE,
|
||||
]);
|
||||
});
|
||||
|
||||
it("should unpause the L1_L2_PAUSE_TYPE", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
|
||||
await expectEvent(pauseManager, unPauseByType(L1_L2_PAUSE_TYPE), "UnPaused", [
|
||||
pauseManagerAccount.address,
|
||||
L1_L2_PAUSE_TYPE,
|
||||
]);
|
||||
});
|
||||
|
||||
it("should unPauseByExpiredType the L1_L2_PAUSE_TYPE", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
await setFutureTimestampForNextBlock(await pauseManager.PAUSE_DURATION());
|
||||
await expectEvent(pauseManager, unPauseByExpiredType(L1_L2_PAUSE_TYPE, nonManager), "UnPausedDueToExpiry", [
|
||||
L1_L2_PAUSE_TYPE,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pausing/unpausing with expiry and cooldown:", () => {
|
||||
it("Pause should set pauseExpiryTimestamp to a time in the future", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE);
|
||||
const lastBlockTimestamp = await getLastBlockTimestamp();
|
||||
expect(await pauseManager.pauseExpiryTimestamp()).to.equal(
|
||||
lastBlockTimestamp + (await pauseManager.PAUSE_DURATION()),
|
||||
);
|
||||
});
|
||||
|
||||
it("unPauseByExpiredType should fail after pause, if pause has not expired", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE);
|
||||
await expectRevertWithCustomError(
|
||||
pauseManager,
|
||||
pauseManager.connect(pauseManagerAccount).unPauseByExpiredType(GENERAL_PAUSE_TYPE),
|
||||
"PauseNotExpired",
|
||||
[await pauseManager.pauseExpiryTimestamp()],
|
||||
);
|
||||
});
|
||||
|
||||
it("unPauseByExpiredType should succeed after pause has expired", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE);
|
||||
await setFutureTimestampForNextBlock(await pauseManager.PAUSE_DURATION());
|
||||
await unPauseByExpiredType(GENERAL_PAUSE_TYPE, nonManager);
|
||||
expect(await pauseManager.isPaused(GENERAL_PAUSE_TYPE)).to.be.false;
|
||||
});
|
||||
|
||||
it("unPauseByExpiredType should not change the pauseExpiryTimestamp", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE);
|
||||
const beforePauseExpiry = await pauseManager.pauseExpiryTimestamp();
|
||||
await setFutureTimestampForNextBlock(await pauseManager.PAUSE_DURATION());
|
||||
await unPauseByExpiredType(GENERAL_PAUSE_TYPE, nonManager);
|
||||
const afterPauseExpiry = await pauseManager.pauseExpiryTimestamp();
|
||||
expect(beforePauseExpiry).to.equal(afterPauseExpiry);
|
||||
});
|
||||
|
||||
it("Should not be able to pause while cooldown is active", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE);
|
||||
await setFutureTimestampForNextBlock(await pauseManager.PAUSE_DURATION());
|
||||
await unPauseByExpiredType(GENERAL_PAUSE_TYPE, nonManager);
|
||||
const expectedCooldown = (await pauseManager.pauseExpiryTimestamp()) + (await pauseManager.COOLDOWN_DURATION());
|
||||
await expectRevertWithCustomError(
|
||||
pauseManager,
|
||||
pauseManager.connect(pauseManagerAccount).pauseByType(GENERAL_PAUSE_TYPE),
|
||||
"PauseUnavailableDueToCooldown",
|
||||
[expectedCooldown],
|
||||
);
|
||||
});
|
||||
|
||||
it("Should be able to pause after cooldown has passed", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE);
|
||||
await setFutureTimestampForNextBlock(await pauseManager.PAUSE_DURATION());
|
||||
await unPauseByExpiredType(GENERAL_PAUSE_TYPE, nonManager);
|
||||
await setFutureTimestampForNextBlock(await pauseManager.COOLDOWN_DURATION());
|
||||
await pauseByType(GENERAL_PAUSE_TYPE);
|
||||
expect(await pauseManager.isPaused(GENERAL_PAUSE_TYPE)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Pausing/unpausing with SECURITY_COUNCIL_ROLE", () => {
|
||||
it("should pause the contract with SECURITY_COUNCIL_ROLE, when another pause type is already active", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
expect(await pauseManager.isPaused(GENERAL_PAUSE_TYPE)).to.be.true;
|
||||
});
|
||||
|
||||
// Should not revert due to overflow from `pauseExpiryTimestamp + COOLDOWN_DURATION`, but due to custom error.
|
||||
it("after pause with SECURITY_COUNCIL_ROLE, should not be able to pause with a non-SECURITY_COUNCIL_ROLE", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
await expectRevertWithCustomError(
|
||||
pauseManager,
|
||||
pauseManager.connect(pauseManagerAccount).pauseByType(L1_L2_PAUSE_TYPE),
|
||||
"PauseUnavailableDueToCooldown",
|
||||
[ethers.MaxUint256],
|
||||
);
|
||||
});
|
||||
|
||||
it("should set pauseExpiryTimestamp to an unreachable timestamp if pause enacted by SECURITY_COUNCIL_ROLE", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
expect(await pauseManager.pauseExpiryTimestamp()).to.equal(
|
||||
ethers.MaxUint256 - (await pauseManager.COOLDOWN_DURATION()),
|
||||
);
|
||||
});
|
||||
|
||||
it("Should be unable to unPauseByExpiredType after pause with SECURITY_COUNCIL_ROLE", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
const pauseTimestamp = await getLastBlockTimestamp();
|
||||
const expectedPauseExpiry = pauseTimestamp + (await pauseManager.PAUSE_DURATION());
|
||||
await setFutureTimestampForNextBlock((await pauseManager.PAUSE_DURATION()) + BigInt(1));
|
||||
await expectRevertWithCustomError(
|
||||
pauseManager,
|
||||
pauseManager.connect(pauseManagerAccount).unPauseByExpiredType(GENERAL_PAUSE_TYPE),
|
||||
"PauseNotExpired",
|
||||
[await pauseManager.pauseExpiryTimestamp()],
|
||||
);
|
||||
// Assert that we have passed expected pause expiry
|
||||
expect(await getLastBlockTimestamp()).to.be.above(expectedPauseExpiry);
|
||||
});
|
||||
|
||||
it("should reset the pause cooldown when unpause contract with SECURITY_COUNCIL_ROLE", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
const unPauseBlockTimestamp = await setFutureTimestampForNextBlock();
|
||||
await unPauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
expect(await pauseManager.pauseExpiryTimestamp()).to.equal(
|
||||
unPauseBlockTimestamp - (await pauseManager.COOLDOWN_DURATION()),
|
||||
);
|
||||
});
|
||||
|
||||
it("should not reset the pause cooldown when unpause contract with non-SECURITY_COUNCIL_ROLE", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
await unPauseByType(GENERAL_PAUSE_TYPE, pauseManagerAccount);
|
||||
expect(await pauseManager.pauseExpiryTimestamp()).to.equal(
|
||||
ethers.MaxUint256 - (await pauseManager.COOLDOWN_DURATION()),
|
||||
);
|
||||
});
|
||||
|
||||
it("after unpause contract with SECURITY_COUNCIL_ROLE, any pause should be possible", async () => {
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
await unPauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
});
|
||||
|
||||
it("during pause cooldown, SECURITY_COUNCIL_ROLE should be able to pause", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
await setFutureTimestampForNextBlock(await pauseManager.PAUSE_DURATION());
|
||||
await unPauseByExpiredType(L1_L2_PAUSE_TYPE, nonManager);
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
});
|
||||
|
||||
it("Non-SECURITY_COUNCIL_ROLE pause -> SECURITY_COUNCIL_ROLE pause -> SECURITY_COUNCIL_ROLE unpause -> unPauseByExpiredType should work", async () => {
|
||||
await pauseByType(L1_L2_PAUSE_TYPE);
|
||||
await pauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
await unPauseByType(GENERAL_PAUSE_TYPE, securityCouncil);
|
||||
await unPauseByExpiredType(L1_L2_PAUSE_TYPE, nonManager);
|
||||
expect(await pauseManager.isPaused(GENERAL_PAUSE_TYPE)).to.be.false;
|
||||
expect(await pauseManager.isPaused(L1_L2_PAUSE_TYPE)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user