Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/oracles/ClassicalOracle.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
import {IOracle} from "src/interfaces/oracles/IOracle.sol";
import {IAggregator} from "src/interfaces/oracles/IAggregator.sol";
import {IRegistryContract} from "src/interfaces/registry/IRegistryContract.sol";
import {IRegistryAccess} from "src/interfaces/registry/IRegistryAccess.sol";
import {CheckAccessControl} from "src/utils/CheckAccessControl.sol";
import {
DEFAULT_ADMIN_ROLE,
CONTRACT_REGISTRY_ACCESS,
BASIS_POINT_BASE,
INITIAL_MAX_DEPEG_THRESHOLD,
SCALAR_ONE,
ONE_WEEK
} from "src/constants.sol";
import {
StablecoinDepeg,
NullAddress,
SameValue,
DepegThresholdTooHigh,
OracleNotWorkingNotCurrent,
OracleNotInitialized,
InvalidTimeout
} from "src/errors.sol";
import {Normalize} from "src/utils/normalize.sol";
/// @title Classical Oracle System
/// @dev This oracle aggregates existing oracles for various tokens.
/// @dev It makes the price of these tokens available through a common interface.
/// @author Usual Tech Team
contract ClassicalOracle is Initializable, IOracle {
using CheckAccessControl for IRegistryAccess;
using Normalize for uint256;
/*//////////////////////////////////////////////////////////////
Storage
//////////////////////////////////////////////////////////////*/
// Storage struct of the contract
struct ClassicalOracleStorageV0 {
IRegistryContract registryContract;
IRegistryAccess registryAccess;
/// @notice mapping to get all oracle information from a token
mapping(address token => TokenOracle) tokenToOracleInfo;
uint256 maxDepegThreshold;
}
// keccak256(abi.encode(uint256(keccak256("classicaloracle.storage.v0")) - 1)) & ~bytes32(uint256(0xff))
// solhint-disable-next-line
bytes32 public constant ClassicalOracleStorageV0Location =
0x54442c47405c4b442fe4a3934462234020dc9307724ba01f7cb4a4588269b600;
/// @notice Returns the storage struct of the contract.
/// @return $ The pointer to the storage struct of the contract.
function _classicalOracleStorageV0()
internal
pure
returns (ClassicalOracleStorageV0 storage $)
{
bytes32 position = ClassicalOracleStorageV0Location;
// solhint-disable-next-line no-inline-assembly
assembly {
$.slot := position
}
}
/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Function for initializing the contract.
/// @dev This function is used to set the initial state of the contract.
/// @param registryContract The registry contract address.
function initialize(address registryContract) public initializer {
if (registryContract == address(0)) {
revert NullAddress();
}
ClassicalOracleStorageV0 storage $ = _classicalOracleStorageV0();
$.registryContract = IRegistryContract(registryContract);
$.registryAccess = IRegistryAccess($.registryContract.getContract(CONTRACT_REGISTRY_ACCESS));
$.maxDepegThreshold = INITIAL_MAX_DEPEG_THRESHOLD;
emit SetMaxDepegThreshold(INITIAL_MAX_DEPEG_THRESHOLD);
}
/// @inheritdoc IOracle
function getMaxDepegThreshold() external view override returns (uint256) {
ClassicalOracleStorageV0 storage $ = _classicalOracleStorageV0();
return $.maxDepegThreshold;
}
/// @inheritdoc IOracle
function setMaxDepegThreshold(uint256 maxAuthorizedDepegPrice) external virtual {
if (maxAuthorizedDepegPrice > BASIS_POINT_BASE) revert DepegThresholdTooHigh();
ClassicalOracleStorageV0 storage $ = _classicalOracleStorageV0();
$.registryAccess.onlyMatchingRole(DEFAULT_ADMIN_ROLE);
if ($.maxDepegThreshold == maxAuthorizedDepegPrice) revert SameValue();
$.maxDepegThreshold = maxAuthorizedDepegPrice;
emit SetMaxDepegThreshold(maxAuthorizedDepegPrice);
}
// --- Functions ---
/// @inheritdoc IOracle
function getPrice(address token) public view override returns (uint256) {
(uint256 price, uint256 decimalsPrice) = _latestRoundData(token);
price = price.tokenAmountToWad(uint8(decimalsPrice));
_checkDepegPrice(token, price);
return price;
}
/// @inheritdoc IOracle
function getQuote(address token, uint256 amount) external view override returns (uint256) {
return Math.mulDiv(getPrice(token), amount, SCALAR_ONE);
}
/// @notice Initialize a new supported token.
/// @dev When adding a new token, we assume that the provided oracle is working.
/// @param token The address of the new token.
/// @param dataSource The address of the Chainlink aggregator, i.e. underlying oracle.
/// @param timeout The timeout in seconds.
function initializeTokenOracle(
address token,
address dataSource,
uint64 timeout,
bool isStablecoin
) external {
if (token == address(0)) revert NullAddress();
if (dataSource == address(0)) revert NullAddress();
// The timeout can't be zero and must be at most one week
if (timeout == 0 || timeout > ONE_WEEK) revert InvalidTimeout();
// slither-disable-next-line unused-return
(, int256 answer,, uint256 updatedAt,) = IAggregator(dataSource).latestRoundData();
if (answer <= 0 || updatedAt == 0 || block.timestamp > updatedAt + timeout) {
revert OracleNotWorkingNotCurrent();
}
ClassicalOracleStorageV0 storage $ = _classicalOracleStorageV0();
$.registryAccess.onlyMatchingRole(DEFAULT_ADMIN_ROLE);
$.tokenToOracleInfo[token].dataSource = dataSource;
$.tokenToOracleInfo[token].isStablecoin = isStablecoin;
$.tokenToOracleInfo[token].timeout = timeout;
}
/*//////////////////////////////////////////////////////////////
Helpers
//////////////////////////////////////////////////////////////*/
/// @notice Get the most recent oracle response for a token.
/// @param token The address of the token
/// @return price The most recent oracle response.
/// @return priceDecimals The amount of decimals for the price.
function _latestRoundData(address token)
internal
view
returns (uint256 price, uint256 priceDecimals)
{
ClassicalOracleStorageV0 storage $ = _classicalOracleStorageV0();
IAggregator priceAggregatorProxy = IAggregator($.tokenToOracleInfo[token].dataSource);
if (address(priceAggregatorProxy) == address(0)) revert OracleNotInitialized();
priceDecimals = priceAggregatorProxy.decimals();
// slither-disable-next-line unused-return
(, int256 answer,, uint256 updatedAt,) = priceAggregatorProxy.latestRoundData();
if (answer <= 0) revert OracleNotWorkingNotCurrent();
if (updatedAt > block.timestamp) revert OracleNotWorkingNotCurrent();
// track the updatedAt value from latestRoundData()
// to make sure that the latest answer is recent enough for your application to use it
// detects that the reported answer is not updated within the heartbeat timeout
if (block.timestamp > $.tokenToOracleInfo[token].timeout + updatedAt) {
revert OracleNotWorkingNotCurrent();
}
return (uint256(answer), priceDecimals);
}
/// @notice Check if a given token's price is around 1 EUR
/// @dev Reverts if the stablecoin(in the context of EUR) has depegged.
/// @dev The allowed range is determined by maxDepegThreshold.
/// @param token The address of the token.
/// @param wadPriceInEUR The price of the token returned by the underlying oracle.
function _checkDepegPrice(address token, uint256 wadPriceInEUR) internal view {
ClassicalOracleStorageV0 storage $ = _classicalOracleStorageV0();
// Skip the check if the token is not a stablecoin
if (!$.tokenToOracleInfo[token].isStablecoin) return;
uint256 threshold = Math.mulDiv($.maxDepegThreshold, SCALAR_ONE, BASIS_POINT_BASE);
if (wadPriceInEUR > SCALAR_ONE + threshold || wadPriceInEUR < SCALAR_ONE - threshold) {
revert StablecoinDepeg();
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/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
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/math/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
"
},
"src/interfaces/oracles/IOracle.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
interface IOracle {
/*//////////////////////////////////////////////////////////////
Structs
//////////////////////////////////////////////////////////////*/
/// @notice Struct representing the oracle information for a token
struct TokenOracle {
/// @notice The oracle contract for the token
address dataSource;
/// @notice Flag indicating if the token is stable token
bool isStablecoin;
/// @notice Timeout in seconds
uint64 timeout;
}
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @notice Event emitted when the value of the max depeg threshold change
/// @param newMaxDepegThreshold The new max depeg threshold
event SetMaxDepegThreshold(uint256 newMaxDepegThreshold);
/*//////////////////////////////////////////////////////////////
Functions
//////////////////////////////////////////////////////////////*/
/// @notice Get the latest price of a token from the underlying oracle.
/// @dev View function which fetches the latest available oracle price from a Chainlink-compatible feed.
/// @dev The result is scaled to 18 decimals.
/// @param token The address of the token.
/// @return The price of the token in ETH with 18 decimals.
function getPrice(address token) external view returns (uint256);
/// @notice Compute a quote for the specified token and amount.
/// @dev This function fetches the latest available price from a Chainlink-compatible feed for the token and computes the quote based on the given amount.
/// @dev The quote is computed by multiplying the amount of tokens by the token price.
/// @dev The result is returned with as many decimals as the input amount.
/// @param token The address of the token to calculate the quote for.
/// @param amount The amount of tokens for which to compute the quote.
/// @return The computed quote in ETH with as many decimals as the input.
function getQuote(address token, uint256 amount) external returns (uint256);
/// @notice Get the maximum allowed depeg threshold for stablecoins.
/// @return The maximum allowed depeg threshold in basis points.
function getMaxDepegThreshold() external view returns (uint256);
/// @notice Set the maximum allowed depeg threshold for stablecoins.
/// @dev The provided value should be in basis points relative to 1 ETH.
/// @dev Valid values are from 0 (exact 1:1 peg required) up to 10_000 (100% depeg).
/// @dev getPrice will revert if the price falls outside of this range.
/// @param maxAuthorizedDepegPrice The new maximum allowed depeg threshold.
function setMaxDepegThreshold(uint256 maxAuthorizedDepegPrice) external;
}
"
},
"src/interfaces/oracles/IAggregator.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
interface IAggregator {
/// @notice Gets the number of decimals used by the aggregator.
function decimals() external view returns (uint8);
/// @notice Get the description of the aggregator.
function description() external view returns (string memory);
/// @notice Get the version of the aggregator.
function version() external view returns (uint256);
/// @notice Gets the round data for a specific round ID.
function getRoundData(uint80 roundId_)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
/// Gets the latest round data.
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
"
},
"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/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/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/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;
"
},
"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();
"
},
"src/utils/normalize.sol": {
"content": "// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.20;
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
import {SCALAR_ONE} from "src/constants.sol";
/// @title Normalize decimals between tokens
library Normalize {
/// @notice Normalize token amount to target decimals
/// @notice i.e 100 EURC with 6 decimals to 100e18 EURC with 18 decimals
/// @param tokenAmount The token amount
/// @param tokenDecimals The token decimals
/// @param targetDecimals The target decimals
function tokenAmountToDecimals(uint256 tokenAmount, uint8 tokenDecimals, uint8 targetDecimals)
internal
pure
returns (uint256)
{
if (tokenDecimals < targetDecimals) {
return tokenAmount * (10 ** uint256(targetDecimals - tokenDecimals));
} else if (tokenDecimals > targetDecimals) {
return tokenAmount / (10 ** uint256(tokenDecimals - targetDecimals));
} else {
return tokenAmount;
}
}
/// @notice Normalize token amount to 18 decimals
/// @notice i.e 100 EURC with 6 decimals to 100e18 EURC with 18 decimals
/// @param tokenAmount The token amount
/// @param tokenDecimals The token decimals
function tokenAmountToWad(uint256 tokenAmount, uint8 tokenDecimals)
internal
pure
returns (uint256)
{
return tokenAmountToDecimals(tokenAmount, tokenDecimals, 18);
}
/// @notice Normalize token amount to wad
/// @notice i.e 10e5 EUTBL with 5 decimals will result in wadAmount = 10e18 and tokenDecimals = 5
/// @param token The token address
/// @param tokenAmount The token amount
/// @return wadAmount The normalized token amount in wad
/// @return tokenDecimals The token decimals
function tokenAmountToWadWithTokenAddress(uint256 tokenAmount, address token)
internal
view
returns (uint256, uint8)
{
uint8 tokenDecimals = uint8(IERC20Metadata(token).decimals());
uint256 wadAmount = tokenAmountToWad(tokenAmount, uint8(tokenDecimals));
return (wadAmount, tokenDecimals);
}
/// @notice Returns wad amount of token at wad price.
/// @notice i.e 10e5 EUTBL with 5 decimals will be wadAmount = 10e18
/// @param wadAmount The wad amount (18 decimals)
/// @param wadPrice The wad price (18 decimals)
/// @return The normalized token amount for price
function wadAmountByPrice(uint256 wadAmount, uint256 wadPrice)
internal
pure
returns (uint256)
{
return Math.mulDiv(wadAmount, wadPrice, SCALAR_ONE, Math.Rounding.Floor);
}
/// @notice return how much token we can have for stable amount.
/// @param wadStableAmount The wad stable token amount
/// @param wadPrice The wad price with for the token in stable
/// @param tokenDecimals The token decimals
/// @return The token amount for price with token decimals
function wadTokenAmountForPrice(uint256 wadStableAmount, uint256 wadPrice, uint8 tokenDecimals)
internal
pure
returns (uint256)
{
return Math.mulDiv(wadStableAmount, 10 ** tokenDecimals, wadPrice, Math.Rounding.Floor);
}
/// @dev Converts a WAD amount to a different number of decimals.
/// @param wadAmount The WAD amount to convert.
/// @param targetDecimals The number of decimals to convert to.
/// @return The converted amount with the target number of decimals.
function wadAmountToDecimals(uint256 wadAmount, uint8 targetDecimals)
internal
pure
returns (uint256)
{
return tokenAmountToDecimals(wadAmount, 18, targetDecimals);
}
}
"
},
"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
Submitted on: 2025-10-03 11:33:27
Comments
Log in to comment.
No comments yet.