Description:
Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/TokenMapping.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import {IRegistryAccess} from "src/interfaces/registry/IRegistryAccess.sol";
import {IRegistryContract} from "src/interfaces/registry/IRegistryContract.sol";
import {ITokenMapping} from "src/interfaces/tokenManager/ITokenMapping.sol";
import {IERC20Metadata} from "openzeppelin-contracts/interfaces/IERC20Metadata.sol";
import {
CONTRACT_REGISTRY_ACCESS,
DEFAULT_ADMIN_ROLE,
MAX_COLLATERAL_TOKEN_COUNT
} from "src/constants.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {CheckAccessControl} from "src/utils/CheckAccessControl.sol";
import {
NullAddress, InvalidToken, SameValue, Invalid, TooManyCollateralTokens
} from "src/errors.sol";
/// @title TokenMapping contract
/// @notice TokenMapping contract to manage Eur0 collateral tokens and Eur0 tokens.
/// @dev This contract provides functionalities to link Eur0 collateral tokens with EUR0 tokens and manage token pairs.
/// @dev It's part of the Usual Tech team's broader ecosystem to facilitate various operations within the platform.
/// @author Usual Tech team
contract TokenMapping is Initializable, ITokenMapping {
using CheckAccessControl for IRegistryAccess;
struct TokenMappingStorageV0 {
/// @notice Immutable instance of the REGISTRY_ACCESS contract for role checks.
IRegistryAccess _registryAccess;
/// @notice Immutable instance of the REGISTRY_CONTRACT for contract interaction.
IRegistryContract _registryContract;
/// @dev track last associated Eur0 collateral token ID associated to EUR0.
uint256 _eur0ToCollateralTokenLastId;
/// @dev assign a Eur0 collateral token address to EUR0 token address.
mapping(address => bool) isEur0Collateral;
/// @dev Eur0 collateral token ID associated with EUR0 token address.
// solhint-disable-next-line var-name-mixedcase
mapping(uint256 => address) EUR0CollateralTokens;
}
// keccak256(abi.encode(uint256(keccak256("tokenmapping.storage.v0")) - 1)) & ~bytes32(uint256(0xff))
// solhint-disable-next-line
bytes32 public constant TokenMappingStorageV0Location =
0xb0e2a10694f571e49337681df93856b25ecda603d0f0049769ee36b541ef2300;
/// @notice Returns the storage struct of the contract.
/// @return $ .
function _tokenMappingStorageV0() private pure returns (TokenMappingStorageV0 storage $) {
bytes32 position = TokenMappingStorageV0Location;
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := position
}
}
/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Initializes the TokenMapping contract with registry information.
/// @dev Sets the registry access and contract addresses upon deployment.
/// @param registryContract The address of the registry contract.
function initialize(address registryContract) public initializer {
if (registryContract == address(0)) {
revert NullAddress();
}
TokenMappingStorageV0 storage $ = _tokenMappingStorageV0();
$._registryContract = IRegistryContract(registryContract);
$._registryAccess = IRegistryAccess(
IRegistryContract(registryContract).getContract(CONTRACT_REGISTRY_ACCESS)
);
}
/// @inheritdoc ITokenMapping
function addEur0CollateralToken(address collateral) external returns (bool) {
if (collateral == address(0)) {
revert NullAddress();
}
// check if there is a decimals function at the address
// and if there is at least 1 decimal
// if not, revert
if (IERC20Metadata(collateral).decimals() == 0) {
revert Invalid();
}
TokenMappingStorageV0 storage $ = _tokenMappingStorageV0();
$._registryAccess.onlyMatchingRole(DEFAULT_ADMIN_ROLE);
// is the collateral token already registered as a EUR0 collateral
if ($.isEur0Collateral[collateral]) revert SameValue();
$.isEur0Collateral[collateral] = true;
// 0 index is always empty
++$._eur0ToCollateralTokenLastId;
if ($._eur0ToCollateralTokenLastId > MAX_COLLATERAL_TOKEN_COUNT) {
revert TooManyCollateralTokens();
}
$.EUR0CollateralTokens[$._eur0ToCollateralTokenLastId] = collateral;
emit AddEur0CollateralToken(collateral, $._eur0ToCollateralTokenLastId);
return true;
}
/*//////////////////////////////////////////////////////////////
View
//////////////////////////////////////////////////////////////*/
/// @inheritdoc ITokenMapping
function getEur0CollateralTokenById(uint256 collateralId) external view returns (address) {
TokenMappingStorageV0 storage $ = _tokenMappingStorageV0();
address collateralToken = $.EUR0CollateralTokens[collateralId];
if (collateralToken == address(0)) {
revert InvalidToken();
}
return collateralToken;
}
/// @inheritdoc ITokenMapping
function getAllEur0CollateralTokens() external view returns (address[] memory) {
TokenMappingStorageV0 storage $ = _tokenMappingStorageV0();
address[] memory collateralTokens = new address[]($._eur0ToCollateralTokenLastId);
// maximum of 10 collateral tokens
uint256 length = $._eur0ToCollateralTokenLastId;
for (uint256 i = 1; i <= length;) {
collateralTokens[i - 1] = $.EUR0CollateralTokens[i];
unchecked {
++i;
}
}
return collateralTokens;
}
/// @inheritdoc ITokenMapping
function getLastEur0CollateralTokenId() external view returns (uint256) {
TokenMappingStorageV0 storage $ = _tokenMappingStorageV0();
return $._eur0ToCollateralTokenLastId;
}
/// @inheritdoc ITokenMapping
function isEur0Collateral(address collateral) external view returns (bool) {
TokenMappingStorageV0 storage $ = _tokenMappingStorageV0();
return $.isEur0Collateral[collateral];
}
}
"
},
"src/interfaces/registry/IRegistryAccess.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import {IAccessControlDefaultAdminRules} from
"openzeppelin-contracts/access/extensions/IAccessControlDefaultAdminRules.sol";
interface IRegistryAccess is IAccessControlDefaultAdminRules {
/*//////////////////////////////////////////////////////////////
Functions
//////////////////////////////////////////////////////////////*/
/// @notice Set the admin role for a specific role
/// @param role The role to set the admin for
/// @param adminRole The admin role to set
function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
}
"
},
"src/interfaces/registry/IRegistryContract.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
interface IRegistryContract {
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @notice This event is emitted when the address of the contract is set
/// @param name The name of the contract in bytes32
/// @param contractAddress The address of the contract
event SetContract(bytes32 indexed name, address indexed contractAddress);
/*//////////////////////////////////////////////////////////////
Functions
//////////////////////////////////////////////////////////////*/
/// @notice Set the address of the contract
/// @param name The name of the contract in bytes32
/// @param contractAddress The address of the contract
function setContract(bytes32 name, address contractAddress) external;
/// @notice Get the address of the contract
/// @param name The name of the contract in bytes32
/// @return contractAddress The address of the contract
function getContract(bytes32 name) external view returns (address);
}
"
},
"src/interfaces/tokenManager/ITokenMapping.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
interface ITokenMapping {
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @notice Emitted when an collateral token is linked to EUR0 token.
/// @param collateral The address of the collateral token.
/// @param collateralId The ID of the collateral token.
event AddEur0CollateralToken(address indexed collateral, uint256 indexed collateralId);
/*//////////////////////////////////////////////////////////////
Functions
//////////////////////////////////////////////////////////////*/
/// @notice Links an collateral token to EUR0 token.
/// @dev Only the admin can link the collateral token to EUR0 token.
/// @dev Ensures the collateral token is valid and not already linked to EUR0 token.
/// @param collateral The address of the collateral token.
/// @return A boolean value indicating success.
function addEur0CollateralToken(address collateral) external returns (bool);
/// @notice Retrieves the collateral token linked to EUR0 token.
/// @dev Returns the address of the collateral token associated with EUR0 token.
/// @param collateralId The ID of the collateral token.
/// @return The address of the associated collateral token.
function getEur0CollateralTokenById(uint256 collateralId) external view returns (address);
/// @notice Retrieves all collateral tokens linked to EUR0 token.
/// @dev Returns an array of addresses of all collateral tokens associated with EUR0 token.
/// @dev the maximum number of collateral tokens that can be associated with EUR0 token is 10.
/// @return An array of addresses of associated collateral tokens.
function getAllEur0CollateralTokens() external view returns (address[] memory);
/// @notice Retrieves the last collateral ID for EUR0 token.
/// @dev Returns the highest index used for the collateral tokens associated with the EUR0 token.
/// @return The last collateral ID used in the STBC to collateral mapping.
function getLastEur0CollateralTokenId() external view returns (uint256);
/// @notice Checks if the collateral token is linked to EUR0 token.
/// @dev Returns a boolean value indicating if the collateral token is linked to EUR0 token.
/// @param collateral The address of the collateral token.
/// @return A boolean value indicating if the collateral token is linked to EUR0 token.
function isEur0Collateral(address collateral) external view returns (bool);
}
"
},
"lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
"
},
"src/constants.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
/* Roles */
bytes32 constant DEFAULT_ADMIN_ROLE = 0x00;
bytes32 constant DAO_REDEMPTION_ROLE = keccak256("DAO_REDEMPTION_ROLE");
bytes32 constant PAUSING_CONTRACTS_ROLE = keccak256("PAUSING_CONTRACTS_ROLE");
bytes32 constant UNPAUSING_CONTRACTS_ROLE = keccak256(
"UNPAUSING_CONTRACTS_ROLE"
);
bytes32 constant BLACKLIST_ROLE = keccak256("BLACKLIST_ROLE");
bytes32 constant EUR0_MINT = keccak256("EUR0_MINT");
bytes32 constant EUR0_BURN = keccak256("EUR0_BURN");
bytes32 constant SWAPPER_ENGINE = keccak256("SWAPPER_ENGINE");
bytes32 constant NONCE_THRESHOLD_SETTER_ROLE = keccak256(
"NONCE_THRESHOLD_SETTER_ROLE"
);
bytes32 constant INTENT_MATCHING_ROLE = keccak256("INTENT_MATCHING_ROLE");
bytes32 constant MIN_REDEEM_AMOUNT_SET_ROLE = keccak256(
"MIN_REDEEM_AMOUNT_SET_ROLE"
);
/* Contracts */
bytes32 constant CONTRACT_EUR0 = keccak256("CONTRACT_EUR0");
bytes32 constant CONTRACT_REGISTRY_ACCESS = keccak256(
"CONTRACT_REGISTRY_ACCESS"
);
bytes32 constant CONTRACT_TOKEN_MAPPING = keccak256("CONTRACT_TOKEN_MAPPING");
bytes32 constant CONTRACT_ORACLE = keccak256("CONTRACT_ORACLE");
bytes32 constant CONTRACT_EURC_ORACLE = keccak256("CONTRACT_EURC_ORACLE");
bytes32 constant CONTRACT_DATA_PUBLISHER = keccak256("CONTRACT_DATA_PUBLISHER");
bytes32 constant CONTRACT_TREASURY = keccak256("CONTRACT_TREASURY");
bytes32 constant CONTRACT_YIELD_TREASURY = keccak256("CONTRACT_YIELD_TREASURY");
bytes32 constant CONTRACT_DAO_COLLATERAL = keccak256("CONTRACT_DAO_COLLATERAL");
bytes32 constant CONTRACT_SWAPPER_ENGINE = keccak256("CONTRACT_SWAPPER_ENGINE");
bytes32 constant INTENT_TYPE_HASH = keccak256(
"SwapIntent(address recipient,address rwaToken,uint256 amountInTokenDecimals,uint256 nonce,uint256 deadline)"
);
/* Token names and symbols */
string constant EUR0Symbol = "EUR0";
string constant EUR0Name = "Usual EUR";
/* Constants */
uint256 constant SCALAR_ONE = 1e18;
uint256 constant ONE_EURC = 1e6;
uint256 constant MAX_REDEEM_FEE = 2500;
uint256 constant MIN_REDEEM_AMOUNT_IN_EUR0 = 0.10e18; // 0.1 EUR0
uint256 constant BASIS_POINT_BASE = 10_000;
uint64 constant ONE_WEEK = 604_800;
uint64 constant ONE_YEAR = 31_536_000;
uint256 constant ONE_PERCENT = 0.01e18;
uint256 constant ONE_AND_HALF_PERCENT = 0.015e18;
uint256 constant REDEEM_FEE = 3; // 0.03% fee
uint256 constant INITIAL_MAX_DEPEG_THRESHOLD = 100;
uint256 constant MAX_COLLATERAL_TOKEN_COUNT = 10;
uint256 constant MINIMUM_EURC_PROVIDED = 100e6;
/* Maximum number of RWA tokens that can be associated with EUR0 */
uint256 constant MAX_RWA_COUNT = 10;
/* External Deployment */
address constant EURC = 0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c;
address constant EURC_USD_CHAINLINK = 0x04F84020Fdf10d9ee64D1dcC2986EDF2F556DA11;
address constant EUR_USD_CHAINLINK = 0xb49f677943BC038e9857d61E7d053CaA2C1734C1;
/* Mainnet Usual Deployment */
address constant USUAL_MULTISIG_MAINNET = 0xE89e6931c803fFdeA19E98F88979967CF211CA76;
address constant USUAL_PROXY_ADMIN_MAINNET = 0x68bFFB0fcD08AA9967297A63E5DEE4767f44cae1;
address constant TREASURY_MAINNET = 0x11D75bC93aE69350231D8fF0F5832A697678183E;
address constant TREASURY_YIELD_MAINNET = 0x81ad394C0Fa87e99Ca46E1aca093BEe020f203f4;
address constant BLACKLIST_MAINNET = 0xFBAE315cfB0A6C770a397bD6E597d245Ba858648;
address constant PAUSER_MAINNET = 0x438D73419ee7E9C3BD91FbA1850928cdB8897ca6;
"
},
"lib/openzeppelin-contracts/contracts/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}
"
},
"src/utils/CheckAccessControl.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import {IRegistryAccess} from "src/interfaces/registry/IRegistryAccess.sol";
import {NotAuthorized} from "src/errors.sol";
/// @title Check Access control library
library CheckAccessControl {
/// @dev Function to restrict to one access role.
/// @param registryAccess The registry access contract.
/// @param role The role being checked.
function onlyMatchingRole(IRegistryAccess registryAccess, bytes32 role) internal view {
if (!registryAccess.hasRole(role, msg.sender)) {
revert NotAuthorized();
}
}
}
"
},
"src/errors.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
error AlreadyWhitelisted();
error AmountTooBig();
error AmountTooLow();
error AmountIsZero();
error Blacklisted();
error SameValue();
error Invalid();
error InvalidName();
error InvalidSymbol();
error InvalidToken();
error InvalidTimeout();
error InvalidDecimals();
error InvalidDecimalsNumber();
error InvalidSigner(address owner);
error InvalidDeadline(uint256 approvalDeadline, uint256 intentDeadline);
error InvalidOrderAmount(address account, uint256 amount);
error ExpiredSignature(uint256 deadline);
error ApprovalFailed();
error InsufficientEUR0Balance();
error OrderNotActive();
error NoOrdersIdsProvided();
error NotRecipient();
error NotAuthorized();
error NullAddress();
error NullContract();
error OracleNotWorkingNotCurrent();
error OracleNotInitialized();
error RedeemMustNotBePaused();
error RedeemMustBePaused();
error SwapMustNotBePaused();
error SwapMustBePaused();
error StablecoinDepeg();
error DepegThresholdTooHigh();
error CBRIsTooHigh();
error CBRIsNull();
error RedeemFeeTooBig();
error RedeemFeeCannotBeZero();
error TooManyCollateralTokens();
error AmountExceedBacking();
error NotSupported();
"
},
"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/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @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.
*/
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 `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
}
},
"settings": {
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"utils/=lib/utils/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"src/=src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false
}
}}
Submitted on: 2025-10-03 11:33:25
Comments
Log in to comment.
No comments yet.