Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/vaults/KYCWhitelist.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {AccessControlDefaultAdminRulesUpgradeable} from "openzeppelin-contracts-upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol";
import {EIP712Upgradeable} from "openzeppelin-contracts-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import {UUPSUpgradeable} from "openzeppelin-contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {ECDSA} from "openzeppelin-contracts/utils/cryptography/ECDSA.sol";
import {MessageHashUtils} from "openzeppelin-contracts/utils/cryptography/MessageHashUtils.sol";
contract KYCWhitelist is AccessControlDefaultAdminRulesUpgradeable, EIP712Upgradeable, UUPSUpgradeable {
using MessageHashUtils for bytes32;
using ECDSA for bytes32;
error AlreadyWhitelisted();
error AlreadyBlacklisted();
error NonceAlreadyUsed();
error SignatureExpired();
enum ActionState {
CLOSED,
OPEN,
WHITELIST
}
struct Vault {
bool isActive;
ActionState depositsState;
ActionState withdrawalsState;
}
uint48 internal constant DEFAULT_ADMIN_DELAY = 1 days;
bytes internal constant WHITELISTING_TYPE =
"Whitelisting(address vault,address account,uint256 expiration,uint256 nonce)";
bytes32 internal constant WHITELISTING_TYPEHASH = keccak256(WHITELISTING_TYPE);
bytes32 public constant SIGNER_ROLE = keccak256("SIGNER_ROLE");
mapping(address => Vault) public vaults;
mapping(address => mapping(address => bool)) public isUserWhitelisted;
mapping(address => mapping(address => bool)) public isUserBlacklisted;
mapping(uint256 => bool) public isUsedNonce;
constructor() {
_disableInitializers();
}
function initialize(address[] memory signers) public initializer {
__AccessControlDefaultAdminRules_init(DEFAULT_ADMIN_DELAY, msg.sender);
__EIP712_init("BracketVaults KYC", "1");
for (uint256 i = 0; i < signers.length; i++) {
_grantRole(SIGNER_ROLE, signers[i]);
}
}
function addToWhitelist(address vault, uint256 deadline, uint256 nonce, bytes memory signature) external {
if (block.timestamp > deadline) revert SignatureExpired();
_useNonce(nonce);
bytes32 digest = hashWhitelisting(vault, msg.sender, deadline, nonce);
address signer = digest.recover(signature);
_checkRole(SIGNER_ROLE, signer);
_whitelistUser(vault, msg.sender);
}
function addToWhitelist(address vault, address[] memory addresses) external onlyRole(SIGNER_ROLE) {
for (uint256 i = 0; i < addresses.length; i++) {
_whitelistUser(vault, addresses[i]);
}
}
function addToBlacklist(address vault, address[] memory addresses) external onlyRole(SIGNER_ROLE) {
for (uint256 i = 0; i < addresses.length; i++) {
_blacklistUser(vault, addresses[i]);
}
}
function removeFromWhitelist(address vault, address[] memory addresses) external onlyRole(SIGNER_ROLE) {
for (uint256 i = 0; i < addresses.length; i++) {
isUserWhitelisted[vault][addresses[i]] = false;
}
}
function removeFromBlacklist(address vault, address[] memory addresses) external onlyRole(SIGNER_ROLE) {
for (uint256 i = 0; i < addresses.length; i++) {
isUserBlacklisted[vault][addresses[i]] = false;
}
}
function setVaultState(address vault, bool isActive, ActionState depositsState, ActionState withdrawalsState) external onlyRole(DEFAULT_ADMIN_ROLE) {
vaults[vault].isActive = isActive;
vaults[vault].depositsState = depositsState;
vaults[vault].withdrawalsState = withdrawalsState;
}
function canDeposit(address vaultAddr, address user) external view returns (bool) {
Vault memory vault = vaults[vaultAddr];
if (!vault.isActive) return false;
if (vault.depositsState == ActionState.OPEN) return true;
if (vault.depositsState == ActionState.WHITELIST) {
// If the deposits are for whitelisted users, and the user is whitelisted, return true
if (isUserWhitelisted[vaultAddr][user]) return true;
// If the deposits are for whitelisted users, and the user is not whitelisted, return false
return false;
}
return false;
}
function canWithdraw(address vaultAddr, address user) external view returns (bool) {
Vault memory vault = vaults[vaultAddr];
if (!vault.isActive) return false;
if (vault.withdrawalsState == ActionState.OPEN) return true;
if (vault.withdrawalsState == ActionState.WHITELIST) {
// If the withdrawals are for whitelisted users, and the user is whitelisted, return true
if (isUserWhitelisted[vaultAddr][user]) return true;
// If the withdrawals are for whitelisted users, and the user is not whitelisted, return false
return false;
}
return false;
}
function canTransfer(address vaultAddr, address from, address to) external view returns (bool) {
// If from or to a blacklisted address return false
if (isUserBlacklisted[vaultAddr][from] || isUserBlacklisted[vaultAddr][to]) return false;
// For everything else return true
return true;
}
function hashWhitelisting(address vault, address account, uint256 expiration, uint256 nonce) public view returns (bytes32) {
return _hashTypedDataV4(
keccak256(
abi.encode(
WHITELISTING_TYPEHASH,
vault,
account,
expiration,
nonce
)
)
);
}
function _whitelistUser(address vault, address account) internal {
if (isUserWhitelisted[vault][account]) revert AlreadyWhitelisted();
isUserWhitelisted[vault][account] = true;
}
function _blacklistUser(address vault, address account) internal {
if (isUserBlacklisted[vault][account]) revert AlreadyBlacklisted();
isUserBlacklisted[vault][account] = true;
}
function _useNonce(uint256 nonce) internal {
if (isUsedNonce[nonce]) revert NonceAlreadyUsed();
isUsedNonce[nonce] = true;
}
function _authorizeUpgrade(address newImplementation) internal override onlyRole(DEFAULT_ADMIN_ROLE) {}
}"
},
"lib/openzeppelin-contracts-upgradeable/contracts/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 {Initializable} from "../../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}.
* * 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);
}
/**
* @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(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);
}
/**
* @dev See {AccessControl-_revokeRole}.
*/
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 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;
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/EIP712Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*/
abstract contract EIP712Upgradeable is Initializable, IERC5267 {
bytes32 private constant TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/// @custom:storage-location erc7201:openzeppelin.storage.EIP712
struct EIP712Storage {
/// @custom:oz-renamed-from _HASHED_NAME
bytes32 _hashedName;
/// @custom:oz-renamed-from _HASHED_VERSION
bytes32 _hashedVersion;
string _name;
string _version;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.EIP712")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100;
function _getEIP712Storage() private pure returns (EIP712Storage storage $) {
assembly {
$.slot := EIP712StorageLocation
}
}
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
__EIP712_init_unchained(name, version);
}
function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
EIP712Storage storage $ = _getEIP712Storage();
$._name = name;
$._version = version;
// Reset prior values in storage if upgrading
$._hashedName = 0;
$._hashedVersion = 0;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator();
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {IERC-5267}.
*/
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
EIP712Storage storage $ = _getEIP712Storage();
// If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized
// and the EIP712 domain is not reliable, as it will be missing name and version.
require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized");
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712Name() internal view virtual returns (string memory) {
EIP712Storage storage $ = _getEIP712Storage();
return $._name;
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712Version() internal view virtual returns (string memory) {
EIP712Storage storage $ = _getEIP712Storage();
return $._version;
}
/**
* @dev The hash of the name parameter for the EIP712 domain.
*
* NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead.
*/
function _EIP712NameHash() internal view returns (bytes32) {
EIP712Storage storage $ = _getEIP712Storage();
string memory name = _EIP712Name();
if (bytes(name).length > 0) {
return keccak256(bytes(name));
} else {
// If the name is empty, the contract may have been upgraded without initializing the new storage.
// We return the name hash in storage if non-zero, otherwise we assume the name is empty by design.
bytes32 hashedName = $._hashedName;
if (hashedName != 0) {
return hashedName;
} else {
return keccak256("");
}
}
}
/**
* @dev The hash of the version parameter for the EIP712 domain.
*
* NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead.
*/
function _EIP712VersionHash() internal view returns (bytes32) {
EIP712Storage storage $ = _getEIP712Storage();
string memory version = _EIP712Version();
if (bytes(version).length > 0) {
return keccak256(bytes(version));
} else {
// If the version is empty, the contract may have been upgraded without initializing the new storage.
// We return the version hash in storage if non-zero, otherwise we assume the version is empty by design.
bytes32 hashedVersion = $._hashedVersion;
if (hashedVersion != 0) {
return hashedVersion;
} else {
return keccak256("");
}
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/cryptography/MessageHashUtils.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\
32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\
32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\
" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\
", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/access/extensions/IAccessControlDefaultAdminRules.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/extensions/IAccessControlDefaultAdminRules.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "../IAccessControl.sol";
/**
* @dev External interface of AccessControlDefaultAdminRules declared to support ERC165 detection.
*/
interface IAccessControlDefaultAdminRules is IAccessControl {
/**
* @dev The new default admin is not a valid default admin.
*/
error AccessControlInvalidDefaultAdmin(address defaultAdmin);
/**
* @dev At least one of the following rules was violated:
*
* - The `DEFAULT_ADMIN_ROLE` must only be managed by itself.
* - The `DEFAULT_ADMIN_ROLE` must only be held by one account at the time.
* - Any `DEFAULT_ADMIN_ROLE` transfer must be in two delayed steps.
*/
error AccessControlEnforcedDefaultAdminRules();
/**
* @dev The delay for transferring the default admin delay is enforced and
* the operation must wait until `schedule`.
*
* NOTE: `schedule` can be 0 indicating there's no transfer scheduled.
*/
error AccessControlEnforcedDefaultAdminDelay(uint48 schedule);
/**
* @dev Emitted when a {defaultAdmin} transfer is started, setting `newAdmin` as the next
* address to become the {defaultAdmin} by calling {acceptDefaultAdminTransfer} only after `acceptSchedule`
* passes.
*/
event DefaultAdminTransferScheduled(address indexed newAdmin, uint48 acceptSchedule);
/**
* @dev Emitted when a {pendingDefaultAdmin} is reset if it was never accepted, regardless of its schedule.
*/
event DefaultAdminTransferCanceled();
/**
* @dev Emitted when a {defaultAdminDelay} change is started, setting `newDelay` as the next
* delay to be applied between default admin transfer after `effectSchedule` has passed.
*/
event DefaultAdminDelayChangeScheduled(uint48 newDelay, uint48 effectSchedule);
/**
* @dev Emitted when a {pendingDefaultAdminDelay} is reset if its schedule didn't pass.
*/
event DefaultAdminDelayChangeCanceled();
/**
* @dev Returns the address of the current `DEFAULT_ADMIN_ROLE` holder.
*/
function defaultAdmin() external view returns (address);
/**
* @dev Returns a tuple of a `newAdmin` and an accept schedule.
*
* After the `schedule` passes, the `newAdmin` will be able to accept the {defaultAdmin} role
* by calling {acceptDefaultAdminTransfer}, completing the role transfer.
*
* A zero value only in `acceptSchedule` indicates no pending admin transfer.
*
* NOTE: A zero address `newAdmin` means that {defaultAdmin} is being renounced.
*/
function pendingDefaultAdmin() external view returns (address newAdmin, uint48 acceptSchedule);
/**
* @dev Returns the delay required to schedule the acceptance of a {defaultAdmin} transfer started.
*
* This delay will be added to the current timestamp when calling {beginDefaultAdminTransfer} to set
* the acceptance schedule.
*
* NOTE: If a delay change has been scheduled, it will take effect as soon as the schedule passes, making this
* function returns the new delay. See {changeDefaultAdminDelay}.
*/
function defaultAdminDelay() external view returns (uint48);
/**
* @dev Returns a tuple of `newDelay` and an effect schedule.
*
* After the `schedule` passes, the `newDelay` will get into effect immediately for every
* new {defaultAdmin} transfer started with {beginDefaultAdminTransfer}.
*
* A zero value only in `effectSchedule` indicates no pending delay change.
*
* NOTE: A zero value only for `newDelay` means that the next {defaultAdminDelay}
* will be zero after the effect schedule.
*/
function pendingDefaultAdminDelay() external view returns (uint48 newDelay, uint48 effectSchedule);
/**
* @dev Starts a {defaultAdmin} transfer by setting a {pendingDefaultAdmin} scheduled for acceptance
* after the current timestamp plus a {defaultAdminDelay}.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* Emits a DefaultAdminRoleChangeStarted event.
*/
function beginDefaultAdminTransfer(address newAdmin) external;
/**
* @dev Cancels a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
*
* A {pendingDefaultAdmin} not yet accepted can also be cancelled with this function.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* May emit a DefaultAdminTransferCanceled event.
*/
function cancelDefaultAdminTransfer() external;
/**
* @dev Completes a {defaultAdmin} transfer previously started with {beginDefaultAdminTransfer}.
*
* After calling the function:
*
* - `DEFAULT_ADMIN_ROLE` should be granted to the caller.
* - `DEFAULT_ADMIN_ROLE` should be revoked from the previous holder.
* - {pendingDefaultAdmin} should be reset to zero values.
*
* Requirements:
*
* - Only can be called by the {pendingDefaultAdmin}'s `newAdmin`.
* - The {pendingDefaultAdmin}'s `acceptSchedule` should've passed.
*/
function acceptDefaultAdminTransfer() external;
/**
* @dev Initiates a {defaultAdminDelay} update by setting a {pendingDefaultAdminDelay} scheduled for getting
* into effect after the current timestamp plus a {defaultAdminDelay}.
*
* This function guarantees that any call to {beginDefaultAdminTransfer} done between the timestamp this
* method is called and the {pendingDefaultAdminDelay} effect schedule will use the current {defaultAdminDelay}
* set before calling.
*
* The {pendingDefaultAdminDelay}'s effect schedule is defined in a way that waiting until the schedule and then
* calling {beginDefaultAdminTransfer} with the new delay will take at least the same as another {defaultAdmin}
* complete transfer (including acceptance).
*
* The schedule is designed for two scenarios:
*
* - When the delay is changed for a larger one the schedule is `block.timestamp + newDelay` capped by
* {defaultAdminDelayIncreaseWait}.
* - When the delay is changed for a shorter one, the schedule is `block.timestamp + (current delay - new delay)`.
*
* A {pendingDefaultAdminDelay} that never got into effect will be canceled in favor of a new scheduled change.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* Emits a DefaultAdminDelayChangeScheduled event and may emit a DefaultAdminDelayChangeCanceled event.
*/
function changeDefaultAdminDelay(uint48 newDelay) external;
/**
* @dev Cancels a scheduled {defaultAdminDelay} change.
*
* Requirements:
*
* - Only can be called by the current {defaultAdmin}.
*
* May emit a DefaultAdminDelayChangeCanceled event.
*/
function rollbackDefaultAdminDelay() external;
/**
* @dev Maximum time in seconds for an increase to {defaultAdminDelay} (that is scheduled using {changeDefaultAdminDelay})
* to take effect. Default to 5 days.
*
* When the {defaultAdminDelay} is scheduled to be increased, it goes into effect after the new delay has passed with
* the purpose of giving enough time for reverting any accidental change (i.e. using milliseconds instead of seconds)
* that may lock the contract. However, to avoid excessive schedules, the wait is capped by this function and it can
* be overrode for a custom {defaultAdminDelay} increase scheduling.
*
* IMPORTANT: Make sure to add a reasonable amount of time while overriding this value, otherwise,
* there's a risk of setting a high new delay that goes into effect almost immediately without the
* possibility of human intervention in the case of an input error (eg. set milliseconds instead of seconds).
*/
function defaultAdminDelayIncreaseWait() external view returns (uint48);
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 "../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 {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
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());
}
Submitted on: 2025-09-19 19:13:21
Comments
Log in to comment.
No comments yet.