123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- // SPDX-License-Identifier: MIT
- // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControlDefaultAdminRules.sol)
- pragma solidity ^0.8.0;
- import "./AccessControl.sol";
- import "./IAccessControlDefaultAdminRules.sol";
- import "../utils/math/SafeCast.sol";
- import "../interfaces/IERC5313.sol";
- /**
- * @dev Extension of {AccessControl} that allows specifying special rules to manage
- * the `DEFAULT_ADMIN_ROLE` holder, which is a sensitive role with special permissions
- * over other roles that may potentially have privileged rights in the system.
- *
- * If a specific role doesn't have an admin role assigned, the holder of the
- * `DEFAULT_ADMIN_ROLE` will have the ability to grant it and revoke it.
- *
- * This contract implements the following risk mitigations on top of {AccessControl}:
- *
- * * Only one account holds the `DEFAULT_ADMIN_ROLE` since deployment until it's potentially renounced.
- * * Enforces a 2-step process to transfer the `DEFAULT_ADMIN_ROLE` to another account.
- * * Enforces a configurable delay between the two steps, with the ability to cancel before the transfer is accepted.
- * * The delay can be changed by scheduling, see {changeDefaultAdminDelay}.
- * * It is not possible to use another role to manage the `DEFAULT_ADMIN_ROLE`.
- *
- * Example usage:
- *
- * ```solidity
- * contract MyToken is AccessControlDefaultAdminRules {
- * constructor() AccessControlDefaultAdminRules(
- * 3 days,
- * msg.sender // Explicit initial `DEFAULT_ADMIN_ROLE` holder
- * ) {}
- * }
- * ```
- *
- * _Available since v4.9._
- */
- abstract contract AccessControlDefaultAdminRules is IAccessControlDefaultAdminRules, IERC5313, AccessControl {
- // pending admin pair read/written together frequently
- address private _pendingDefaultAdmin;
- uint48 private _pendingDefaultAdminSchedule; // 0 == unset
- uint48 private _currentDelay;
- address private _currentDefaultAdmin;
- // pending delay pair read/written together frequently
- uint48 private _pendingDelay;
- uint48 private _pendingDelaySchedule; // 0 == unset
- /**
- * @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
- */
- constructor(uint48 initialDelay, address initialDefaultAdmin) {
- require(initialDefaultAdmin != address(0), "AccessControl: 0 default admin");
- _currentDelay = initialDelay;
- _grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
- }
- /**
- * @dev See {IERC165-supportsInterface}.
- */
- function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
- return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);
- }
- /**
- * @dev See {IERC5313-owner}.
- */
- function owner() public view virtual returns (address) {
- return defaultAdmin();
- }
- ///
- /// Override AccessControl role management
- ///
- /**
- * @dev See {AccessControl-grantRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
- */
- function grantRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
- require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly grant default admin role");
- super.grantRole(role, account);
- }
- /**
- * @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
- */
- function revokeRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
- require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't directly revoke default admin role");
- super.revokeRole(role, account);
- }
- /**
- * @dev See {AccessControl-renounceRole}.
- *
- * For the `DEFAULT_ADMIN_ROLE`, it only allows renouncing in two steps by first calling
- * {beginDefaultAdminTransfer} to the `address(0)`, so it's required that the {pendingDefaultAdmin} schedule
- * has also passed when calling this function.
- *
- * After its execution, it will not be possible to call `onlyRole(DEFAULT_ADMIN_ROLE)` functions.
- *
- * NOTE: Renouncing `DEFAULT_ADMIN_ROLE` will leave the contract without a {defaultAdmin},
- * thereby disabling any functionality that is only available for it, and the possibility of reassigning a
- * non-administrated role.
- */
- function renounceRole(bytes32 role, address account) public virtual override(AccessControl, IAccessControl) {
- if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
- (address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
- require(
- newDefaultAdmin == address(0) && _isScheduleSet(schedule) && _hasSchedulePassed(schedule),
- "AccessControl: only can renounce in two delayed steps"
- );
- delete _pendingDefaultAdminSchedule;
- }
- super.renounceRole(role, account);
- }
- /**
- * @dev See {AccessControl-_grantRole}.
- *
- * For `DEFAULT_ADMIN_ROLE`, it only allows granting if there isn't already a {defaultAdmin} or if the
- * role has been previously renounced.
- *
- * NOTE: Exposing this function through another mechanism may make the `DEFAULT_ADMIN_ROLE`
- * assignable again. Make sure to guarantee this is the expected behavior in your implementation.
- */
- function _grantRole(bytes32 role, address account) internal virtual override {
- if (role == DEFAULT_ADMIN_ROLE) {
- require(defaultAdmin() == address(0), "AccessControl: default admin already granted");
- _currentDefaultAdmin = account;
- }
- super._grantRole(role, account);
- }
- /**
- * @dev See {AccessControl-_revokeRole}.
- */
- function _revokeRole(bytes32 role, address account) internal virtual override {
- if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
- delete _currentDefaultAdmin;
- }
- super._revokeRole(role, account);
- }
- /**
- * @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
- */
- function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
- require(role != DEFAULT_ADMIN_ROLE, "AccessControl: can't violate default admin rules");
- super._setRoleAdmin(role, adminRole);
- }
- ///
- /// AccessControlDefaultAdminRules accessors
- ///
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function defaultAdmin() public view virtual returns (address) {
- return _currentDefaultAdmin;
- }
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
- return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);
- }
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function defaultAdminDelay() public view virtual returns (uint48) {
- uint48 schedule = _pendingDelaySchedule;
- return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
- }
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
- schedule = _pendingDelaySchedule;
- return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);
- }
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {
- return 5 days;
- }
- ///
- /// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin
- ///
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function beginDefaultAdminTransfer(address newAdmin) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- _beginDefaultAdminTransfer(newAdmin);
- }
- /**
- * @dev See {beginDefaultAdminTransfer}.
- *
- * Internal function without access restriction.
- */
- function _beginDefaultAdminTransfer(address newAdmin) internal virtual {
- uint48 newSchedule = SafeCast.toUint48(block.timestamp) + defaultAdminDelay();
- _setPendingDefaultAdmin(newAdmin, newSchedule);
- emit DefaultAdminTransferScheduled(newAdmin, newSchedule);
- }
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function cancelDefaultAdminTransfer() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- _cancelDefaultAdminTransfer();
- }
- /**
- * @dev See {cancelDefaultAdminTransfer}.
- *
- * Internal function without access restriction.
- */
- function _cancelDefaultAdminTransfer() internal virtual {
- _setPendingDefaultAdmin(address(0), 0);
- }
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function acceptDefaultAdminTransfer() public virtual {
- (address newDefaultAdmin, ) = pendingDefaultAdmin();
- require(_msgSender() == newDefaultAdmin, "AccessControl: pending admin must accept");
- _acceptDefaultAdminTransfer();
- }
- /**
- * @dev See {acceptDefaultAdminTransfer}.
- *
- * Internal function without access restriction.
- */
- function _acceptDefaultAdminTransfer() internal virtual {
- (address newAdmin, uint48 schedule) = pendingDefaultAdmin();
- require(_isScheduleSet(schedule) && _hasSchedulePassed(schedule), "AccessControl: transfer delay not passed");
- _revokeRole(DEFAULT_ADMIN_ROLE, defaultAdmin());
- _grantRole(DEFAULT_ADMIN_ROLE, newAdmin);
- delete _pendingDefaultAdmin;
- delete _pendingDefaultAdminSchedule;
- }
- ///
- /// AccessControlDefaultAdminRules public and internal setters for defaultAdminDelay/pendingDefaultAdminDelay
- ///
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function changeDefaultAdminDelay(uint48 newDelay) public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- _changeDefaultAdminDelay(newDelay);
- }
- /**
- * @dev See {changeDefaultAdminDelay}.
- *
- * Internal function without access restriction.
- */
- function _changeDefaultAdminDelay(uint48 newDelay) internal virtual {
- uint48 newSchedule = SafeCast.toUint48(block.timestamp) + _delayChangeWait(newDelay);
- _setPendingDelay(newDelay, newSchedule);
- emit DefaultAdminDelayChangeScheduled(newDelay, newSchedule);
- }
- /**
- * @inheritdoc IAccessControlDefaultAdminRules
- */
- function rollbackDefaultAdminDelay() public virtual onlyRole(DEFAULT_ADMIN_ROLE) {
- _rollbackDefaultAdminDelay();
- }
- /**
- * @dev See {rollbackDefaultAdminDelay}.
- *
- * Internal function without access restriction.
- */
- function _rollbackDefaultAdminDelay() internal virtual {
- _setPendingDelay(0, 0);
- }
- /**
- * @dev Returns the amount of seconds to wait after the `newDelay` will
- * become the new {defaultAdminDelay}.
- *
- * The value returned guarantees that if the delay is reduced, it will go into effect
- * after a wait that honors the previously set delay.
- *
- * See {defaultAdminDelayIncreaseWait}.
- */
- function _delayChangeWait(uint48 newDelay) internal view virtual returns (uint48) {
- uint48 currentDelay = defaultAdminDelay();
- // When increasing the delay, we schedule the delay change to occur after a period of "new delay" has passed, up
- // to a maximum given by defaultAdminDelayIncreaseWait, by default 5 days. For example, if increasing from 1 day
- // to 3 days, the new delay will come into effect after 3 days. If increasing from 1 day to 10 days, the new
- // delay will come into effect after 5 days. The 5 day wait period is intended to be able to fix an error like
- // using milliseconds instead of seconds.
- //
- // When decreasing the delay, we wait the difference between "current delay" and "new delay". This guarantees
- // that an admin transfer cannot be made faster than "current delay" at the time the delay change is scheduled.
- // For example, if decreasing from 10 days to 3 days, the new delay will come into effect after 7 days.
- return
- newDelay > currentDelay
- ? uint48(Math.min(newDelay, defaultAdminDelayIncreaseWait())) // no need to safecast, both inputs are uint48
- : currentDelay - newDelay;
- }
- ///
- /// Private setters
- ///
- /**
- * @dev Setter of the tuple for pending admin and its schedule.
- *
- * May emit a DefaultAdminTransferCanceled event.
- */
- function _setPendingDefaultAdmin(address newAdmin, uint48 newSchedule) private {
- (, uint48 oldSchedule) = pendingDefaultAdmin();
- _pendingDefaultAdmin = newAdmin;
- _pendingDefaultAdminSchedule = newSchedule;
- // An `oldSchedule` from `pendingDefaultAdmin()` is only set if it hasn't been accepted.
- if (_isScheduleSet(oldSchedule)) {
- // Emit for implicit cancellations when another default admin was scheduled.
- emit DefaultAdminTransferCanceled();
- }
- }
- /**
- * @dev Setter of the tuple for pending delay and its schedule.
- *
- * May emit a DefaultAdminDelayChangeCanceled event.
- */
- function _setPendingDelay(uint48 newDelay, uint48 newSchedule) private {
- uint48 oldSchedule = _pendingDelaySchedule;
- if (_isScheduleSet(oldSchedule)) {
- if (_hasSchedulePassed(oldSchedule)) {
- // Materialize a virtual delay
- _currentDelay = _pendingDelay;
- } else {
- // Emit for implicit cancellations when another delay was scheduled.
- emit DefaultAdminDelayChangeCanceled();
- }
- }
- _pendingDelay = newDelay;
- _pendingDelaySchedule = newSchedule;
- }
- ///
- /// Private helpers
- ///
- /**
- * @dev Defines if an `schedule` is considered set. For consistency purposes.
- */
- function _isScheduleSet(uint48 schedule) private pure returns (bool) {
- return schedule != 0;
- }
- /**
- * @dev Defines if an `schedule` is considered passed. For consistency purposes.
- */
- function _hasSchedulePassed(uint48 schedule) private view returns (bool) {
- return schedule < block.timestamp;
- }
- }
|