feat: initial commit for wtf-contract v2
This commit is contained in:
+232
@@ -0,0 +1,232 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.6.0) (access/AccessControl.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
|
||||
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
|
||||
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
|
||||
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
|
||||
|
||||
/**
|
||||
* @dev Contract module that allows children to implement role-based access
|
||||
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
|
||||
* members except through off-chain means by accessing the contract event logs. Some
|
||||
* applications may benefit from on-chain enumerability, for those cases see
|
||||
* {AccessControlEnumerable}.
|
||||
*
|
||||
* Roles are referred to by their `bytes32` identifier. These should be exposed
|
||||
* in the external API and be unique. The best way to achieve this is by
|
||||
* using `public constant` hash digests:
|
||||
*
|
||||
* ```solidity
|
||||
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
|
||||
* ```
|
||||
*
|
||||
* Roles can be used to represent a set of permissions. To restrict access to a
|
||||
* function call, use {hasRole}:
|
||||
*
|
||||
* ```solidity
|
||||
* function foo() public {
|
||||
* require(hasRole(MY_ROLE, msg.sender));
|
||||
* ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Roles can be granted and revoked dynamically via the {grantRole} and
|
||||
* {revokeRole} functions. Each role has an associated admin role, and only
|
||||
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
|
||||
*
|
||||
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
|
||||
* that only accounts with this role will be able to grant or revoke other
|
||||
* roles. More complex role relationships can be created by using
|
||||
* {_setRoleAdmin}.
|
||||
*
|
||||
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
|
||||
* grant and revoke this role. Extra precautions should be taken to secure
|
||||
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
|
||||
* to enforce additional security measures for this role.
|
||||
*/
|
||||
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
|
||||
struct RoleData {
|
||||
mapping(address account => bool) hasRole;
|
||||
bytes32 adminRole;
|
||||
}
|
||||
|
||||
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
|
||||
|
||||
|
||||
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
|
||||
struct AccessControlStorage {
|
||||
mapping(bytes32 role => RoleData) _roles;
|
||||
}
|
||||
|
||||
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
|
||||
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
|
||||
|
||||
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
|
||||
assembly {
|
||||
$.slot := AccessControlStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Modifier that checks that an account has a specific role. Reverts
|
||||
* with an {AccessControlUnauthorizedAccount} error including the required role.
|
||||
*/
|
||||
modifier onlyRole(bytes32 role) {
|
||||
_checkRole(role);
|
||||
_;
|
||||
}
|
||||
|
||||
function __AccessControl_init() internal onlyInitializing {
|
||||
}
|
||||
|
||||
function __AccessControl_init_unchained() internal onlyInitializing {
|
||||
}
|
||||
/// @inheritdoc ERC165Upgradeable
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
||||
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns `true` if `account` has been granted `role`.
|
||||
*/
|
||||
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
|
||||
AccessControlStorage storage $ = _getAccessControlStorage();
|
||||
return $._roles[role].hasRole[account];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
|
||||
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
|
||||
*/
|
||||
function _checkRole(bytes32 role) internal view virtual {
|
||||
_checkRole(role, _msgSender());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
|
||||
* is missing `role`.
|
||||
*/
|
||||
function _checkRole(bytes32 role, address account) internal view virtual {
|
||||
if (!hasRole(role, account)) {
|
||||
revert AccessControlUnauthorizedAccount(account, role);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Returns the admin role that controls `role`. See {grantRole} and
|
||||
* {revokeRole}.
|
||||
*
|
||||
* To change a role's admin, use {_setRoleAdmin}.
|
||||
*/
|
||||
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
|
||||
AccessControlStorage storage $ = _getAccessControlStorage();
|
||||
return $._roles[role].adminRole;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Grants `role` to `account`.
|
||||
*
|
||||
* If `account` had not been already granted `role`, emits a {RoleGranted}
|
||||
* event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must have ``role``'s admin role.
|
||||
*
|
||||
* May emit a {RoleGranted} event.
|
||||
*/
|
||||
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
|
||||
_grantRole(role, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revokes `role` from `account`.
|
||||
*
|
||||
* If `account` had been granted `role`, emits a {RoleRevoked} event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must have ``role``'s admin role.
|
||||
*
|
||||
* May emit a {RoleRevoked} event.
|
||||
*/
|
||||
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
|
||||
_revokeRole(role, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Revokes `role` from the calling account.
|
||||
*
|
||||
* Roles are often managed via {grantRole} and {revokeRole}: this function's
|
||||
* purpose is to provide a mechanism for accounts to lose their privileges
|
||||
* if they are compromised (such as when a trusted device is misplaced).
|
||||
*
|
||||
* If the calling account had been revoked `role`, emits a {RoleRevoked}
|
||||
* event.
|
||||
*
|
||||
* Requirements:
|
||||
*
|
||||
* - the caller must be `callerConfirmation`.
|
||||
*
|
||||
* May emit a {RoleRevoked} event.
|
||||
*/
|
||||
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
|
||||
if (callerConfirmation != _msgSender()) {
|
||||
revert AccessControlBadConfirmation();
|
||||
}
|
||||
|
||||
_revokeRole(role, callerConfirmation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets `adminRole` as ``role``'s admin role.
|
||||
*
|
||||
* Emits a {RoleAdminChanged} event.
|
||||
*/
|
||||
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
|
||||
AccessControlStorage storage $ = _getAccessControlStorage();
|
||||
bytes32 previousAdminRole = getRoleAdmin(role);
|
||||
$._roles[role].adminRole = adminRole;
|
||||
emit RoleAdminChanged(role, previousAdminRole, adminRole);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
|
||||
*
|
||||
* Internal function without access restriction.
|
||||
*
|
||||
* May emit a {RoleGranted} event.
|
||||
*/
|
||||
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
|
||||
AccessControlStorage storage $ = _getAccessControlStorage();
|
||||
if (!hasRole(role, account)) {
|
||||
$._roles[role].hasRole[account] = true;
|
||||
emit RoleGranted(role, account, _msgSender());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
|
||||
*
|
||||
* Internal function without access restriction.
|
||||
*
|
||||
* May emit a {RoleRevoked} event.
|
||||
*/
|
||||
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
|
||||
AccessControlStorage storage $ = _getAccessControlStorage();
|
||||
if (hasRole(role, account)) {
|
||||
$._roles[role].hasRole[account] = false;
|
||||
emit RoleRevoked(role, account, _msgSender());
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// OpenZeppelin Contracts (last updated v5.6.0) (access/extensions/AccessControlDefaultAdminRules.sol)
|
||||
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import {IAccessControlDefaultAdminRules} from "@openzeppelin/contracts/access/extensions/IAccessControlDefaultAdminRules.sol";
|
||||
import {AccessControlUpgradeable} from "../AccessControlUpgradeable.sol";
|
||||
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
|
||||
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
|
||||
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
|
||||
import {IERC5313} from "@openzeppelin/contracts/interfaces/IERC5313.sol";
|
||||
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
||||
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.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}.
|
||||
* * Role transfers must wait at least one block after scheduling before it can be accepted.
|
||||
* * 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
|
||||
* ) {}
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
abstract contract AccessControlDefaultAdminRulesUpgradeable is Initializable, IAccessControlDefaultAdminRules, IERC5313, AccessControlUpgradeable {
|
||||
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControlDefaultAdminRules
|
||||
struct AccessControlDefaultAdminRulesStorage {
|
||||
// pending admin pair read/written together frequently
|
||||
address _pendingDefaultAdmin;
|
||||
uint48 _pendingDefaultAdminSchedule; // 0 == unset
|
||||
|
||||
uint48 _currentDelay;
|
||||
address _currentDefaultAdmin;
|
||||
|
||||
// pending delay pair read/written together frequently
|
||||
uint48 _pendingDelay;
|
||||
uint48 _pendingDelaySchedule; // 0 == unset
|
||||
}
|
||||
|
||||
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControlDefaultAdminRules")) - 1)) & ~bytes32(uint256(0xff))
|
||||
bytes32 private constant AccessControlDefaultAdminRulesStorageLocation = 0xeef3dac4538c82c8ace4063ab0acd2d15cdb5883aa1dff7c2673abb3d8698400;
|
||||
|
||||
function _getAccessControlDefaultAdminRulesStorage() private pure returns (AccessControlDefaultAdminRulesStorage storage $) {
|
||||
assembly {
|
||||
$.slot := AccessControlDefaultAdminRulesStorageLocation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Sets the initial values for {defaultAdminDelay} and {defaultAdmin} address.
|
||||
*/
|
||||
function __AccessControlDefaultAdminRules_init(uint48 initialDelay, address initialDefaultAdmin) internal onlyInitializing {
|
||||
__AccessControlDefaultAdminRules_init_unchained(initialDelay, initialDefaultAdmin);
|
||||
}
|
||||
|
||||
function __AccessControlDefaultAdminRules_init_unchained(uint48 initialDelay, address initialDefaultAdmin) internal onlyInitializing {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
if (initialDefaultAdmin == address(0)) {
|
||||
revert AccessControlInvalidDefaultAdmin(address(0));
|
||||
}
|
||||
$._currentDelay = initialDelay;
|
||||
_grantRole(DEFAULT_ADMIN_ROLE, initialDefaultAdmin);
|
||||
}
|
||||
|
||||
/// @inheritdoc IERC165
|
||||
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
|
||||
return interfaceId == type(IAccessControlDefaultAdminRules).interfaceId || super.supportsInterface(interfaceId);
|
||||
}
|
||||
|
||||
/// @inheritdoc IERC5313
|
||||
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(AccessControlUpgradeable, IAccessControl) {
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
super.grantRole(role, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {AccessControl-revokeRole}. Reverts for `DEFAULT_ADMIN_ROLE`.
|
||||
*/
|
||||
function revokeRole(bytes32 role, address account) public virtual override(AccessControlUpgradeable, IAccessControl) {
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
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(AccessControlUpgradeable, IAccessControl) {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
|
||||
(address newDefaultAdmin, uint48 schedule) = pendingDefaultAdmin();
|
||||
if (newDefaultAdmin != address(0) || !_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
|
||||
revert AccessControlEnforcedDefaultAdminDelay(schedule);
|
||||
}
|
||||
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 returns (bool) {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
if (defaultAdmin() != address(0)) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
$._currentDefaultAdmin = account;
|
||||
}
|
||||
return super._grantRole(role, account);
|
||||
}
|
||||
|
||||
/// @inheritdoc AccessControlUpgradeable
|
||||
function _revokeRole(bytes32 role, address account) internal virtual override returns (bool) {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
if (role == DEFAULT_ADMIN_ROLE && account == defaultAdmin()) {
|
||||
delete $._currentDefaultAdmin;
|
||||
}
|
||||
return super._revokeRole(role, account);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {AccessControl-_setRoleAdmin}. Reverts for `DEFAULT_ADMIN_ROLE`.
|
||||
*/
|
||||
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual override {
|
||||
if (role == DEFAULT_ADMIN_ROLE) {
|
||||
revert AccessControlEnforcedDefaultAdminRules();
|
||||
}
|
||||
super._setRoleAdmin(role, adminRole);
|
||||
}
|
||||
|
||||
///
|
||||
/// AccessControlDefaultAdminRules accessors
|
||||
///
|
||||
|
||||
/// @inheritdoc IAccessControlDefaultAdminRules
|
||||
function defaultAdmin() public view virtual returns (address) {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
return $._currentDefaultAdmin;
|
||||
}
|
||||
|
||||
/// @inheritdoc IAccessControlDefaultAdminRules
|
||||
function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
return ($._pendingDefaultAdmin, $._pendingDefaultAdminSchedule);
|
||||
}
|
||||
|
||||
/// @inheritdoc IAccessControlDefaultAdminRules
|
||||
function defaultAdminDelay() public view virtual returns (uint48) {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
uint48 schedule = $._pendingDelaySchedule;
|
||||
return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? $._pendingDelay : $._currentDelay;
|
||||
}
|
||||
|
||||
/// @inheritdoc IAccessControlDefaultAdminRules
|
||||
function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
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();
|
||||
if (_msgSender() != newDefaultAdmin) {
|
||||
// Enforce newDefaultAdmin explicit acceptance.
|
||||
revert AccessControlInvalidDefaultAdmin(_msgSender());
|
||||
}
|
||||
_acceptDefaultAdminTransfer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev See {acceptDefaultAdminTransfer}.
|
||||
*
|
||||
* Internal function without access restriction.
|
||||
*/
|
||||
function _acceptDefaultAdminTransfer() internal virtual {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
(address newAdmin, uint48 schedule) = pendingDefaultAdmin();
|
||||
if (!_isScheduleSet(schedule) || !_hasSchedulePassed(schedule)) {
|
||||
revert AccessControlEnforcedDefaultAdminDelay(schedule);
|
||||
}
|
||||
_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 {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
(, 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 {
|
||||
AccessControlDefaultAdminRulesStorage storage $ = _getAccessControlDefaultAdminRulesStorage();
|
||||
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 a `schedule` is considered set. For consistency purposes.
|
||||
*/
|
||||
function _isScheduleSet(uint48 schedule) private pure returns (bool) {
|
||||
return schedule != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Defines if a `schedule` is considered passed. For consistency purposes.
|
||||
*/
|
||||
function _hasSchedulePassed(uint48 schedule) private view returns (bool) {
|
||||
return schedule < block.timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user