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": {
"contracts/SupplyControl.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { AccessControlDefaultAdminRulesUpgradeable } from "./openzeppelin/contracts-upgradeable/access/AccessControlDefaultAdminRulesUpgradeable.sol";
import { UUPSUpgradeable } from "./openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { IAccessControl } from "./openzeppelin/contracts/access/IAccessControl.sol";
import { EnumerableSet } from "./openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { DoubleEndedQueue } from "./openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol";
import { PaxosBaseAbstract } from "./lib/PaxosBaseAbstract.sol";
import { RateLimit } from "./lib/RateLimit.sol";
/**
* @title SupplyControl
* @dev control the token supply. The `SUPPLY_CONTROLLER_MANAGER_ROLE` role is responsible for managing
* addresses with the `SUPPLY_CONTROLLER_ROLE`, referred to as supplyControllers. Only supplyControllers can
* mint and burn tokens. SupplyControllers can optionally have rate limits to limit how many tokens can be
* minted over a given time frame.
* @custom:security-contact smart-contract-security@paxos.com
*/
contract SupplyControl is AccessControlDefaultAdminRulesUpgradeable, UUPSUpgradeable {
using EnumerableSet for EnumerableSet.AddressSet;
// Access control roles
// keccak256("SUPPLY_CONTROLLER_MANAGER_ROLE")
// Can add, update, and remove `SupplyController`s
bytes32 public constant SUPPLY_CONTROLLER_MANAGER_ROLE =
0x5d3e9f1ecbcdad7b0da30e7d29c9eddaef83a4502dafe3d2dd85cfdb12e4af10;
// keccak256("SUPPLY_CONTROLLER_ROLE")
// Can mint/burn tokens
bytes32 public constant SUPPLY_CONTROLLER_ROLE = 0x9c00d6f280439b1dfa4da90321e0a3f3c2e87280f4d07fea9fa43ff2cf02df2b;
// keccak256("TOKEN_CONTRACT_ROLE")
// Tracks the token contract to protect functions which impact rate limits
bytes32 public constant TOKEN_CONTRACT_ROLE = 0xd32fd1ee5f4f111da6f27444787e5200ec57a8849509c00ef2998467052b32a3;
// SUPPLY CONTROL DATA
mapping(address => SupplyController) internal supplyControllerMap;
//Used to get all supply controllers
EnumerableSet.AddressSet internal supplyControllerSet;
uint256[35] private __gap_SC; // solhint-disable-line var-name-mixedcase
/**
* @dev Struct defines a supply controller. Different supply controllers can have different rules.
* @param rateLimit Contract which handles rate limit logic
* @param mintAddressWhitelist Addresses the {SupplyController} can mint to
* @param allowAnyMintAndBurnAddress If true, allows the supply controller to mint to and burn from any address
*/
struct SupplyController {
RateLimit.Storage rateLimitStorage;
EnumerableSet.AddressSet mintAddressWhitelist;
bool allowAnyMintAndBurnAddress;
}
/**
* @dev Struct defines the configuration needed when creating a new supply controller.
* @param newSupplyController Address of the new supply controller
* @param limitConfig Limit configuration
* @param mintAddressWhitelist Addresses the supply controller can mint to
* @param allowAnyMintAndBurnAddress If true, allows the supply controller to mint to and burn from any address
*/
struct SupplyControllerInitialization {
address newSupplyController;
RateLimit.LimitConfig limitConfig;
address[] mintAddressWhitelist;
bool allowAnyMintAndBurnAddress;
}
/**
* @dev Emitted when {addSupplyController} is called.
* @param newSupplyController Address of the new supply controller
* @param limitCapacity Max amount for the rate limit. Checked in `_checkCurrentPeriodAmount`
* @param refillPerSecond Amount to add to limit each second up to the `limitCapacity`
* @param mintAddressWhitelist Addresses the supply controller can mint to
* @param allowAnyMintAndBurnAddress If true, allows the supply controller to mint to and burn from any address
*/
event SupplyControllerAdded(
address indexed newSupplyController,
uint256 limitCapacity,
uint256 refillPerSecond,
address[] mintAddressWhitelist,
bool allowAnyMintAndBurnAddress
);
/**
* @dev Emitted when {removeSupplyController} is called.
* @param oldSupplyController The old supply controller address
*/
event SupplyControllerRemoved(address indexed oldSupplyController);
/**
* @dev Emitted when limit configuration is updated for `supplyController`.
* Occurs when {updateLimitConfig} is called.
* @param supplyController Supply controller address
* @param newLimitConfig New limit configuration
* @param oldLimitConfig Old limit configuration
*/
event LimitConfigUpdated(
address indexed supplyController,
RateLimit.LimitConfig newLimitConfig,
RateLimit.LimitConfig oldLimitConfig
);
/**
* @dev Emitted when `allowAnyMintAndBurnAddress` is updated for `supplyController`.
* Occurs when {updateAllowAnyMintAndBurnAddress} is called.
* @param supplyController Supply controller address
* @param newAllowAnyMintAndBurnAddress New allow config
* @param oldAllowAnyMintAndBurnAddress Old allow config
*/
event AllowAnyMintAndBurnAddressUpdated(
address indexed supplyController,
bool newAllowAnyMintAndBurnAddress,
bool oldAllowAnyMintAndBurnAddress
);
/**
* @dev Emitted when `mintAddress` is added to `mintAddressWhitelist` in `supplyController`.
* Occurs when {addMintAddressToWhitelist} is called
* @param supplyController Supply controller address
* @param mintAddress New address which can be minted to
*/
event MintAddressAddedToWhitelist(address indexed supplyController, address indexed mintAddress);
/**
* @dev Emitted when `mintAddress` is removed from `mintAddressWhitelist` in `supplyController`.
* Occurs when {removeMintAddressFromWhitelist} is called
* @param supplyController Supply controller address
* @param mintAddress Address which can no longer be minted to
*/
event MintAddressRemovedFromWhitelist(address indexed supplyController, address indexed mintAddress);
error AccountMissingSupplyControllerRole(address account);
error AccountAlreadyHasSupplyControllerRole(address account);
error CannotMintToAddress(address supplyController, address mintToAddress);
error CannotBurnFromAddress(address supplyController, address burnFromAddress);
error CannotAddDuplicateAddress(address addressToAdd);
error CannotRemoveNonExistantAddress(address addressToRemove);
error ZeroAddress();
/**
* @dev Modifier which checks that the specified `supplyController` address has the SUPPLY_CONTROLLER_ROLE
* @param supplyController Supply controller address
*/
modifier onlySupplyController(address supplyController) {
if (!hasRole(SUPPLY_CONTROLLER_ROLE, supplyController)) {
revert AccountMissingSupplyControllerRole(supplyController);
}
_;
}
/**
* @dev Modifier which checks that the specified `supplyController` address does not have the SUPPLY_CONTROLLER_ROLE
* @param supplyController Supply controller address
*/
modifier notSupplyController(address supplyController) {
if (hasRole(SUPPLY_CONTROLLER_ROLE, supplyController)) {
revert AccountAlreadyHasSupplyControllerRole(supplyController);
}
_;
}
/**
* @dev Modifier to check for zero address.
*/
modifier isNonZeroAddress(address addr) {
if (addr == address(0)) {
revert ZeroAddress();
}
_;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @dev Initializer for SupplyControl.
* Proper order of setting up the contracts:
* 1. Deploy/reinitialize PaxosToken
* 2. Deploy SupplyControl with `SupplyControllerInitialization` config
* 3. Set SupplyControl address in PaxosToken via `setSupplyControl`
* @param initialOwner Initial owner address
* @param supplyControllerManager SupplyControllerManager address
* @param tokenAddress Token contract address
* @param scInitializationConfig Configuration to initialize a list of supply controllers
*/
function initialize(
address initialOwner,
address supplyControllerManager,
address tokenAddress,
SupplyControllerInitialization[] calldata scInitializationConfig
) external initializer isNonZeroAddress(supplyControllerManager) isNonZeroAddress(tokenAddress) {
__AccessControlDefaultAdminRules_init(3 hours, initialOwner);
__UUPSUpgradeable_init();
_grantRole(SUPPLY_CONTROLLER_MANAGER_ROLE, supplyControllerManager);
_grantRole(TOKEN_CONTRACT_ROLE, tokenAddress);
for (uint256 i = 0; i < scInitializationConfig.length; ) {
_addSupplyController(scInitializationConfig[i]);
unchecked {
i++;
}
}
}
/**
* @dev Adds a new supply controller which can be used to control the supply of a token.
* Can be called externally by the `SUPPLY_CONTROLLER_MANAGER_ROLE`.
* @param newSupplyController Address of the new supply controller
* @param limitCapacity Max amount for the rate limit.
* @param refillPerSecond Amount to add to limit each second up to the `limitCapacity`
* @param mintAddressWhitelist Addresses the supply controller can mint to
* @param allowAnyMintAndBurnAddress If true, allows the supply controller to mint to and burn from any address
*/
function addSupplyController(
address newSupplyController,
uint256 limitCapacity,
uint256 refillPerSecond,
address[] memory mintAddressWhitelist,
bool allowAnyMintAndBurnAddress
) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) {
RateLimit.LimitConfig memory limitConfig = RateLimit.LimitConfig(limitCapacity, refillPerSecond);
SupplyControllerInitialization memory scInitializationConfig = SupplyControllerInitialization(
newSupplyController,
limitConfig,
mintAddressWhitelist,
allowAnyMintAndBurnAddress
);
_addSupplyController(scInitializationConfig);
}
/**
* @dev Removes `oldSupplyController`
* @param oldSupplyController The old supply controller address
*/
function removeSupplyController(
address oldSupplyController
) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(oldSupplyController) {
_revokeRole(SUPPLY_CONTROLLER_ROLE, oldSupplyController);
SupplyController storage supplyController = supplyControllerMap[oldSupplyController];
_removeAddressSet(supplyController.mintAddressWhitelist);
EnumerableSet.remove(supplyControllerSet, oldSupplyController);
delete supplyControllerMap[oldSupplyController];
emit SupplyControllerRemoved(oldSupplyController);
}
/**
* Update limit configuration
* @param supplyController_ Supply controller address.
* @param limitCapacity Max amount for the rate limit
* @param refillPerSecond Amount to add to limit each second up to the `limitCapacity`
*/
function updateLimitConfig(
address supplyController_,
uint256 limitCapacity,
uint256 refillPerSecond
) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(supplyController_) {
RateLimit.LimitConfig memory limitConfig = RateLimit.LimitConfig(limitCapacity, refillPerSecond);
SupplyController storage supplyController = supplyControllerMap[supplyController_];
RateLimit.LimitConfig memory oldLimitConfig = supplyController.rateLimitStorage.limitConfig;
supplyController.rateLimitStorage.limitConfig = limitConfig;
emit LimitConfigUpdated(supplyController_, limitConfig, oldLimitConfig);
}
function updateAllowAnyMintAndBurnAddress(
address supplyController_,
bool allowAnyMintAndBurnAddress
) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(supplyController_) {
SupplyController storage supplyController = supplyControllerMap[supplyController_];
bool oldAllowValue = supplyController.allowAnyMintAndBurnAddress;
supplyController.allowAnyMintAndBurnAddress = allowAnyMintAndBurnAddress;
emit AllowAnyMintAndBurnAddressUpdated(supplyController_, allowAnyMintAndBurnAddress, oldAllowValue);
}
/**
* @dev Adds `mintAddress` to `mintAddressWhitelist` in `supplyController`.
* @param supplyController_ Supply controller address
* @param mintAddress Address which can be minted to
*/
function addMintAddressToWhitelist(
address supplyController_,
address mintAddress
) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(supplyController_) {
SupplyController storage supplyController = supplyControllerMap[supplyController_];
if (EnumerableSet.contains(supplyController.mintAddressWhitelist, mintAddress)) {
revert CannotAddDuplicateAddress(mintAddress);
}
EnumerableSet.add(supplyController.mintAddressWhitelist, mintAddress);
emit MintAddressAddedToWhitelist(supplyController_, mintAddress);
}
/**
* @dev Removes `mintAddress` from `mintAddressWhitelist` in `supplyController`.
* @param supplyController_ Supply controller address
* @param mintAddress Address which can no longer be minted to
*/
function removeMintAddressFromWhitelist(
address supplyController_,
address mintAddress
) external onlyRole(SUPPLY_CONTROLLER_MANAGER_ROLE) onlySupplyController(supplyController_) {
SupplyController storage supplyController = supplyControllerMap[supplyController_];
if (!EnumerableSet.contains(supplyController.mintAddressWhitelist, mintAddress)) {
revert CannotRemoveNonExistantAddress(mintAddress);
}
EnumerableSet.remove(supplyController.mintAddressWhitelist, mintAddress);
emit MintAddressRemovedFromWhitelist(supplyController_, mintAddress);
}
/**
* @dev Gets supply controller configuration
* @param supplyController_ Supply controller address
*/
function getSupplyControllerConfig(
address supplyController_
)
external
view
onlySupplyController(supplyController_)
returns (
RateLimit.LimitConfig memory limitConfig,
address[] memory mintAddressWhitelist,
bool allowAnyMintAndBurnAddress
)
{
SupplyController storage supplyController = supplyControllerMap[supplyController_];
RateLimit.LimitConfig memory limitConfig_ = supplyController.rateLimitStorage.limitConfig;
address[] memory mintAddressWhitelist_ = EnumerableSet.values(
supplyControllerMap[supplyController_].mintAddressWhitelist
);
return (limitConfig_, mintAddressWhitelist_, supplyController.allowAnyMintAndBurnAddress);
}
/**
* @dev Gets all supply controller addresses
*/
function getAllSupplyControllerAddresses() external view returns (address[] memory) {
return EnumerableSet.values(supplyControllerSet);
}
/**
* @dev Get remaining amount which can be minted at `timestamp`
* @param supplyController_ Supply controller address
* @param timestamp Time to check remaining amount for
*/
function getRemainingMintAmount(
address supplyController_,
uint256 timestamp
) external view onlySupplyController(supplyController_) returns (uint256) {
SupplyController storage supplyController = supplyControllerMap[supplyController_];
RateLimit.Storage storage limitStorage = supplyController.rateLimitStorage;
return RateLimit.getRemainingAmount(timestamp, limitStorage);
}
/**
* @dev Function which checks that `mintToAddress` is in the whitelisted map for msg.sender
* and the amount does not exceed the rate limit
* @param mintToAddress Mint to address
* @param amount Amount to check
* @param sender Supply controller address
*/
function canMintToAddress(
address mintToAddress,
uint256 amount,
address sender
) external onlySupplyController(sender) onlyRole(TOKEN_CONTRACT_ROLE) {
SupplyController storage supplyController = supplyControllerMap[sender];
if (
!supplyController.allowAnyMintAndBurnAddress &&
!EnumerableSet.contains(supplyController.mintAddressWhitelist, mintToAddress)
) {
revert CannotMintToAddress(sender, mintToAddress);
}
RateLimit.Storage storage limitStorage = supplyController.rateLimitStorage;
RateLimit.checkNewEvent(block.timestamp, amount, limitStorage);
}
/**
* @dev Function which checks that `burnFromAddress` is the 'sender' or that the 'sender' is allowed to burn
* from any address.
* Also checks that the `sender` is a supply controller since only a supply controller can burn tokens.
* @param burnFromAddress Burn from address
* @param sender Supply controller address
*/
function canBurnFromAddress(address burnFromAddress, address sender) external view onlySupplyController(sender) {
SupplyController storage supplyController = supplyControllerMap[sender];
if (!supplyController.allowAnyMintAndBurnAddress && sender != burnFromAddress) {
revert CannotBurnFromAddress(sender, burnFromAddress);
}
}
/**
* @dev Adds a new supply controller which can be used to control the supply of a token.
* Can only be called internally.
* @param scInitializationConfig Configuration to setup a new supply controller
*/
function _addSupplyController(
SupplyControllerInitialization memory scInitializationConfig
)
internal
notSupplyController(scInitializationConfig.newSupplyController)
isNonZeroAddress(scInitializationConfig.newSupplyController)
{
SupplyController storage supplyController = supplyControllerMap[scInitializationConfig.newSupplyController];
supplyController.rateLimitStorage.limitConfig = scInitializationConfig.limitConfig;
supplyController.allowAnyMintAndBurnAddress = scInitializationConfig.allowAnyMintAndBurnAddress;
_addressArrayToSet(scInitializationConfig.mintAddressWhitelist, supplyController.mintAddressWhitelist);
_grantRole(SUPPLY_CONTROLLER_ROLE, scInitializationConfig.newSupplyController);
EnumerableSet.add(supplyControllerSet, scInitializationConfig.newSupplyController);
emit SupplyControllerAdded(
scInitializationConfig.newSupplyController,
scInitializationConfig.limitConfig.limitCapacity,
scInitializationConfig.limitConfig.refillPerSecond,
scInitializationConfig.mintAddressWhitelist,
scInitializationConfig.allowAnyMintAndBurnAddress
);
}
/**
* @dev required by the OZ UUPS module to authorize an upgrade
* of the contract. Restricted to DEFAULT_ADMIN_ROLE.
*/
function _authorizeUpgrade(address) internal override onlyRole(DEFAULT_ADMIN_ROLE) {} // solhint-disable-line no-empty-blocks
/**
* @dev Helper function for setting `mintAddressWhitelist`
* @param addressArray Array containing mint addresses
* @param addressSet Set which addresses should be added to
*/
function _addressArrayToSet(address[] memory addressArray, EnumerableSet.AddressSet storage addressSet) private {
for (uint256 i = 0; i < addressArray.length; ) {
EnumerableSet.add(addressSet, addressArray[i]);
unchecked {
i++;
}
}
}
/**
* @dev Helper function for removing all addresses from `mintAddressWhitelist`
* Removes elements in reverse order to reduce array reordering and improve gas efficiency
* @param addressSet Set of addresses
*/
function _removeAddressSet(EnumerableSet.AddressSet storage addressSet) private {
uint256 length = EnumerableSet.length(addressSet);
for (uint256 i = length; i > 0; ) {
unchecked {
i--;
}
EnumerableSet.remove(addressSet, EnumerableSet.at(addressSet, i));
}
}
}
"
},
"contracts/lib/RateLimit.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import { DoubleEndedQueue } from "@openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "hardhat/console.sol";
/**
* @title RateLimit library
* @dev Performs rate limiting using an elapsed time algorithm
* @custom:security-contact smart-contract-security@paxos.com
*/
library RateLimit {
uint256 constant SKIP_RATE_LIMIT_CHECK = 0;
struct Storage {
// Limit configuration
LimitConfig limitConfig;
// Remaining amount for the time period
uint256 remainingAmount;
//Timestamp of last event
uint256 lastRefillTime;
}
struct LimitConfig {
// Max amount for the rate limit
uint256 limitCapacity;
// Amount to add to limit each second up to the limitCapacity
uint256 refillPerSecond;
}
error RateLimitExceeded();
error OldTimestamp(uint256 timestamp, uint256 expected);
/**
* @dev Uses an elapsed time algorithm to determine if the new event is allowed or not.
* High level steps:
* 1. Calculate elapsed time since last event
* 2. Calculate amount that can be sent at the current `timestamp`
* 3. Check if rate limit is exceeded or not and update remaining amount
*
* @param timestamp Timestamp of the new event
* @param amount Amount of the new event
* @param limitStorage Storage data specific to the rate limit check
*/
function checkNewEvent(uint256 timestamp, uint256 amount, Storage storage limitStorage) internal {
//Limit time period of 0 is a special value indicating we should skip rate limit checking
if (limitStorage.limitConfig.refillPerSecond == SKIP_RATE_LIMIT_CHECK) {
return;
}
limitStorage.remainingAmount = refill(timestamp, limitStorage);
limitStorage.lastRefillTime = timestamp;
if (amount > limitStorage.remainingAmount) {
revert RateLimitExceeded();
}
limitStorage.remainingAmount -= amount;
}
/**
* @dev Gets remaining amount that can be sent in the window
* @param timestamp Timestamp to check remaining amount for
* @param limitStorage Storage data specific to the rate limit check
*/
function getRemainingAmount(uint256 timestamp, Storage storage limitStorage) internal view returns (uint256) {
// Limit time period of 0 is a special value indicating we should skip rate limit checking
if (limitStorage.limitConfig.refillPerSecond == SKIP_RATE_LIMIT_CHECK) {
return type(uint256).max;
}
return refill(timestamp, limitStorage);
}
/**
* @dev Refills the amount based on the elapsed time from the previous event.
* `timestamp` cannot be older than the `lastRefillTime`.
* @param timestamp Timestamp of the new event
* @param limitStorage Storage data specific to the rate limit check
*/
function refill(uint256 timestamp, Storage storage limitStorage) private view returns (uint256) {
if (limitStorage.lastRefillTime > timestamp) {
revert OldTimestamp(timestamp, limitStorage.lastRefillTime);
}
uint256 secondsElapsed = timestamp - limitStorage.lastRefillTime;
(bool safeMul, uint256 newTokens) = SafeMath.tryMul(secondsElapsed, limitStorage.limitConfig.refillPerSecond);
(bool safeAdd, uint256 amount) = SafeMath.tryAdd(limitStorage.remainingAmount, newTokens);
if (!safeMul || !safeAdd) {
return limitStorage.limitConfig.limitCapacity;
}
return Math.min(limitStorage.limitConfig.limitCapacity, amount);
}
}
"
},
"contracts/lib/PaxosBaseAbstract.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
* @title PaxosBaseAbstract contract
* @dev An abstract contract for Paxos tokens with additional internal functions.
* @custom:security-contact smart-contract-security@paxos.com
*/
abstract contract PaxosBaseAbstract {
// keccak256("PAUSE_ROLE")
bytes32 public constant PAUSE_ROLE = 0x139c2898040ef16910dc9f44dc697df79363da767d8bc92f2e310312b816e46d;
// keccak256("ASSET_PROTECTION_ROLE")
bytes32 public constant ASSET_PROTECTION_ROLE = 0xe3e4f9d7569515307c0cdec302af069a93c9e33f325269bac70e6e22465a9796;
// All base errors.
error ZeroAddress();
error ContractPaused();
error AddressFrozen();
error InvalidPermission();
error AccessControlUnauthorizedAccount(address account, bytes32 role);
error InvalidSignature();
error ArgumentLengthMismatch();
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
if (_isPaused()) revert ContractPaused();
_;
}
/**
* @dev Modifier to check for zero address.
*/
modifier isNonZeroAddress(address addr) {
if (addr == address(0)) revert ZeroAddress();
_;
}
/*
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return "PaxosToken USD";
}
/*
* @dev Returns the symbol of the token.
*/
function symbol() public view virtual returns (string memory) {
return "PaxosToken";
}
/*
* @dev Returns the decimal count of the token.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev Set allowance for a given spender, of a given owner.
* @param owner address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @param value uint256 The amount of tokens to increase the allowance by.
*/
function _approve(address owner, address spender, uint256 value) internal virtual;
/**
* @dev Transfer `value` amount `from` => `to`.
* @param from address The address which you want to send tokens from
* @param to address The address which you want to send tokens to
* @param value uint256 the amount of tokens to be transferred
*/
function _transfer(address from, address to, uint256 value) internal virtual;
/**
* @dev Check if contract is paused.
* @return bool True if the contract is paused, false otherwise.
*/
function _isPaused() internal view virtual returns (bool);
/**
* @dev Internal function to check whether the address is currently frozen by checking
* the sanctioned list first.
* @param addr The address to check if frozen.
* @return A bool representing whether the given address is frozen.
*/
function _isAddrFrozen(address addr) internal view virtual returns (bool);
}
"
},
"contracts/openzeppelin/contracts/utils/structs/DoubleEndedQueue.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/DoubleEndedQueue.sol)
pragma solidity ^0.8.4;
import "../math/SafeCast.sol";
/**
* @dev A sequence of items with the ability to efficiently push and pop items (i.e. insert and remove) on both ends of
* the sequence (called front and back). Among other access patterns, it can be used to implement efficient LIFO and
* FIFO queues. Storage use is optimized, and all operations are O(1) constant time. This includes {clear}, given that
* the existing queue contents are left in storage.
*
* The struct is called `Bytes32Deque`. Other types can be cast to and from `bytes32`. This data structure can only be
* used in storage, and not in memory.
* ```solidity
* DoubleEndedQueue.Bytes32Deque queue;
* ```
*
* _Available since v4.6._
*/
library DoubleEndedQueue {
/**
* @dev An operation (e.g. {front}) couldn't be completed due to the queue being empty.
*/
error Empty();
/**
* @dev An operation (e.g. {at}) couldn't be completed due to an index being out of bounds.
*/
error OutOfBounds();
/**
* @dev Indices are signed integers because the queue can grow in any direction. They are 128 bits so begin and end
* are packed in a single storage slot for efficient access. Since the items are added one at a time we can safely
* assume that these 128-bit indices will not overflow, and use unchecked arithmetic.
*
* Struct members have an underscore prefix indicating that they are "private" and should not be read or written to
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior.
*
* Indices are in the range [begin, end) which means the first item is at data[begin] and the last item is at
* data[end - 1].
*/
struct Bytes32Deque {
int128 _begin;
int128 _end;
mapping(int128 => bytes32) _data;
}
/**
* @dev Inserts an item at the end of the queue.
*/
function pushBack(Bytes32Deque storage deque, bytes32 value) internal {
int128 backIndex = deque._end;
deque._data[backIndex] = value;
unchecked {
deque._end = backIndex + 1;
}
}
/**
* @dev Removes the item at the end of the queue and returns it.
*
* Reverts with `Empty` if the queue is empty.
*/
function popBack(Bytes32Deque storage deque) internal returns (bytes32 value) {
if (empty(deque)) revert Empty();
int128 backIndex;
unchecked {
backIndex = deque._end - 1;
}
value = deque._data[backIndex];
delete deque._data[backIndex];
deque._end = backIndex;
}
/**
* @dev Inserts an item at the beginning of the queue.
*/
function pushFront(Bytes32Deque storage deque, bytes32 value) internal {
int128 frontIndex;
unchecked {
frontIndex = deque._begin - 1;
}
deque._data[frontIndex] = value;
deque._begin = frontIndex;
}
/**
* @dev Removes the item at the beginning of the queue and returns it.
*
* Reverts with `Empty` if the queue is empty.
*/
function popFront(Bytes32Deque storage deque) internal returns (bytes32 value) {
if (empty(deque)) revert Empty();
int128 frontIndex = deque._begin;
value = deque._data[frontIndex];
delete deque._data[frontIndex];
unchecked {
deque._begin = frontIndex + 1;
}
}
/**
* @dev Returns the item at the beginning of the queue.
*
* Reverts with `Empty` if the queue is empty.
*/
function front(Bytes32Deque storage deque) internal view returns (bytes32 value) {
if (empty(deque)) revert Empty();
int128 frontIndex = deque._begin;
return deque._data[frontIndex];
}
/**
* @dev Returns the item at the end of the queue.
*
* Reverts with `Empty` if the queue is empty.
*/
function back(Bytes32Deque storage deque) internal view returns (bytes32 value) {
if (empty(deque)) revert Empty();
int128 backIndex;
unchecked {
backIndex = deque._end - 1;
}
return deque._data[backIndex];
}
/**
* @dev Return the item at a position in the queue given by `index`, with the first item at 0 and last item at
* `length(deque) - 1`.
*
* Reverts with `OutOfBounds` if the index is out of bounds.
*/
function at(Bytes32Deque storage deque, uint256 index) internal view returns (bytes32 value) {
// int256(deque._begin) is a safe upcast
int128 idx = SafeCast.toInt128(int256(deque._begin) + SafeCast.toInt256(index));
if (idx >= deque._end) revert OutOfBounds();
return deque._data[idx];
}
/**
* @dev Resets the queue back to being empty.
*
* NOTE: The current items are left behind in storage. This does not affect the functioning of the queue, but misses
* out on potential gas refunds.
*/
function clear(Bytes32Deque storage deque) internal {
deque._begin = 0;
deque._end = 0;
}
/**
* @dev Returns the number of items in the queue.
*/
function length(Bytes32Deque storage deque) internal view returns (uint256) {
// The interface preserves the invariant that begin <= end so we assume this will not overflow.
// We also assume there are at most int256.max items in the queue.
unchecked {
return uint256(int256(deque._end) - int256(deque._begin));
}
}
/**
* @dev Returns true if the queue is empty.
*/
function empty(Bytes32Deque storage deque) internal view returns (bool) {
return deque._end <= deque._begin;
}
}
"
},
"contracts/openzeppelin/contracts/utils/structs/EnumerableSet.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
"
},
"contracts/openzeppelin/contracts/access/IAccessControl.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @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.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @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 granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
"
},
"contracts/openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.0;
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./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.
*
* _Available since v4.1._
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
address private immutable __self = address(this);
/**
* @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() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
_;
}
/**
* @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() {
require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
_;
}
/**
* @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 override notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeTo(address newImplementation) public virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
}
/**
* @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, true);
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeTo} and {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal override onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
"
},
"contracts/openzeppelin/contracts-upgradeable/access/AccessControlDefaultAdminRulesUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControlDefaultAdminRules.sol)
pragma solidity ^0.8.0;
import "./AccessControlUpgradeable.sol";
import "./IAccessControlDefaultAdminRulesUpgradeable.sol";
import "../utils/math/SafeCastUpgradeable.sol";
import "../interfaces/IERC5313Upgradeable.sol";
import "../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
* ) {}
* }
* ```
*
* _Available since v4.9._
*/
abstract contract AccessControlDefaultAdminRulesUpgradeable is Initializable, IAccessControlDefaultAdminRulesUpgradeable, IERC5313Upgradeable, AccessControlUpgradeable {
// 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.
*/
function __AccessControlDefaultAdminRules_init(uint48 initialDelay, address initialDefaultAdmin) internal onlyInitializing {
__AccessControlDefaultAdminRules_init_unchained(initialDelay, initialDefaultAdmin);
}
function __AccessControlDefaultAdminRules_init_unchained(uint48 initialDelay, address initialDefaultAdmin) internal onlyInitializing {
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(IAccessControlDefaultAdminRulesUpgradeable).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, IAccessControlUpgradeable) {
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(AccessControlUpgradeable, IAccessControlUpgradeable) {
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(AccessControlUpgradeable, IAccessControlUpgradeable) {
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 IAccessControlDefaultAdminRulesUpgradeable
*/
function defaultAdmin() public view virtual returns (address) {
return _currentDefaultAdmin;
}
/**
* @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
*/
function pendingDefaultAdmin() public view virtual returns (address newAdmin, uint48 schedule) {
return (_pendingDefaultAdmin, _pendingDefaultAdminSchedule);
}
/**
* @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
*/
function defaultAdminDelay() public view virtual returns (uint48) {
uint48 schedule = _pendingDelaySchedule;
return (_isScheduleSet(schedule) && _hasSchedulePassed(schedule)) ? _pendingDelay : _currentDelay;
}
/**
* @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
*/
function pendingDefaultAdminDelay() public view virtual returns (uint48 newDelay, uint48 schedule) {
schedule = _pendingDelaySchedule;
return (_isScheduleSet(schedule) && !_hasSchedulePassed(schedule)) ? (_pendingDelay, schedule) : (0, 0);
}
/**
* @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
*/
function defaultAdminDelayIncreaseWait() public view virtual returns (uint48) {
return 5 days;
}
///
/// AccessControlDefaultAdminRules public and internal setters for defaultAdmin/pendingDefaultAdmin
///
/**
* @inheritdoc IAccessControlDefaultAdminRulesUpgradeable
*/
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 = SafeCastUpgradeable.toUint48(block.timestamp) + defaultAdminDelay();
_setPendingDefaultAdmin(newAdmin, newSchedu
Submitted on: 2025-10-24 22:15:44
Comments
Log in to comment.
No comments yet.