-
Notifications
You must be signed in to change notification settings - Fork 195
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
df8400f
commit 4e18178
Showing
4 changed files
with
183 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// SPDX-FileCopyrightText: 2024 Lido <[email protected]> | ||
// SPDX-License-Identifier: GPL-3.0 | ||
|
||
// See contracts/COMPILERS.md | ||
pragma solidity 0.8.25; | ||
|
||
import {PausableUntil} from "contracts/common/utils/PausableUntil.sol"; | ||
import {AccessControlEnumerableUpgradeable} from "contracts/openzeppelin/5.0.2/upgradeable/access/extensions/AccessControlEnumerableUpgradeable.sol"; | ||
|
||
/// @title PausableAccessControlEnumerableUpgradeable aka PausableACEU | ||
/// @author folkyatina | ||
abstract contract OZPausableUntil is PausableUntil, AccessControlEnumerableUpgradeable { | ||
/// @notice role that allows to pause the hub | ||
bytes32 public constant PAUSE_ROLE = keccak256("OZPausableUntil.PauseRole"); | ||
/// @notice role that allows to resume the hub | ||
bytes32 public constant RESUME_ROLE = keccak256("OZPausableUntil.ResumeRole"); | ||
|
||
/// @notice Resume withdrawal requests placement and finalization | ||
/// @dev Contract is deployed in paused state and should be resumed explicitly | ||
function resume() external onlyRole(RESUME_ROLE) { | ||
_resume(); | ||
} | ||
|
||
/// @notice Pause withdrawal requests placement and finalization. Claiming finalized requests will still be available | ||
/// @param _duration pause duration in seconds (use `PAUSE_INFINITELY` for unlimited) | ||
/// @dev Reverts if contract is already paused | ||
/// @dev Reverts reason if sender has no `PAUSE_ROLE` | ||
/// @dev Reverts if zero duration is passed | ||
function pauseFor(uint256 _duration) external onlyRole(PAUSE_ROLE) { | ||
_pauseFor(_duration); | ||
} | ||
|
||
/// @notice Pause withdrawal requests placement and finalization. Claiming finalized requests will still be available | ||
/// @param _pauseUntilInclusive the last second to pause until inclusive | ||
/// @dev Reverts if the timestamp is in the past | ||
/// @dev Reverts if sender has no `PAUSE_ROLE` | ||
/// @dev Reverts if contract is already paused | ||
function pauseUntil(uint256 _pauseUntilInclusive) external onlyRole(PAUSE_ROLE) { | ||
_pauseUntil(_pauseUntilInclusive); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// SPDX-FileCopyrightText: 2023 Lido <[email protected]>, Aragon | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.9; | ||
|
||
library UnstructuredStorage { | ||
function getStorageBool(bytes32 position) internal view returns (bool data) { | ||
assembly { data := sload(position) } | ||
} | ||
|
||
function getStorageAddress(bytes32 position) internal view returns (address data) { | ||
assembly { data := sload(position) } | ||
} | ||
|
||
function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { | ||
assembly { data := sload(position) } | ||
} | ||
|
||
function getStorageUint256(bytes32 position) internal view returns (uint256 data) { | ||
assembly { data := sload(position) } | ||
} | ||
|
||
function setStorageBool(bytes32 position, bool data) internal { | ||
assembly { sstore(position, data) } | ||
} | ||
|
||
function setStorageAddress(bytes32 position, address data) internal { | ||
assembly { sstore(position, data) } | ||
} | ||
|
||
function setStorageBytes32(bytes32 position, bytes32 data) internal { | ||
assembly { sstore(position, data) } | ||
} | ||
|
||
function setStorageUint256(bytes32 position, uint256 data) internal { | ||
assembly { sstore(position, data) } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// SPDX-FileCopyrightText: 2023 Lido <[email protected]> | ||
// SPDX-License-Identifier: GPL-3.0 | ||
pragma solidity ^0.8.9; | ||
|
||
import {UnstructuredStorage} from "contracts/common/lib/UnstructuredStorage.sol"; | ||
|
||
|
||
abstract contract PausableUntil { | ||
using UnstructuredStorage for bytes32; | ||
|
||
/// Contract resume/pause control storage slot | ||
bytes32 internal constant RESUME_SINCE_TIMESTAMP_POSITION = keccak256("lido.PausableUntil.resumeSinceTimestamp"); | ||
/// Special value for the infinite pause | ||
uint256 public constant PAUSE_INFINITELY = type(uint256).max; | ||
|
||
/// @notice Emitted when paused by the `pauseFor` or `pauseUntil` call | ||
event Paused(uint256 duration); | ||
/// @notice Emitted when resumed by the `resume` call | ||
event Resumed(); | ||
|
||
error ZeroPauseDuration(); | ||
error PausedExpected(); | ||
error ResumedExpected(); | ||
error PauseUntilMustBeInFuture(); | ||
|
||
/// @notice Reverts when paused | ||
modifier whenResumed() { | ||
_checkResumed(); | ||
_; | ||
} | ||
|
||
function _checkPaused() internal view { | ||
if (!isPaused()) { | ||
revert PausedExpected(); | ||
} | ||
} | ||
|
||
function _checkResumed() internal view { | ||
if (isPaused()) { | ||
revert ResumedExpected(); | ||
} | ||
} | ||
|
||
/// @notice Returns whether the contract is paused | ||
function isPaused() public view returns (bool) { | ||
return block.timestamp < RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256(); | ||
} | ||
|
||
/// @notice Returns one of: | ||
/// - PAUSE_INFINITELY if paused infinitely returns | ||
/// - first second when get contract get resumed if paused for specific duration | ||
/// - some timestamp in past if not paused | ||
function getResumeSinceTimestamp() external view returns (uint256) { | ||
return RESUME_SINCE_TIMESTAMP_POSITION.getStorageUint256(); | ||
} | ||
|
||
function _resume() internal { | ||
_checkPaused(); | ||
RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(block.timestamp); | ||
emit Resumed(); | ||
} | ||
|
||
function _pauseFor(uint256 _duration) internal { | ||
_checkResumed(); | ||
if (_duration == 0) revert ZeroPauseDuration(); | ||
|
||
uint256 resumeSince; | ||
if (_duration == PAUSE_INFINITELY) { | ||
resumeSince = PAUSE_INFINITELY; | ||
} else { | ||
resumeSince = block.timestamp + _duration; | ||
} | ||
_setPausedState(resumeSince); | ||
} | ||
|
||
function _pauseUntil(uint256 _pauseUntilInclusive) internal { | ||
_checkResumed(); | ||
if (_pauseUntilInclusive < block.timestamp) revert PauseUntilMustBeInFuture(); | ||
|
||
uint256 resumeSince; | ||
if (_pauseUntilInclusive != PAUSE_INFINITELY) { | ||
resumeSince = _pauseUntilInclusive + 1; | ||
} else { | ||
resumeSince = PAUSE_INFINITELY; | ||
} | ||
_setPausedState(resumeSince); | ||
} | ||
|
||
function _setPausedState(uint256 _resumeSince) internal { | ||
RESUME_SINCE_TIMESTAMP_POSITION.setStorageUint256(_resumeSince); | ||
if (_resumeSince == PAUSE_INFINITELY) { | ||
emit Paused(PAUSE_INFINITELY); | ||
} else { | ||
emit Paused(_resumeSince - block.timestamp); | ||
} | ||
} | ||
Check warning Code scanning / Slither Dangerous strict equalities Medium
PausableUntil._setPausedState(uint256) uses a dangerous strict equality:
- _resumeSince == PAUSE_INFINITELY |
||
} |