Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Grant Role | 21675026 | 402 days ago | IN | 0 ETH | 0.00086938 |
Latest 6 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60806040 | 22167381 | 333 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 22167381 | 333 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 21675057 | 402 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 21675057 | 402 days ago | Contract Creation | 0 ETH | |||
| 0x60a08060 | 21674933 | 402 days ago | Contract Creation | 0 ETH | |||
| 0x60a08060 | 21674933 | 402 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
InsuranceCapitalLayerFactory
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 100 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
//SPDX-License-Identifier:MIT
pragma solidity 0.8.20;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./InsuranceCapitalLayer.sol";
import "./CollateralManager.sol";
import {ISharePriceCalculator} from "./interfaces/ISharePriceCalculator.sol";
import {SharePriceCalculatorFactory} from "./SharePriceCalculatorFactory.sol";
import {ICollateralManagerFactory} from "./interfaces/ICollateralManagerFactory.sol";
import {ShareToken} from "./token/ShareToken.sol";
contract InsuranceCapitalLayerFactory is AccessControl {
IKYCRegistry public immutable KYC_REGISTRY;
IPoolRegistry public immutable POOL_REGISTRY;
SharePriceCalculatorFactory public immutable SHARE_PRICE_CALCULATOR_FACTORY;
ICollateralManagerFactory public immutable COLLATERAL_MANAGER_FACTORY;
address public immutable ICL_IMPLEMENTATION;
address public immutable SHARE_TOKEN_IMPLEMENTATION;
bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE");
event InsuranceCapitalLayerCreated(
address indexed icl,
address indexed shareToken
);
constructor(
address kycRegistry_,
address poolRegistry_,
address sharePriceCalculatorFactory_,
address collateralManagerFactory_
) {
KYC_REGISTRY = IKYCRegistry(kycRegistry_);
POOL_REGISTRY = IPoolRegistry(poolRegistry_);
SHARE_PRICE_CALCULATOR_FACTORY = SharePriceCalculatorFactory(
sharePriceCalculatorFactory_
);
COLLATERAL_MANAGER_FACTORY = ICollateralManagerFactory(
collateralManagerFactory_
);
// Deploy the implementations
InsuranceCapitalLayer iclImplementation = new InsuranceCapitalLayer();
ICL_IMPLEMENTATION = address(iclImplementation);
ShareToken shareTokenImplementation = new ShareToken();
SHARE_TOKEN_IMPLEMENTATION = address(shareTokenImplementation);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
function createInsuranceCapitalLayer(
address depositTokenRegistry,
string memory tokenName,
string memory tokenSymbol,
address admin,
address operator,
uint256 initialSharePrice
) external onlyRole(DEPLOYER_ROLE) returns (address) {
ERC1967Proxy shareTokenProxy = new ERC1967Proxy(
address(SHARE_TOKEN_IMPLEMENTATION),
abi.encodeWithSelector(
ShareToken.initialize.selector,
tokenName,
tokenSymbol,
18,
msg.sender
)
);
bytes memory initData = abi.encodeWithSelector(
InsuranceCapitalLayer.initialize.selector,
msg.sender,
tokenName,
tokenSymbol,
address(KYC_REGISTRY),
operator,
depositTokenRegistry,
address(
SHARE_PRICE_CALCULATOR_FACTORY.createSharePriceCalculator(
depositTokenRegistry,
initialSharePrice,
admin,
operator
)
)
);
ERC1967Proxy iclProxy = new ERC1967Proxy(
address(ICL_IMPLEMENTATION),
initData
);
address newICL = address(iclProxy);
// Set up roles
ShareToken shareToken = ShareToken(address(shareTokenProxy));
shareToken.grantRole(shareToken.DEFAULT_ADMIN_ROLE(), admin);
shareToken.grantRole(shareToken.MINTER_ROLE(), newICL);
// Set sharetoken in ICL
InsuranceCapitalLayer(newICL).setShareToken(
ShareToken(address(shareTokenProxy))
);
InsuranceCapitalLayer(newICL).grantRole(
InsuranceCapitalLayer(newICL).DEFAULT_ADMIN_ROLE(),
admin
);
emit InsuranceCapitalLayerCreated(newICL, address(shareTokenProxy));
return newICL;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// 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
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/UUPSUpgradeable.sol)
pragma solidity ^0.8.20;
import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";
/**
* @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
* {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
*
* A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
* reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
* `UUPSUpgradeable` with a custom implementation of upgrades.
*
* The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
*/
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable __self = address(this);
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
* and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
* while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev The call is from an unauthorized context.
*/
error UUPSUnauthorizedCallContext();
/**
* @dev The storage `slot` is unsupported as a UUID.
*/
error UUPSUnsupportedProxiableUUID(bytes32 slot);
/**
* @dev Check that the execution is being performed through a delegatecall call and that the execution context is
* a proxy contract with an implementation (as defined in ERC1967) pointing to self. This should only be the case
* for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
* function through ERC1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
* fail.
*/
modifier onlyProxy() {
_checkProxy();
_;
}
/**
* @dev Check that the execution is not being performed through a delegate call. This allows a function to be
* callable on the implementing contract but not through proxies.
*/
modifier notDelegated() {
_checkNotDelegated();
_;
}
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
/**
* @dev Implementation of the ERC1822 {proxiableUUID} function. This returns the storage slot used by the
* implementation. It is used to validate the implementation's compatibility when performing an upgrade.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
*/
function proxiableUUID() external view virtual notDelegated returns (bytes32) {
return ERC1967Utils.IMPLEMENTATION_SLOT;
}
/**
* @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
* encoded in `data`.
*
* Calls {_authorizeUpgrade}.
*
* Emits an {Upgraded} event.
*
* @custom:oz-upgrades-unsafe-allow-reachable delegatecall
*/
function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data);
}
/**
* @dev Reverts if the execution is not performed via delegatecall or the execution
* context is not of a proxy with an ERC1967-compliant implementation pointing to self.
* See {_onlyProxy}.
*/
function _checkProxy() internal view virtual {
if (
address(this) == __self || // Must be called through delegatecall
ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
) {
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Reverts if the execution is performed via delegatecall.
* See {notDelegated}.
*/
function _checkNotDelegated() internal view virtual {
if (address(this) != __self) {
// Must not be called through delegatecall
revert UUPSUnauthorizedCallContext();
}
}
/**
* @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
* {upgradeToAndCall}.
*
* Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
*
* ```solidity
* function _authorizeUpgrade(address) internal onlyOwner {}
* ```
*/
function _authorizeUpgrade(address newImplementation) internal virtual;
/**
* @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
*
* As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
* is expected to be the implementation slot in ERC1967.
*
* Emits an {IERC1967-Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
revert UUPSUnsupportedProxiableUUID(slot);
}
ERC1967Utils.upgradeToAndCall(newImplementation, data);
} catch {
// The implementation is not UUPS
revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors {
/// @custom:storage-location erc7201:openzeppelin.storage.ERC20
struct ERC20Storage {
mapping(address account => uint256) _balances;
mapping(address account => mapping(address spender => uint256)) _allowances;
uint256 _totalSupply;
string _name;
string _symbol;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00;
function _getERC20Storage() private pure returns (ERC20Storage storage $) {
assembly {
$.slot := ERC20StorageLocation
}
}
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing {
__ERC20_init_unchained(name_, symbol_);
}
function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing {
ERC20Storage storage $ = _getERC20Storage();
$._name = name_;
$._symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
ERC20Storage storage $ = _getERC20Storage();
return $._symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
ERC20Storage storage $ = _getERC20Storage();
return $._allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
ERC20Storage storage $ = _getERC20Storage();
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
$._totalSupply += value;
} else {
uint256 fromBalance = $._balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
$._balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
$._totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
$._balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
ERC20Storage storage $ = _getERC20Storage();
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
$._allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165Upgradeable is Initializable, IERC165 {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuardUpgradeable is Initializable {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
/// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard
struct ReentrancyGuardStorage {
uint256 _status;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) {
assembly {
$.slot := ReentrancyGuardStorageLocation
}
}
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
function __ReentrancyGuard_init() internal onlyInitializing {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal onlyInitializing {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
$._status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// On the first call to nonReentrant, _status will be NOT_ENTERED
if ($._status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
$._status = ENTERED;
}
function _nonReentrantAfter() private {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
$._status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage();
return $._status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// 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;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Proxy.sol)
pragma solidity ^0.8.20;
import {Proxy} from "../Proxy.sol";
import {ERC1967Utils} from "./ERC1967Utils.sol";
/**
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
* implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/
contract ERC1967Proxy is Proxy {
/**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
*
* If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
* encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
*
* Requirements:
*
* - If `data` is empty, `msg.value` must be zero.
*/
constructor(address implementation, bytes memory _data) payable {
ERC1967Utils.upgradeToAndCall(implementation, _data);
}
/**
* @dev Returns the current implementation address.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/
function _implementation() internal view virtual override returns (address) {
return ERC1967Utils.getImplementation();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.20;
import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*/
library ERC1967Utils {
// We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
// This will be fixed in Solidity 0.8.21. At that point we should remove these events.
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
pragma solidity ^0.8.20;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback
* function and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback() external payable virtual {
_fallback();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// 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;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./interfaces/ICollateralAdmin.sol";
import "./interfaces/ICollateralProvider.sol";
import "./interfaces/IDepositTokenRegistry.sol";
import "./interfaces/IPoolRegistry.sol";
import "./interfaces/IPool.sol";
import "./interfaces/ICollateralTokenHandler.sol";
import "./utils/ConvertDecimals.sol";
contract CollateralManager is
ICollateralProvider,
ICollateralAdmin,
ERC165,
AccessControl,
Pausable,
ReentrancyGuard
{
using SafeERC20 for IERC20;
using Math for uint256;
using ConvertDecimals for uint256;
struct ReservationRequest {
address pool;
address token;
uint256 amount;
RequestStatus status;
}
struct TransferRequest {
address pool;
address token;
uint256 amount;
RequestStatus status;
}
struct UndeployedReturnRequest {
address pool;
address token;
uint256 amount;
uint256 total;
RequestStatus status;
}
struct DeployedReturnRequest {
address pool;
address transferredToken;
address returnToken;
uint256 netAmount;
uint256 fees;
uint256 total;
RequestStatus status;
}
struct ProfitReturnRequest {
address pool;
address token;
uint256 netAmount;
uint256 fees;
uint256 total;
RequestStatus status;
}
struct CollateralLimitIncreaseRequest {
address pool;
uint256 currentLimit;
uint256 requestedLimit;
uint256 timestamp;
RequestStatus status;
}
// State variables
uint256 private _nextRequestId;
mapping(uint256 => ReservationRequest) private _reservationRequests;
mapping(address => mapping(address => uint256))
private _pendingReservationsByPoolToken;
mapping(uint256 => TransferRequest) private _transferRequests;
mapping(address => mapping(address => uint256))
private _pendingTransfersByPoolToken;
mapping(uint256 => UndeployedReturnRequest) private _undeployedReturnRequests;
mapping(uint256 => DeployedReturnRequest) private _deployedReturnRequests;
mapping(address => mapping(address => uint256))
private _pendingUndeployedReturnsByPoolToken;
mapping(address => mapping(address => uint256))
private _pendingDeployedReturnsByPoolToken;
mapping(uint256 => ProfitReturnRequest) private _profitReturnRequests;
mapping(address => mapping(address => uint256))
private _pendingProfitReturnsByPoolToken;
mapping(address => mapping(address => uint256)) private _profitReturnsAmounts;
IPoolRegistry public immutable poolRegistry;
IDepositTokenRegistry public immutable depositTokenRegistry;
// Add mapping to track total approved reservations per token
mapping(address => uint256) private _totalReservedByToken;
// Add mapping to track approved reservations per pool and token
mapping(address => mapping(address => uint256))
private _approvedReservationsByPoolToken;
// Add mapping to track transferred amounts per pool and token
mapping(address => mapping(address => uint256)) private _transferredAmounts;
// Update role constant name
bytes32 public constant COLLATERAL_ADMIN = keccak256("COLLATERAL_ADMIN");
// Add near other state variables
mapping(address => mapping(address => uint256)) private _returnedAmounts;
ICollateralTokenHandler public immutable collateralTokenHandler;
uint256 private constant MINIMUM_DUST_DECIMALS = 6; // At least 0.000001 of token
// Track collateral limit increase requests
mapping(uint256 => CollateralLimitIncreaseRequest)
private _limitIncreaseRequests;
mapping(address => uint256) private _pendingLimitIncreaseByPool;
event CollateralManagerPaused(address indexed operator);
event CollateralManagerUnpaused(address indexed operator);
// Add custom error
error RequestDoesNotExist(uint256 requestId);
error RequestNotPending(uint256 requestId);
error PoolNotRegistered(address pool);
error NotRequestingPool(address caller, address pool);
error PendingRequestExists(string requestType);
// Add near other errors
error InvalidAdminAddress();
error InvalidRegistryAddress();
error InvalidPoolRegistryAddress();
error InvalidCollateralAdminAddress();
error InvalidTokenHandlerAddress();
// Add these custom errors near the other error declarations
error AmountMustBeGreaterThanZero();
error InsufficientTokenBalance();
error TokenNotEligibleAsCollateral();
error InvalidTokenAddress();
error InsufficientReservedAmount();
error TotalCollateralExceedsLimit();
error CollateralNotFullyReturned();
error MustIncreaseLimit();
error AfterTransferDate();
error PendingRequestAlreadyExists();
error OnlyPoolCanCancel();
error RequestNotFound();
error InsufficientBalanceForTransfer();
error PendingReturnRequestExists();
error AmountExceedsTransferred();
/// @notice Checks if a request exists
/// @param requestId The ID of the request to validate
modifier requestExists(uint256 requestId) {
if (
_reservationRequests[requestId].pool == address(0) &&
_transferRequests[requestId].pool == address(0) &&
_undeployedReturnRequests[requestId].pool == address(0) &&
_deployedReturnRequests[requestId].pool == address(0) &&
_profitReturnRequests[requestId].pool == address(0) &&
_limitIncreaseRequests[requestId].pool == address(0)
) {
revert RequestDoesNotExist(requestId);
}
_;
}
/// @notice Checks if a request is in pending status
/// @param requestId The ID of the request to validate
modifier requestPending(uint256 requestId) {
RequestStatus status;
if (_reservationRequests[requestId].pool != address(0)) {
status = _reservationRequests[requestId].status;
} else if (_transferRequests[requestId].pool != address(0)) {
status = _transferRequests[requestId].status;
} else if (_undeployedReturnRequests[requestId].pool != address(0)) {
status = _undeployedReturnRequests[requestId].status;
} else if (_deployedReturnRequests[requestId].pool != address(0)) {
status = _deployedReturnRequests[requestId].status;
} else if (_profitReturnRequests[requestId].pool != address(0)) {
status = _profitReturnRequests[requestId].status;
} else if (_limitIncreaseRequests[requestId].pool != address(0)) {
status = _limitIncreaseRequests[requestId].status;
}
if (status != RequestStatus.Pending) {
revert RequestNotPending(requestId);
}
_;
}
/// @notice Checks if a pool is registered
/// @param pool The address of the pool to validate
modifier onlyRegisteredPool(address pool) {
if (!poolRegistry.isRegisteredPool(pool)) {
revert PoolNotRegistered(pool);
}
_;
}
/// @notice Checks if caller is the requesting pool
/// @param pool The address of the pool to validate against
modifier onlyRequestingPool(address pool) {
if (msg.sender != pool) {
revert NotRequestingPool(msg.sender, pool);
}
_;
}
/// @notice Initializes the CollateralManager contract
/// @dev Sets up initial roles and connections to other protocol contracts
/// @param initialAdmin_ Address of the initial admin
/// @param depositTokenRegistry_ Address of the DepositTokenRegistry contract
/// @param poolRegistry_ Address of the PoolRegistry contract
/// @param collateralAdmin_ Address to be granted the COLLATERAL_ADMIN role
/// @param collateralTokenHandler_ Address of the CollateralTokenHandler contract
constructor(
address initialAdmin_,
address depositTokenRegistry_,
address poolRegistry_,
address collateralAdmin_,
address collateralTokenHandler_
) {
if (initialAdmin_ == address(0)) revert InvalidAdminAddress();
if (depositTokenRegistry_ == address(0)) revert InvalidRegistryAddress();
if (poolRegistry_ == address(0)) revert InvalidPoolRegistryAddress();
if (collateralAdmin_ == address(0)) revert InvalidCollateralAdminAddress();
if (collateralTokenHandler_ == address(0))
revert InvalidTokenHandlerAddress();
depositTokenRegistry = IDepositTokenRegistry(depositTokenRegistry_);
poolRegistry = IPoolRegistry(poolRegistry_);
collateralTokenHandler = ICollateralTokenHandler(collateralTokenHandler_);
_grantRole(DEFAULT_ADMIN_ROLE, initialAdmin_);
_grantRole(COLLATERAL_ADMIN, collateralAdmin_);
}
/// @notice Calculates the value of a token amount normalized to 18 decimals
/// @dev Internal helper function to normalize token amounts to a common decimal base
/// @param token The address of the token
/// @param amount The amount to normalize
/// @return The normalized value with 18 decimals
function _getTokenValue(
address token,
uint256 amount
) internal view returns (uint256) {
// Just normalize to 18 decimals
return amount.mulDiv(1e18, 10 ** IERC20Metadata(token).decimals());
}
/// @notice Gets the total value of all collateral for a pool
/// @dev Calculates the sum of all reserved and transferred collateral, normalized to 18 decimals
/// @param pool The address of the pool to query
/// @return totalValue The total value of all collateral, normalized to 18 decimals
/// @custom:throws PoolNotRegistered if the pool is not registered
function getTotalValue(address pool) public view override returns (uint256) {
if (!poolRegistry.isRegisteredPool(pool)) revert PoolNotRegistered(pool);
uint256 totalValue = 0;
address[] memory tokens = depositTokenRegistry.getCollateralTokens();
for (uint i = 0; i < tokens.length; i++) {
address token = tokens[i];
uint256 transferred = _transferredAmounts[pool][token];
if (transferred > 0) {
totalValue += _getTokenValue(token, transferred);
}
uint256 reserved = _approvedReservationsByPoolToken[pool][token];
if (reserved > 0) {
totalValue += _getTokenValue(token, reserved);
}
}
return totalValue;
}
/// @notice Functions for managing collateral reservations
/// @dev Reservations allow pools to request and lock collateral before transferring
/// @dev The reservation process has 3 steps:
/// @dev 1. Pool requests reservation via requestCollateralReservation()
/// @dev 2. Admin approves/rejects via approveCollateralReservation()
/// @dev 3. Pool requests transfer of approved amount via requestCollateralTransfer()
/// @notice Requests a reservation of collateral tokens for a pool
/// @dev Only callable by registered pools within their collateral limit
/// @param token The address of the collateral token to reserve
/// @param amount The amount of tokens to reserve
/// @return reservationRequestId The unique identifier for the reservation request
/// @custom:throws PoolNotRegistered if the caller is not a registered pool
/// @custom:throws PendingRequestExists if there's already a pending request for this pool-token pair
function requestCollateralReservation(
address token,
uint256 amount
) external whenNotPaused returns (uint256 reservationRequestId) {
address pool = msg.sender;
if (!poolRegistry.isRegisteredPool(pool)) revert PoolNotRegistered(pool);
if (!depositTokenRegistry.isEligibleAsCollateral(token))
revert TokenNotEligibleAsCollateral();
if (token == address(0)) revert InvalidTokenAddress();
if (amount == 0) revert AmountMustBeGreaterThanZero();
if (collateralTokenHandler.getAvailableBalance(token) < amount)
revert InsufficientTokenBalance();
uint256 limit = IPool(pool).getCollateralLimit();
uint256 totalValue = getTotalValue(pool);
uint256 newRequestValue = _getTokenValue(token, amount);
if (totalValue + newRequestValue > limit)
revert TotalCollateralExceedsLimit();
// Track the new reservation
_totalReservedByToken[token] += amount;
// Check if there's already a pending request for this pool-token pair
uint256 existingRequestId = _pendingReservationsByPoolToken[pool][token];
if (
existingRequestId != 0 &&
_reservationRequests[existingRequestId].status == RequestStatus.Pending
) {
revert PendingRequestAlreadyExists();
}
reservationRequestId = ++_nextRequestId;
_reservationRequests[reservationRequestId] = ReservationRequest({
pool: pool,
token: token,
amount: amount,
status: RequestStatus.Pending
});
// Track the pending request
_pendingReservationsByPoolToken[pool][token] = reservationRequestId;
emit CollateralReservationRequested(
reservationRequestId,
pool,
token,
amount
);
return reservationRequestId;
}
/// @notice Cancels a pending collateral reservation request
/// @dev Only callable by the pool that created the request
/// @param requestId The ID of the reservation request to cancel
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws NotRequestingPool if caller is not the requesting pool
function cancelCollateralReservation(
uint256 requestId
) external whenNotPaused requestExists(requestId) requestPending(requestId) {
ReservationRequest storage request = _reservationRequests[requestId];
if (msg.sender != request.pool) revert OnlyPoolCanCancel();
request.status = RequestStatus.Cancelled;
// Clear the pending request tracking
_pendingReservationsByPoolToken[request.pool][request.token] = 0;
// Update reserved amount on cancellation
_totalReservedByToken[request.token] -= request.amount;
emit CollateralReservationCancelled(requestId, msg.sender);
}
/// @notice Approves a collateral reservation request
/// @dev Only callable by addresses with COLLATERAL_ADMIN role
/// @param requestId The ID of the reservation request to approve
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws PoolNotRegistered if the requesting pool is not registered
function approveCollateralReservation(
uint256 requestId
)
external
whenNotPaused
nonReentrant
onlyRole(COLLATERAL_ADMIN)
requestExists(requestId)
requestPending(requestId)
{
ReservationRequest storage request = _reservationRequests[requestId];
if (!poolRegistry.isRegisteredPool(request.pool))
revert PoolNotRegistered(request.pool);
if (!depositTokenRegistry.isEligibleAsCollateral(request.token))
revert TokenNotEligibleAsCollateral();
request.status = RequestStatus.Approved;
// Clear the pending request tracking
_pendingReservationsByPoolToken[request.pool][request.token] = 0;
// Add to approved reservations
_approvedReservationsByPoolToken[request.pool][request.token] += request
.amount;
// Update reserved amount before transfer
_totalReservedByToken[request.token] -= request.amount;
collateralTokenHandler.lockCollateralTokens(request.token, request.amount);
emit CollateralReservationApproved(requestId, msg.sender);
}
/// @notice Rejects a collateral reservation request
/// @dev Only callable by addresses with COLLATERAL_ADMIN role
/// @param requestId The ID of the reservation request to reject
/// @param reason The reason for rejection
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
function rejectCollateralReservation(
uint256 requestId,
string calldata reason
) external whenNotPaused onlyRole(COLLATERAL_ADMIN) {
ReservationRequest storage request = _reservationRequests[requestId];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
if (request.status != RequestStatus.Pending)
revert RequestNotPending(requestId);
request.status = RequestStatus.Rejected;
// Clear the pending request tracking
_pendingReservationsByPoolToken[request.pool][request.token] = 0;
// Update reserved amount on rejection
_totalReservedByToken[request.token] -= request.amount;
emit CollateralReservationRejected(requestId, msg.sender, reason);
}
/// @notice Gets the total amount of tokens reserved for a pool
/// @dev Returns the amount of tokens that have been approved for reservation but not yet transferred
/// @param pool The address of the pool to query
/// @param token The address of the token to query
/// @return amount The total amount of tokens currently reserved
/// @custom:throws PoolNotRegistered if the pool address is not registered
function getReservedAmount(
address pool,
address token
) external view returns (uint256 amount) {
if (!poolRegistry.isRegisteredPool(pool)) revert PoolNotRegistered(pool);
return _approvedReservationsByPoolToken[pool][token];
}
/////////////// Collateral Transfer Functions ////////////////////
/// @notice Requests a transfer of previously reserved collateral to a pool
/// @dev Only callable by registered pools with approved reservations
/// @param token The address of the collateral token to transfer
/// @param amount The amount of tokens to transfer
/// @return transferRequestId The unique identifier for the transfer request
/// @custom:throws PoolNotRegistered if the caller is not a registered pool
/// @custom:throws PendingRequestExists if there's already a pending transfer request for this pool-token pair
function requestCollateralTransfer(
address token,
uint256 amount
) external whenNotPaused returns (uint256 transferRequestId) {
address pool = msg.sender;
if (!poolRegistry.isRegisteredPool(pool)) revert PoolNotRegistered(pool);
if (token == address(0)) revert InvalidTokenAddress();
if (amount == 0) revert AmountMustBeGreaterThanZero();
if (_approvedReservationsByPoolToken[pool][token] < amount)
revert InsufficientReservedAmount();
if (IERC20(token).balanceOf(address(collateralTokenHandler)) < amount)
revert InsufficientBalanceForTransfer();
uint256 existingRequestId = _pendingTransfersByPoolToken[pool][token];
if (
existingRequestId != 0 &&
_transferRequests[existingRequestId].status == RequestStatus.Pending
) revert PendingRequestExists("transfer");
transferRequestId = ++_nextRequestId;
_transferRequests[transferRequestId] = TransferRequest({
pool: pool,
token: token,
amount: amount,
status: RequestStatus.Pending
});
_pendingTransfersByPoolToken[pool][token] = transferRequestId;
emit CollateralTransferRequested(transferRequestId, pool, token, amount);
return transferRequestId;
}
/// @notice Approves a collateral transfer request and executes the transfer
/// @dev Only callable by addresses with COLLATERAL_ADMIN role
/// @dev Transfers tokens from the collateral token handler to the requesting pool
/// @param requestId The ID of the transfer request to approve
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws PoolNotRegistered if the requesting pool is not registered
function approveCollateralTransfer(
uint256 requestId
) external whenNotPaused onlyRole(COLLATERAL_ADMIN) nonReentrant {
// All checks first
TransferRequest storage request = _transferRequests[requestId];
require(request.pool != address(0), "Request does not exist");
require(request.status == RequestStatus.Pending, "Request is not pending");
require(poolRegistry.isRegisteredPool(request.pool), "Pool not registered");
uint256 reservedAmount = _approvedReservationsByPoolToken[request.pool][
request.token
];
require(reservedAmount >= request.amount, "Insufficient reserved amount");
require(
IERC20(request.token).balanceOf(address(collateralTokenHandler)) >=
request.amount,
"Insufficient balance for collateral transfer"
);
// Cache values to avoid state changes after external call
address token = request.token;
address pool = request.pool;
uint256 amount = request.amount;
// All state changes
request.status = RequestStatus.Approved;
_pendingTransfersByPoolToken[pool][token] = 0;
_approvedReservationsByPoolToken[pool][token] -= amount;
_transferredAmounts[pool][token] += amount;
emit CollateralTransferApproved(requestId, msg.sender);
// External interaction last
collateralTokenHandler.transferTokens(token, pool, amount);
collateralTokenHandler.unlockCollateralTokens(token, amount);
}
/// @notice Cancels a pending collateral transfer request
/// @dev Only callable by the pool that created the request
/// @param requestId The ID of the transfer request to cancel
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws NotRequestingPool if caller is not the requesting pool
function cancelCollateralTransfer(
uint256 requestId
) external whenNotPaused requestExists(requestId) requestPending(requestId) {
TransferRequest storage request = _transferRequests[requestId];
if (msg.sender != request.pool)
revert NotRequestingPool(msg.sender, request.pool);
request.status = RequestStatus.Cancelled;
_pendingTransfersByPoolToken[request.pool][request.token] = 0;
emit CollateralTransferCancelled(requestId, msg.sender);
}
/// @notice Gets the details of a collateral transfer request
/// @dev Returns all relevant information about a specific transfer request
/// @param requestId The ID of the request to query
/// @return pool The address of the requesting pool
/// @return token The address of the token being transferred
/// @return amount The amount of tokens requested for transfer
/// @return status The current status of the request
/// @custom:throws RequestDoesNotExist if the request ID is invalid
function getTransferRequest(
uint256 requestId
)
external
view
returns (address pool, address token, uint256 amount, RequestStatus status)
{
TransferRequest storage request = _transferRequests[requestId];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
return (request.pool, request.token, request.amount, request.status);
}
/// @notice Requests to return undeployed collateral back to the provider
/// @dev Only callable by registered pools with transferred collateral
/// @dev Undeployed collateral is collateral that hasn't been used in any strategy
/// @param token The address of the token to return
/// @param amount The amount of tokens to return
/// @return requestId The unique identifier for the return request
/// @custom:throws PoolNotRegistered if the caller is not a registered pool
/// @custom:throws PendingRequestExists if there's already a pending return request for this pool-token pair
function requestUndeployedCollateralReturn(
address token,
uint256 amount
)
external
whenNotPaused
onlyRegisteredPool(msg.sender)
returns (uint256 requestId)
{
address pool = msg.sender;
if (token == address(0)) revert InvalidTokenAddress();
if (amount == 0) revert AmountMustBeGreaterThanZero();
if (_transferredAmounts[pool][token] < amount)
revert AmountExceedsTransferred();
uint256 existingRequestId = _pendingUndeployedReturnsByPoolToken[pool][
token
];
if (
existingRequestId != 0 &&
_undeployedReturnRequests[existingRequestId].status ==
RequestStatus.Pending
) revert PendingReturnRequestExists();
requestId = ++_nextRequestId;
_undeployedReturnRequests[requestId] = UndeployedReturnRequest({
pool: pool,
token: token,
amount: amount,
total: amount,
status: RequestStatus.Pending
});
_pendingUndeployedReturnsByPoolToken[pool][token] = requestId;
emit UndeployedCollateralReturnRequested(
requestId,
pool,
token,
amount,
amount
);
return requestId;
}
/// @notice Requests to return deployed collateral back to the provider
/// @dev Only callable by registered pools with transferred collateral
/// @dev Deployed collateral is collateral that has been used in strategies and may return in a different token
/// @param transferredToken The address of the token that was originally transferred
/// @param returnToken The address of the token being returned (may be different from transferred)
/// @param netAmount The amount of tokens to return (excluding fees)
/// @param fees The amount of fees to be kept by the pool
/// @return requestId The unique identifier for the return request
/// @custom:throws PoolNotRegistered if the caller is not a registered pool
/// @custom:throws PendingRequestExists if there's already a pending return request for this pool-token pair
function requestDeployedCollateralReturn(
address transferredToken,
address returnToken,
uint256 netAmount,
uint256 fees
)
external
whenNotPaused
onlyRegisteredPool(msg.sender)
returns (uint256 requestId)
{
address pool = msg.sender;
// require(poolRegistry.isRegisteredPool(pool), "Pool not registered");
uint256 totalAmount = netAmount + fees;
// Convert using ConvertDecimals
uint8 fromDecimals = IERC20Metadata(transferredToken).decimals();
uint8 toDecimals = IERC20Metadata(returnToken).decimals();
// First normalize the total amount to 18 decimals
uint256 normalizedTotal = totalAmount.convertTo18(fromDecimals);
// Then convert to the target token's decimals
uint256 convertedTotal = normalizedTotal.convertFrom18(toDecimals);
// Calculate the proportional converted amount and fees
uint256 convertedNetAmount = (netAmount * convertedTotal) / totalAmount;
uint256 convertedFees = convertedTotal - convertedNetAmount;
uint256 existingRequestId = _pendingDeployedReturnsByPoolToken[pool][
returnToken
];
require(
existingRequestId == 0 ||
_deployedReturnRequests[existingRequestId].status !=
RequestStatus.Pending,
"Pending return request already exists for pool-token pair"
);
requestId = ++_nextRequestId;
_deployedReturnRequests[requestId] = DeployedReturnRequest({
pool: pool,
transferredToken: transferredToken,
returnToken: returnToken,
netAmount: convertedNetAmount, // Store the converted amount
fees: convertedFees, // Store the converted fees
total: convertedTotal, // Store the converted total
status: RequestStatus.Pending
});
_pendingDeployedReturnsByPoolToken[pool][returnToken] = requestId;
emit DeployedCollateralReturnRequested(
requestId,
pool,
returnToken,
convertedNetAmount,
convertedFees,
convertedTotal
);
return requestId;
}
/// @notice Approves an undeployed collateral return request
/// @dev Only callable by addresses with COLLATERAL_ADMIN role
/// @dev Transfers tokens from the pool back to the collateral token handler
/// @param requestId The ID of the return request to approve
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws PoolNotRegistered if the requesting pool is not registered
function approveUndeployedCollateralReturn(
uint256 requestId
)
external
whenNotPaused
onlyRole(COLLATERAL_ADMIN)
requestExists(requestId)
requestPending(requestId)
{
UndeployedReturnRequest storage request = _undeployedReturnRequests[
requestId
];
if (!poolRegistry.isRegisteredPool(request.pool))
revert PoolNotRegistered(request.pool);
// Cache values
address token = request.token;
address pool = request.pool;
uint256 total = request.total;
uint256 amount = request.amount;
// All state changes
request.status = RequestStatus.Approved;
_pendingUndeployedReturnsByPoolToken[pool][token] = 0;
_transferredAmounts[pool][token] -= total;
_returnedAmounts[pool][token] += amount;
emit UndeployedCollateralReturnApproved(requestId, msg.sender, total);
// External interaction last
collateralTokenHandler.receiveTokens(token, pool, total);
}
/// @notice Gets the details of an undeployed collateral return request
/// @dev Returns all relevant information about a specific undeployed return request
/// @param requestId The ID of the request to query
/// @return pool The address of the requesting pool
/// @return token The address of the token being returned
/// @return amount The amount of tokens requested to return (excluding fees)
/// @return total The total amount of tokens to be returned (including fees)
/// @return status The current status of the request
/// @custom:throws RequestDoesNotExist if the request ID is invalid
function getUndeployedReturnRequest(
uint256 requestId
)
external
view
returns (
address pool,
address token,
uint256 amount,
uint256 total,
RequestStatus status
)
{
UndeployedReturnRequest storage request = _undeployedReturnRequests[
requestId
];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
return (
request.pool,
request.token,
request.amount,
request.total,
request.status
);
}
/// @notice Approves a deployed collateral return request
/// @dev Only callable by addresses with COLLATERAL_ADMIN role
/// @dev Transfers tokens from the pool back to the collateral token handler, accounting for fees
/// @param requestId The ID of the return request to approve
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws PoolNotRegistered if the requesting pool is not registered
function approveDeployedCollateralReturn(
uint256 requestId
)
external
whenNotPaused
onlyRole(COLLATERAL_ADMIN)
requestExists(requestId)
requestPending(requestId)
{
DeployedReturnRequest storage request = _deployedReturnRequests[requestId];
if (!poolRegistry.isRegisteredPool(request.pool))
revert PoolNotRegistered(request.pool);
uint8 fromDecimals = IERC20Metadata(request.returnToken).decimals();
uint8 toDecimals = IERC20Metadata(request.transferredToken).decimals();
uint256 normalizedAmount = request.total.convertTo18(fromDecimals);
uint256 transferredValue = normalizedAmount.convertFrom18(toDecimals);
// Update state
_transferredAmounts[request.pool][
request.transferredToken
] -= transferredValue;
_returnedAmounts[request.pool][
request.transferredToken
] += transferredValue;
request.status = RequestStatus.Approved;
_pendingDeployedReturnsByPoolToken[request.pool][request.returnToken] = 0;
collateralTokenHandler.receiveTokens(
request.returnToken,
request.pool,
request.netAmount
);
emit DeployedCollateralReturnApproved(
requestId,
msg.sender,
transferredValue
);
}
/// @notice Gets the total amount of tokens transferred to a pool
/// @dev Returns the cumulative amount of a specific token transferred to a pool
/// @param pool The address of the pool to query
/// @param token The address of the token to query
/// @return amount The total amount of tokens transferred
function getTransferredAmount(
address pool,
address token
) external view returns (uint256) {
return _transferredAmounts[pool][token];
}
/// @notice Gets the details of a deployed collateral return request
/// @dev Returns all relevant information about a specific return request
/// @param requestId The ID of the request to query
/// @return transferredToken The address of the token that was originally transferred
/// @return netAmount The amount of tokens requested to return (excluding fees)
/// @return fees The amount of fees to be kept by the pool
/// @return status The current status of the request
/// @custom:throws RequestDoesNotExist if the request ID is invalid
function getDeployedCollateralReturnRequest(
uint256 requestId
)
external
view
returns (
address transferredToken,
uint256 netAmount,
uint256 fees,
RequestStatus status
)
{
DeployedReturnRequest storage request = _deployedReturnRequests[requestId];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
return (
request.transferredToken,
request.netAmount,
request.fees,
request.status
);
}
///////////////// Profit Return Functions ////////////////
/// @notice Requests to return profits generated from collateral deployment
/// @dev Only callable by registered pools with transferred collateral
/// @dev Profits are amounts above the original transferred collateral amount
/// @param token The address of the token to return profits in
/// @param netAmount The amount of profits to return (excluding fees)
/// @param totalFees The total amount of fees to be kept by the pool
/// @return requestId The unique identifier for the profit return request
/// @custom:throws PoolNotRegistered if the caller is not a registered pool
/// @custom:throws PendingRequestExists if there's already a pending profit return request for this pool-token pair
function requestProfitReturn(
address token,
uint256 netAmount,
uint256 totalFees
)
external
whenNotPaused
onlyRegisteredPool(msg.sender)
returns (uint256 requestId)
{
address pool = msg.sender;
if (pool == address(0)) revert InvalidTokenAddress();
if (token == address(0)) revert InvalidTokenAddress();
if (netAmount == 0) revert AmountMustBeGreaterThanZero();
if (!poolRegistry.isRegisteredPool(pool)) revert PoolNotRegistered(pool);
if (!depositTokenRegistry.isEligibleAsCollateral(token))
revert TokenNotEligibleAsCollateral();
// Check all collateral tokens
address[] memory tokens = depositTokenRegistry.getCollateralTokens();
for (uint i = 0; i < tokens.length; i++) {
address collateralToken = tokens[i];
uint256 transferred = _transferredAmounts[pool][collateralToken];
if (transferred > 0) {
uint256 tokenDecimals = IERC20Metadata(collateralToken).decimals();
uint256 dustDecimals = Math.max(
tokenDecimals / 2 + 3,
tokenDecimals > MINIMUM_DUST_DECIMALS
? tokenDecimals - MINIMUM_DUST_DECIMALS
: 0
);
uint256 dust = 10 ** dustDecimals;
if (transferred > dust) revert CollateralNotFullyReturned();
}
}
uint256 existingRequestId = _pendingProfitReturnsByPoolToken[pool][token];
if (
existingRequestId != 0 &&
_profitReturnRequests[existingRequestId].status == RequestStatus.Pending
) {
revert PendingRequestExists("profit return");
}
requestId = ++_nextRequestId;
_profitReturnRequests[requestId] = ProfitReturnRequest({
pool: pool,
token: token,
netAmount: netAmount,
fees: totalFees,
total: netAmount + totalFees,
status: RequestStatus.Pending
});
_pendingProfitReturnsByPoolToken[pool][token] = requestId;
emit ProfitReturnRequested(
requestId,
pool,
token,
netAmount,
totalFees,
netAmount + totalFees,
block.timestamp
);
return requestId;
}
function cancelProfitReturn(
uint256 requestId
) external whenNotPaused requestExists(requestId) requestPending(requestId) {
ProfitReturnRequest storage request = _profitReturnRequests[requestId];
if (msg.sender != request.pool)
revert NotRequestingPool(msg.sender, request.pool);
request.status = RequestStatus.Cancelled;
_pendingProfitReturnsByPoolToken[request.pool][request.token] = 0;
emit ProfitReturnCancelled(requestId, msg.sender, block.timestamp);
}
/// @notice Approves a profit return request
/// @dev Only callable by addresses with COLLATERAL_ADMIN role
/// @dev Transfers profit tokens from the pool back to the collateral token handler
/// @param requestId The ID of the profit return request to approve
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws PoolNotRegistered if the requesting pool is not registered
function approveProfitReturn(
uint256 requestId
)
external
whenNotPaused
onlyRole(COLLATERAL_ADMIN)
requestExists(requestId)
requestPending(requestId)
{
ProfitReturnRequest storage request = _profitReturnRequests[requestId];
if (!poolRegistry.isRegisteredPool(request.pool))
revert PoolNotRegistered(request.pool);
request.status = RequestStatus.Approved;
_pendingProfitReturnsByPoolToken[request.pool][request.token] = 0;
// Update profit returns tracking - use the net amount (excluding fees)
_profitReturnsAmounts[request.pool][request.token] += request.netAmount;
// Replace direct transfer with handler call - transfer only the net amount
collateralTokenHandler.receiveTokens(
request.token,
request.pool,
request.netAmount
);
emit ProfitReturnApproved(requestId, msg.sender);
}
/// @notice Rejects a profit return request
/// @dev Only callable by addresses with COLLATERAL_ADMIN role
/// @param requestId The ID of the profit return request to reject
/// @param reason The reason for rejection
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws PoolNotRegistered if the requesting pool is not registered
function rejectProfitReturn(
uint256 requestId,
string calldata reason
) external whenNotPaused onlyRole(COLLATERAL_ADMIN) {
ProfitReturnRequest storage request = _profitReturnRequests[requestId];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
if (request.status != RequestStatus.Pending)
revert RequestNotPending(requestId);
if (!poolRegistry.isRegisteredPool(request.pool))
revert PoolNotRegistered(request.pool);
request.status = RequestStatus.Rejected;
_pendingProfitReturnsByPoolToken[request.pool][request.token] = 0;
emit ProfitReturnRejected(requestId, msg.sender, reason);
}
/// @notice Gets the details of a profit return request
/// @dev Returns all relevant information about a specific profit return request
/// @param requestId The ID of the request to query
/// @return token The address of the profit token
/// @return netAmount The amount of profits requested to return (excluding fees)
/// @return fees The amount of fees to be kept by the pool
/// @return total The total amount of profits to be returned
/// @return status The current status of the request
/// @custom:throws RequestDoesNotExist if the request ID is invalid
function getProfitReturnRequest(
uint256 requestId
)
external
view
returns (
address token,
uint256 netAmount,
uint256 fees,
uint256 total,
RequestStatus status
)
{
ProfitReturnRequest storage request = _profitReturnRequests[requestId];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
return (
request.token,
request.netAmount,
request.fees,
request.total,
request.status
);
}
/// @notice Gets the total amount of profits returned by a pool for a specific token
/// @dev Returns the cumulative amount of profits returned in a specific token
/// @param pool The address of the pool to query
/// @param token The address of the token to query
/// @return amount The total amount of profits returned
function getProfitReturnsAmount(
address pool,
address token
) external view returns (uint256) {
return _profitReturnsAmounts[pool][token];
}
/**
* @dev Returns the total value of all returned collateral for a pool
* @param pool The address of the pool
* @return totalReturned The total value of returned collateral normalized to 18 decimals
*/
function getTotalReturnedCollateral(
address pool
) external view returns (uint256 totalReturned) {
if (!poolRegistry.isRegisteredPool(pool)) revert PoolNotRegistered(pool);
// Get all possible collateral tokens
address[] memory tokens = depositTokenRegistry.getCollateralTokens();
for (uint i = 0; i < tokens.length; i++) {
address token = tokens[i];
uint256 returnedAmount = _returnedAmounts[pool][token];
if (returnedAmount > 0) {
uint256 value = _getTokenValue(token, returnedAmount);
totalReturned += value;
}
}
return totalReturned;
}
function supportsInterface(
bytes4 interfaceId
)
public
view
virtual
override(AccessControl, ERC165, IERC165)
returns (bool)
{
return
interfaceId == type(ICollateralProvider).interfaceId ||
interfaceId == type(ICollateralAdmin).interfaceId ||
super.supportsInterface(interfaceId);
}
/// @notice Functions for managing collateral limit increases
/// @dev Collateral limit increases allow pools to request higher collateral limits before their transfer date
/// @notice Requests an increase in the collateral limit for a pool
/// @dev Only callable by registered pools before their transfer date
/// @param requestedLimit The new requested collateral limit
/// @return requestId The unique identifier for the request
/// @custom:throws PoolNotRegistered if the caller is not a registered pool
/// @custom:throws PendingRequestExists if there's already a pending request for this pool
function requestCollateralLimitIncrease(
uint256 requestedLimit
)
external
whenNotPaused
onlyRegisteredPool(msg.sender)
returns (uint256 requestId)
{
address pool = msg.sender;
uint256 currentLimit = IPool(pool).getCollateralLimit();
if (requestedLimit <= currentLimit) revert MustIncreaseLimit();
uint256 transferDate = IPool(pool).getTransferDate();
if (block.timestamp >= transferDate) revert AfterTransferDate();
// Check for existing pending request
uint256 existingRequestId = _pendingLimitIncreaseByPool[pool];
if (
existingRequestId != 0 &&
_limitIncreaseRequests[existingRequestId].status == RequestStatus.Pending
) {
revert PendingRequestExists("limit increase");
}
requestId = ++_nextRequestId;
_limitIncreaseRequests[requestId] = CollateralLimitIncreaseRequest({
pool: pool,
currentLimit: currentLimit,
requestedLimit: requestedLimit,
timestamp: block.timestamp,
status: RequestStatus.Pending
});
_pendingLimitIncreaseByPool[pool] = requestId;
emit CollateralLimitIncreaseRequested(
requestId,
pool,
currentLimit,
requestedLimit,
block.timestamp
);
return requestId;
}
/// @notice Cancels a pending collateral limit increase request
/// @dev Only callable by the pool that created the request
/// @param requestId The ID of the request to cancel
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws NotRequestingPool if caller is not the requesting pool
function cancelCollateralLimitIncrease(
uint256 requestId
) external whenNotPaused {
CollateralLimitIncreaseRequest storage request = _limitIncreaseRequests[
requestId
];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
if (request.status != RequestStatus.Pending)
revert RequestNotPending(requestId);
if (msg.sender != request.pool)
revert NotRequestingPool(msg.sender, request.pool);
request.status = RequestStatus.Cancelled;
_pendingLimitIncreaseByPool[request.pool] = 0;
emit CollateralLimitIncreaseCancelled(
requestId,
msg.sender,
block.timestamp
);
}
/// @notice Approves a collateral limit increase request
/// @dev Only callable by addresses with COLLATERAL_ADMIN role. Updates the pool's collateral limit upon approval
/// @param requestId The ID of the request to approve
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws PoolNotRegistered if the requesting pool is not registered
function approveCollateralLimitIncrease(
uint256 requestId
)
external
whenNotPaused
onlyRole(COLLATERAL_ADMIN)
requestExists(requestId)
requestPending(requestId)
{
CollateralLimitIncreaseRequest storage request = _limitIncreaseRequests[
requestId
];
if (!poolRegistry.isRegisteredPool(request.pool))
revert PoolNotRegistered(request.pool);
request.status = RequestStatus.Approved;
_pendingLimitIncreaseByPool[request.pool] = 0;
// Update the pool's collateral limit
IPool(request.pool).updateCollateralLimit(request.requestedLimit);
emit CollateralLimitIncreaseApproved(
requestId,
msg.sender,
block.timestamp
);
}
/// @notice Rejects a collateral limit increase request
/// @dev Only callable by addresses with COLLATERAL_ADMIN role
/// @param requestId The ID of the request to reject
/// @param reason The reason for rejection
/// @custom:throws RequestDoesNotExist if the request ID is invalid
/// @custom:throws RequestNotPending if the request is not in pending status
/// @custom:throws PoolNotRegistered if the requesting pool is not registered
function rejectCollateralLimitIncrease(
uint256 requestId,
string calldata reason
) external whenNotPaused onlyRole(COLLATERAL_ADMIN) {
CollateralLimitIncreaseRequest storage request = _limitIncreaseRequests[
requestId
];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
if (request.status != RequestStatus.Pending)
revert RequestNotPending(requestId);
if (!poolRegistry.isRegisteredPool(request.pool))
revert PoolNotRegistered(request.pool);
request.status = RequestStatus.Rejected;
_pendingLimitIncreaseByPool[request.pool] = 0;
emit CollateralLimitIncreaseRejected(
requestId,
msg.sender,
reason,
block.timestamp
);
}
/// @notice Retrieves details of a collateral limit increase request
/// @dev Returns all relevant information about a specific request
/// @param requestId The ID of the request to query
/// @return pool The address of the requesting pool
/// @return currentLimit The pool's current collateral limit
/// @return requestedLimit The requested new limit
/// @return status The current status of the request
/// @return timestamp The timestamp when the request was created
/// @custom:throws RequestDoesNotExist if the request ID is invalid
function getCollateralLimitIncreaseRequest(
uint256 requestId
)
external
view
returns (
address pool,
uint256 currentLimit,
uint256 requestedLimit,
RequestStatus status,
uint256 timestamp
)
{
CollateralLimitIncreaseRequest storage request = _limitIncreaseRequests[
requestId
];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
return (
request.pool,
request.currentLimit,
request.requestedLimit,
request.status,
request.timestamp
);
}
function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_pause();
emit CollateralManagerPaused(msg.sender);
}
function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
_unpause();
emit CollateralManagerUnpaused(msg.sender);
}
/// @notice Gets the details of a collateral reservation request
/// @dev Returns all relevant information about a specific reservation request
/// @param requestId The ID of the request to query
/// @return pool The address of the requesting pool
/// @return token The address of the token to reserve
/// @return amount The amount requested to reserve
/// @return status The current status of the request
/// @custom:throws RequestDoesNotExist if the request ID is invalid
function getReservationRequest(
uint256 requestId
)
external
view
returns (address pool, address token, uint256 amount, RequestStatus status)
{
ReservationRequest storage request = _reservationRequests[requestId];
if (request.pool == address(0)) revert RequestDoesNotExist(requestId);
return (request.pool, request.token, request.amount, request.status);
}
function getCollateralTokenHandler() external view returns (address) {
return address(collateralTokenHandler);
}
}// SPDX-License-Identifier: MIT pragma solidity 0.8.20; /** * @dev Error thrown when an operation is attempted by a user without KYC approval */ error OnlyApprovedKYCUser(); /** * @dev Error thrown when an operation is attempted by an address other than the collateral manager */ error OnlyCollateralManager(); /** * @dev Error thrown when attempting to deposit while deposits are disabled */ error DepositNotEnabled(); /** * @dev Error thrown when attempting to deposit a token that is not enabled * @param token The token address that is not enabled for deposit */ error TokenNotEnabledForDeposit(address token); /** * @dev Error thrown when attempting to deposit native token with zero value */ error NoNativeTokenSent(); /** * @dev Error thrown when an invalid token address is provided * @param token The invalid token address */ error InvalidToken(address token); /** * @dev Error thrown when an invalid amount is provided * @param amount The invalid amount */ error InvalidAmount(uint256 amount); /** * @dev Error thrown when an invalid custodian is provided for a token * @param custodian The invalid custodian address * @param token The token address */ error InvalidCustodian(address custodian, address token); /** * @dev Error thrown when trying to add a custodian that is already set for a token * @param custodian The custodian address * @param token The token address */ error CustodianAlreadySet(address custodian, address token); /** * @dev Error thrown when an insufficient allowance is provided * @param sender The sender address * @param allowance The current allowance * @param needed The required allowance */ error InsufficientAllowance(address sender, uint256 allowance, uint256 needed); /** * @dev Error thrown when an invalid admin address is provided */ error InvalidAdminAddress(); /** * @dev Error thrown when an invalid KYC registry address is provided */ error InvalidKYCRegistryAddress(); /** * @dev Error thrown when an invalid operator address is provided */ error InvalidOperatorAddress(); /** * @dev Error thrown when an invalid registry address is provided */ error InvalidRegistryAddress(); /** * @dev Error thrown when an invalid calculator address is provided */ error InvalidCalculatorAddress(); /** * @dev Error thrown when an invalid collateral manager address is provided */ error InvalidCollateralManagerAddress(); /** * @dev Error thrown when an invalid redemption manager address is provided */ error InvalidRedemptionManagerAddress(); /** * @dev Error thrown when shares received is less than minimum expected * @param received The amount of shares received * @param minimum The minimum amount of shares expected */ error InsufficientShares(uint256 received, uint256 minimum); /** * @dev Error thrown when trying to set deposit state to its current value * @param depositEnabled The deposit enabled value */ error DepositStateAlreadySet(bool depositEnabled); /** * @dev Error thrown when an invalid recipient address is provided * @param recipient The invalid recipient address */ error InvalidRecipient(address recipient); /** * @dev Error thrown when attempting to claim fees when there are none */ error NoFeesToClaim(); /** * @dev Error thrown when an operation is attempted by an address other than the redemption manager */ error OnlyRedemptionManager(); /** * @dev Error thrown when trying to lock more tokens than are available * @param available The current available balance * @param required The required balance */ error InsufficientAvailableBalance(uint256 available, uint256 required); /** * @dev Error thrown when trying to unlock more collateral tokens than are locked * @param token The token address * @param locked The amount of tokens currently locked * @param requested The amount requested to unlock */ error InsufficientLockedCollateral( address token, uint256 locked, uint256 requested ); /** * @dev Error thrown when trying to transfer more redemption tokens than are locked * @param token The token address * @param locked The amount of tokens currently locked * @param requested The amount requested to transfer */ error InsufficientLockedRedeemableBalance( address token, uint256 locked, uint256 requested ); /** * @dev Error thrown when an invalid sender address is provided * @param sender The invalid sender address */ error InvalidSender(address sender); /** * @dev Error thrown when an insufficient balance is provided * @param balance The current balance * @param amount The required amount */ error InsufficientBalance(uint256 balance, uint256 amount);
//SPDX-License-Identifier:ISC
pragma solidity 0.8.20;
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IKYCRegistry} from "./interfaces/IKYCRegistry.sol";
import {IPool} from "./interfaces/IPool.sol";
import {IPoolRegistry} from "./interfaces/IPoolRegistry.sol";
import {IInsuranceCapitalLayer} from "./interfaces/IInsuranceCapitalLayer.sol";
import {IDecentralizedFund} from "./interfaces/IDecentralizedFund.sol";
import {ShareToken} from "./token/ShareToken.sol";
import {ConvertDecimals} from "./utils/ConvertDecimals.sol";
import {IDepositTokenRegistry} from "./interfaces/IDepositTokenRegistry.sol";
import {ISharePriceCalculator} from "./interfaces/ISharePriceCalculator.sol";
import {ICollateralTokenHandler} from "./interfaces/ICollateralTokenHandler.sol";
import {IWETH} from "./interfaces/IWETH.sol";
import {IRedemptionTokenHandler} from "./interfaces/IRedemptionTokenHandler.sol";
import {IDepositManager} from "./interfaces/IDepositManager.sol";
import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol";
import {ICustodyManager} from "./interfaces/ICustodyManager.sol";
import "./errors/InsuranceCapitalLayerErrors.sol";
contract InsuranceCapitalLayer is
Initializable,
ReentrancyGuardUpgradeable,
AccessControlUpgradeable,
PausableUpgradeable,
UUPSUpgradeable,
IDepositManager,
ICollateralTokenHandler,
IRedemptionTokenHandler,
ICustodyManager
{
using SafeERC20 for IERC20;
/////////////////////
/// Configuration ///
/////////////////////
/// @notice Role identifier for operators who can manage core functionality
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
/// @notice Role identifier for administrators with elevated privileges
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
/// @notice Role identifier for fee managers who can adjust and claim fees
bytes32 public constant FEE_MANAGER_ROLE = keccak256("FEE_MANAGER_ROLE");
/// @notice Role identifier for custodian managers who can add/remove custodians
bytes32 public constant CUSTODIAN_MANAGER_ROLE =
keccak256("CUSTODIAN_MANAGER_ROLE");
/// @notice Role identifier for collateral managers who handle collateral operations
bytes32 public constant COLLATERAL_MANAGER_ROLE =
keccak256("COLLATERAL_MANAGER_ROLE");
/// @notice Role identifier for users who can pause the contract
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
/// @notice Role identifier for custodians who can deposit assets back
bytes32 public constant CUSTODIAN_ROLE = keccak256("CUSTODIAN_ROLE");
/// @notice Role identifier for upgraders who can upgrade the contract
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
/// @notice ShareToken contract reference
/// @dev Immutable after deployment
ShareToken public shareToken;
/// @notice KYC registry contract reference for user verification
IKYCRegistry public kycRegistry;
/// @notice Name of the insurance capital layer
string public name;
/// @notice Symbol of the insurance capital layer
string public symbol;
/// @notice Mapping of total deposit fees collected per token
mapping(address => uint256) private _totalDepositFeeAmount;
/// @notice Last claimed deposit fee amount
/// @dev Updated only when fees are claimed
uint public lastClaimedDepositFeeAmount;
/// @notice Flag indicating if deposits are enabled
bool private _depositEnabled;
/// @notice Registry for managing deposit tokens
IDepositTokenRegistry public depositTokenRegistry;
/// @notice Calculator for share price conversions
ISharePriceCalculator public sharePriceCalculator;
/// @notice Address of the collateral manager
address public collateralManager;
/// @notice Address of the redemption manager
address public redemptionManager;
/// @notice Mapping of custodians for each token
/// @dev token address => array of custodian addresses
mapping(address => address[]) public custodians;
// Add state variables for tracking
mapping(address => uint256) public userRedemptionDeposits;
uint256 public contractShareTokenBalance;
mapping(address => uint256) private _lockedRedemptionTokens;
mapping(address => uint256) private _lockedCollateralTokens;
mapping(address => mapping(address => uint256))
private _totalDepositsFromCustodian;
mapping(address => mapping(address => uint256))
private _totalWithdrawalsToCustodian;
// Add near the top with other state variables
bool private _shareTokenSet;
// Add event for transparency
event ShareTokenSet(address indexed token);
// Add error
error ShareTokenAlreadySet();
//////////////
/// EVENTS ///
//////////////
/**
* @notice Emitted when deposit fees are claimed
* @param token The token address the fees were claimed for
* @param recipient The address that received the fees
* @param amount The amount of fees claimed
*/
event DepositFeeClaimed(
address indexed token,
address indexed recipient,
uint256 amount
);
event CollateralTokensLocked(address indexed token, uint256 amount);
event CollateralTokensUnlocked(address indexed token, uint256 amount);
/**
* @notice Emitted when a user deposits tokens into the insurance capital layer
* @param user Address of the user making the deposit
* @param token Address of the token being deposited
* @param amount Amount of tokens deposited
*/
event Deposited(address user, address token, uint amount);
/**
* @notice Emitted when tokens are deposited into a pool
* @param pool Address of the pool receiving the deposit
* @param amount Amount of tokens deposited into the pool
*/
event DepositedToPool(address pool, uint amount);
/**
* @notice Emitted when tokens are withdrawn from a pool
* @param pool Address of the pool tokens are withdrawn from
* @param amount Amount of tokens withdrawn
*/
event WithdrawnFromPool(address pool, uint amount);
/**
* @notice Emitted when the collateral manager address is updated
* @param collateralManager New address of the collateral manager
*/
event CollateralManagerUpdated(address collateralManager);
/**
* @notice Emitted when a new custodian is added for a token
* @param token Address of the token the custodian is added for
* @param custodian Address of the newly added custodian
*/
event CustodianAdded(address indexed token, address indexed custodian);
/**
* @notice Emitted when a custodian is removed for a token
* @param token Address of the token the custodian is removed from
* @param custodian Address of the removed custodian
*/
event CustodianRemoved(address indexed token, address indexed custodian);
/////////////////
/// MODIFIERS ///
/////////////////
/// @notice Modifier to ensure only KYC approved users can access certain functions
modifier onlyKYCUser() {
if (!kycRegistry.isKYCApproved(msg.sender)) {
revert OnlyApprovedKYCUser();
}
_;
}
/// @notice Modifier to validate token address
modifier validToken(address token) {
if (token == address(0)) {
revert InvalidToken(token);
}
_;
}
/// @notice Modifier to validate amount
modifier validAmount(uint256 amount) {
if (amount == 0) {
revert InvalidAmount(amount);
}
_;
}
/// @notice Modifier to validate custodian for token
modifier validCustodian(address custodian, address token) {
if (custodian == address(0) || !isCustodian(token, custodian)) {
revert InvalidCustodian(custodian, token);
}
_;
}
modifier onlyCollateralManager() {
if (msg.sender != collateralManager) {
revert OnlyCollateralManager();
}
_;
}
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize(
address initialAdmin_,
string memory tokenName,
string memory tokenSymbol,
address kycRegistry_,
address operator_,
address depositTokenRegistry_,
address _sharePriceCalculator
) public initializer {
__ReentrancyGuard_init();
__AccessControl_init();
__Pausable_init();
__UUPSUpgradeable_init();
if (initialAdmin_ == address(0)) revert InvalidAdminAddress();
if (kycRegistry_ == address(0)) revert InvalidKYCRegistryAddress();
if (operator_ == address(0)) revert InvalidOperatorAddress();
if (depositTokenRegistry_ == address(0)) revert InvalidRegistryAddress();
if (_sharePriceCalculator == address(0)) revert InvalidCalculatorAddress();
kycRegistry = IKYCRegistry(kycRegistry_);
depositTokenRegistry = IDepositTokenRegistry(depositTokenRegistry_);
sharePriceCalculator = ISharePriceCalculator(_sharePriceCalculator);
_grantRole(DEFAULT_ADMIN_ROLE, initialAdmin_);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(OPERATOR_ROLE, operator_);
_grantRole(OPERATOR_ROLE, msg.sender);
_grantRole(UPGRADER_ROLE, initialAdmin_);
}
/// @dev Authorization function for UUPS upgrade
function _authorizeUpgrade(
address newImplementation
) internal override onlyRole(UPGRADER_ROLE) {}
/**
* @notice Simulates a deposit operation without executing it
* @dev This function calculates the expected shares and fees for a potential deposit
* @param token The address of the token to deposit
* @param amount The amount of tokens to deposit
* @return shares The amount of shares that would be minted
* @return depositFees The amount of fees that would be charged
* @return isAllowed Whether the deposit would be allowed
* @return errorMessage A descriptive message if the deposit would fail, empty string if successful
*/
function previewDeposit(
address token,
uint256 amount
)
external
view
whenNotPaused
returns (
uint256 shares,
uint256 depositFees,
bool isAllowed,
string memory errorMessage
)
{
// Check if deposit is enabled
if (!_depositEnabled) {
return (0, 0, false, "Deposit is not enabled");
}
// Check if token deposit is enabled
if (!depositTokenRegistry.isDepositEnabled(token)) {
return (0, 0, false, "Token deposit not enabled");
}
// Check if amount is valid
if (amount == 0) {
return (0, 0, false, "Invalid amount");
}
// Calculate deposit fee using DepositTokenRegistry
try depositTokenRegistry.previewDepositFees(token, amount) returns (
uint256 fees
) {
depositFees = fees;
} catch Error(string memory reason) {
return (0, 0, false, reason);
} catch {
return (0, 0, false, "Error calculating fees");
}
uint256 amountAfterFee = amount - depositFees;
// Calculate shares
try sharePriceCalculator.convertToShares(token, amountAfterFee) returns (
uint256 calculatedShares
) {
shares = calculatedShares;
isAllowed = true;
errorMessage = "";
} catch Error(string memory reason) {
return (0, 0, false, reason);
} catch {
return (0, 0, false, "Error calculating shares");
}
}
function pause() external onlyRole(PAUSER_ROLE) {
_pause();
}
function unpause() external onlyRole(PAUSER_ROLE) {
_unpause();
}
// Update the _processDeposit function to use the new fee calculation
function _processDeposit(
address token,
uint amount,
uint minShares,
address receiver
) internal whenNotPaused {
if (!_depositEnabled) {
revert DepositNotEnabled();
}
if (amount == 0) {
revert InvalidAmount(amount);
}
if (!depositTokenRegistry.isDepositEnabled(token)) {
revert TokenNotEnabledForDeposit(token);
}
uint depositFeeAmount = depositTokenRegistry.previewDepositFees(
token,
amount
);
uint amountAfterFee = amount - depositFeeAmount;
uint shareAmount = sharePriceCalculator.convertToShares(
token,
amountAfterFee
);
if (shareAmount < minShares) {
revert InsufficientShares(shareAmount, minShares);
}
_totalDepositFeeAmount[token] += depositFeeAmount;
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
shareToken.mint(receiver, shareAmount);
emit Deposited(msg.sender, token, amount);
}
/**
* @dev Deposit tokens to insurance capital layer
* @param token Address of the token to deposit
* @param amount Token amount to deposit
*/
function deposit(
address token,
uint amount,
uint minShares
) external nonReentrant whenNotPaused onlyKYCUser {
_processDeposit(token, amount, minShares, msg.sender);
}
/**
* @dev Deposit native token (ETH) to insurance capital layer
* @param minShares Minimum shares expected to receive
*/
function depositNative(
uint minShares
) external payable nonReentrant whenNotPaused onlyKYCUser {
if (msg.value == 0) {
revert NoNativeTokenSent();
}
if (!_depositEnabled) {
revert DepositNotEnabled();
}
address token = depositTokenRegistry.getWrappedNative();
if (!depositTokenRegistry.isDepositEnabled(token)) {
revert TokenNotEnabledForDeposit(token);
}
uint depositFeeAmount = depositTokenRegistry.previewDepositFees(
token,
msg.value
);
uint amountAfterFee = msg.value - depositFeeAmount;
uint shareAmount = sharePriceCalculator.convertToShares(
token,
amountAfterFee
);
if (shareAmount < minShares) {
revert InsufficientShares(shareAmount, minShares);
}
_totalDepositFeeAmount[token] += depositFeeAmount;
shareToken.mint(msg.sender, shareAmount);
IWETH(token).deposit{value: msg.value}();
emit Deposited(msg.sender, token, msg.value);
}
/**
* @dev Deposit tokens to insurance capital layer
* @param token Address of the token to deposit
* @param amount Token amount to deposit
*/
function processPrestakedDeposit(
address token,
uint amount,
uint minShares,
address receiver
) external nonReentrant whenNotPaused {
if (!kycRegistry.isKYCApproved(receiver)) {
revert OnlyApprovedKYCUser();
}
_processDeposit(token, amount, minShares, receiver);
}
/**
* @dev Set Deposit enabled or not
* @param depositEnabled_ new deposit enabled value to be set
*/
function setDepositEnabled(
bool depositEnabled_
) external whenNotPaused onlyRole(OPERATOR_ROLE) {
if (_depositEnabled == depositEnabled_) {
revert DepositStateAlreadySet(depositEnabled_);
}
_depositEnabled = depositEnabled_;
}
function setKycRegistry(
address kycRegistry_
) external whenNotPaused onlyRole(OPERATOR_ROLE) {
if (kycRegistry_ == address(0)) {
revert InvalidKYCRegistryAddress();
}
kycRegistry = IKYCRegistry(kycRegistry_);
}
/**
* @notice Simulates a mint operation without executing it
* @dev This function calculates the required token amount and fees to mint a specific number of shares
* @param token The address of the token to be deposited
* @param shares The amount of shares to be minted
* @return amount The total amount of tokens required (including fees)
* @return depositFees The amount of fees that would be charged
* @return isAllowed Whether the mint operation would be allowed
* @return errorMessage A descriptive message if the mint would fail, empty string if successful
*/
function previewMint(
address token,
uint256 shares
)
external
view
whenNotPaused
returns (
uint256 amount,
uint256 depositFees,
bool isAllowed,
string memory errorMessage
)
{
// Check if deposit is enabled
if (!_depositEnabled) {
return (0, 0, false, "Deposit is not enabled");
}
// Check if token deposit is enabled
if (!depositTokenRegistry.isDepositEnabled(token)) {
return (0, 0, false, "Token deposit not enabled");
}
// Check if shares is valid
if (shares == 0) {
return (0, 0, false, "Invalid shares amount");
}
// Calculate token amount from shares without fees
uint256 baseAmount;
try sharePriceCalculator.convertFromShares(token, shares) returns (
uint256 calculatedAmount
) {
baseAmount = calculatedAmount;
} catch Error(string memory reason) {
return (0, 0, false, reason);
} catch {
return (0, 0, false, "Error calculating token amount");
}
// Calculate fees
try depositTokenRegistry.previewMintFees(token, baseAmount) returns (
uint256 fees
) {
depositFees = fees;
amount = baseAmount + fees;
isAllowed = true;
errorMessage = "";
} catch Error(string memory reason) {
return (baseAmount, 0, false, reason);
} catch {
return (baseAmount, 0, false, "Error calculating fees");
}
return (amount, depositFees, isAllowed, errorMessage);
}
/**
* @notice Converts a token amount to the equivalent number of shares
* @dev Uses the sharePriceCalculator to determine the conversion rate
* @param token The address of the token to convert from
* @param amount The amount of tokens to convert
* @return The number of shares equivalent to the given token amount
*/
function convertToShares(
address token,
uint256 amount
) external view whenNotPaused returns (uint256) {
return sharePriceCalculator.convertToShares(token, amount);
}
function convertFromShares(
address token,
uint256 shares
) external view whenNotPaused returns (uint256) {
return sharePriceCalculator.convertFromShares(token, shares);
}
function setDepositTokenRegistry(
address depositTokenRegistry_
) external whenNotPaused onlyRole(OPERATOR_ROLE) {
if (depositTokenRegistry_ == address(0)) {
revert InvalidRegistryAddress();
}
depositTokenRegistry = IDepositTokenRegistry(depositTokenRegistry_);
}
function setSharePriceCalculator(
address sharePriceCalculator_
) external whenNotPaused onlyRole(OPERATOR_ROLE) {
if (sharePriceCalculator_ == address(0)) {
revert InvalidCalculatorAddress();
}
sharePriceCalculator = ISharePriceCalculator(sharePriceCalculator_);
}
/**
* @dev Sets the collateral manager address
* @param _collateralManager The new collateral manager address
*/
function setCollateralManager(
address _collateralManager
) external whenNotPaused onlyRole(OPERATOR_ROLE) {
if (_collateralManager == address(0)) {
revert InvalidCollateralManagerAddress();
}
collateralManager = _collateralManager;
emit CollateralManagerUpdated(_collateralManager);
}
/**
* @dev Sets the redemption manager address
* @param _redemptionManager The new redemption manager address
*/
function setRedemptionManager(
address _redemptionManager
) external whenNotPaused onlyRole(OPERATOR_ROLE) {
if (_redemptionManager == address(0)) {
revert InvalidRedemptionManagerAddress();
}
redemptionManager = _redemptionManager;
}
function depositEnabled() external view returns (bool) {
return _depositEnabled;
}
/**
* @dev Add a custodian for a specific token
* @param token The address of the token
* @param custodian The address of the custodian
*/
function addCustodian(
address token,
address custodian
) external whenNotPaused onlyRole(CUSTODIAN_MANAGER_ROLE) {
if (token == address(0)) {
revert InvalidToken(token);
}
if (custodian == address(0)) {
revert InvalidCustodian(custodian, token);
}
if (isCustodian(token, custodian)) {
revert CustodianAlreadySet(custodian, token);
}
custodians[token].push(custodian);
emit CustodianAdded(token, custodian);
}
/**
* @dev Remove a custodian for a specific token
* @param token The address of the token
* @param custodian The address of the custodian
*/
function removeCustodian(
address token,
address custodian
) external whenNotPaused onlyRole(CUSTODIAN_MANAGER_ROLE) {
if (token == address(0)) {
revert InvalidToken(token);
}
if (custodian == address(0)) {
revert InvalidCustodian(custodian, token);
}
address[] storage tokenCustodians = custodians[token];
for (uint i = 0; i < tokenCustodians.length; i++) {
if (tokenCustodians[i] == custodian) {
tokenCustodians[i] = tokenCustodians[tokenCustodians.length - 1];
tokenCustodians.pop();
emit CustodianRemoved(token, custodian);
return;
}
}
revert InvalidCustodian(custodian, token);
}
/**
* @dev Check if a custodian is valid for a token
* @param token The address of the token
* @param custodian The address of the custodian
* @return True if the custodian is valid for the token, false otherwise
*/
function isCustodian(
address token,
address custodian
) public view whenNotPaused returns (bool) {
address[] memory tokenCustodians = custodians[token];
for (uint i = 0; i < tokenCustodians.length; i++) {
if (tokenCustodians[i] == custodian) {
return true;
}
}
return false;
}
/**
* @dev Get the list of custodians for a specific token
* @param token The address of the token
* @return An array of custodian addresses
*/
function getCustodiansForToken(
address token
) external view whenNotPaused returns (address[] memory) {
return custodians[token];
}
/**
* @dev Transfer a specified token and amount to the designated custodian
* @param custodian The address of the custodian
* @param token The address of the token to transfer
* @param amount The amount of tokens to transfer
*/
function withdrawToCustodian(
address custodian,
address token,
uint256 amount
)
external
onlyRole(OPERATOR_ROLE)
nonReentrant
whenNotPaused
validToken(token)
validAmount(amount)
validCustodian(custodian, token)
{
uint256 availableBalance = IERC20(token).balanceOf(address(this)) -
_lockedRedemptionTokens[token] -
_lockedCollateralTokens[token];
if (availableBalance < amount) {
revert InsufficientAvailableBalance(availableBalance, amount);
}
_totalWithdrawalsToCustodian[token][custodian] += amount;
IERC20(token).safeTransfer(custodian, amount);
emit AssetWithdrawnToCustodian(custodian, token, amount, block.timestamp);
}
/**
* @dev Receive tokens back from a custodian
* @param token The address of the token being deposited
* @param amount The amount of tokens to deposit
*/
function depositFromCustodian(
address token,
uint256 amount
)
external
nonReentrant
whenNotPaused
validToken(token)
validAmount(amount)
validCustodian(msg.sender, token)
{
if (IERC20(token).allowance(msg.sender, address(this)) < amount) {
revert InsufficientAllowance(
msg.sender,
IERC20(token).allowance(msg.sender, address(this)),
amount
);
}
_totalDepositsFromCustodian[token][msg.sender] += amount;
IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
emit AssetDepositedFromCustodian(
msg.sender,
token,
amount,
block.timestamp
);
}
// Add view functions to query the totals
function getTotalDepositsFromCustodian(
address token,
address custodian
) external view returns (uint256) {
return _totalDepositsFromCustodian[token][custodian];
}
function getTotalWithdrawalsToCustodian(
address token,
address custodian
) external view returns (uint256) {
return _totalWithdrawalsToCustodian[token][custodian];
}
/**
* @notice Get the net balance position of a custodian for a specific token
* @param token The address of the token
* @param custodian The address of the custodian
* @return withdrawals Total withdrawals to custodian
* @return deposits Total deposits from custodian
* @return netBalance Net balance position (deposits - withdrawals)
*/
function getCustodianNetPosition(
address token,
address custodian
)
external
view
returns (uint256 withdrawals, uint256 deposits, int256 netBalance)
{
withdrawals = _totalWithdrawalsToCustodian[token][custodian];
deposits = _totalDepositsFromCustodian[token][custodian];
netBalance = int256(deposits) - int256(withdrawals);
return (withdrawals, deposits, netBalance);
}
/**
* @dev Transfer tokens to a recipient
* @param token The address of the token to transfer
* @param recipient The address of the recipient
* @param amount The amount of tokens to transfer
*/
function transferTokens(
address token,
address recipient,
uint256 amount
) external override nonReentrant whenNotPaused {
if (msg.sender != collateralManager) {
revert OnlyCollateralManager();
}
if (token == address(0)) {
revert InvalidToken(token);
}
if (recipient == address(0)) {
revert InvalidRecipient(recipient);
}
if (amount == 0) {
revert InvalidAmount(amount);
}
uint256 balance = IERC20(token).balanceOf(address(this));
if (balance < amount) {
revert InsufficientAvailableBalance(balance, amount);
}
IERC20(token).safeTransfer(recipient, amount);
}
/**
* @dev Receives tokens into InsuranceCapitalLayer from a sender.
* @param token The address of the ERC20 token.
* @param from The sender address.
* @param amount The amount of tokens to receive.
*/
function receiveTokens(
address token,
address from,
uint256 amount
) external nonReentrant whenNotPaused {
if (msg.sender != collateralManager) {
revert OnlyCollateralManager();
}
if (token == address(0)) {
revert InvalidToken(token);
}
if (from == address(0)) {
revert InvalidSender(from);
}
if (amount == 0) {
revert InvalidAmount(amount);
}
if (IERC20(token).allowance(from, address(this)) < amount) {
revert InsufficientAllowance(
from,
IERC20(token).allowance(from, address(this)),
amount
);
}
IERC20(token).safeTransferFrom(from, address(this), amount);
}
/**
* @notice Receives share tokens from a user into the contract for redemption
* @param from The address sending the share tokens
* @param amount The amount of share tokens to receive
*/
function receiveShareTokens(
address from,
uint256 amount
) external override nonReentrant whenNotPaused {
if (msg.sender != redemptionManager) {
revert OnlyRedemptionManager();
}
if (from == address(0)) {
revert InvalidSender(from);
}
if (amount == 0) {
revert InvalidAmount(amount);
}
if (IERC20(address(shareToken)).allowance(from, address(this)) < amount) {
revert InsufficientAllowance(
from,
IERC20(address(shareToken)).allowance(from, address(this)),
amount
);
}
// Update tracking state
userRedemptionDeposits[from] += amount;
contractShareTokenBalance += amount;
// Transfer tokens
IERC20(address(shareToken)).safeTransferFrom(from, address(this), amount);
emit ShareTokensReceived(from, amount);
}
/**
* @notice Burns share tokens from the contract after redemption
* @param amount The amount of share tokens to burn
*/
function burnShareTokens(
uint256 amount
) external override nonReentrant whenNotPaused {
if (msg.sender != redemptionManager) {
revert OnlyRedemptionManager();
}
if (amount == 0) {
revert InvalidAmount(amount);
}
if (IERC20(address(shareToken)).balanceOf(address(this)) < amount) {
revert InsufficientBalance(
IERC20(address(shareToken)).balanceOf(address(this)),
amount
);
}
// Only update contract balance, not user deposits
contractShareTokenBalance -= amount;
// Burn tokens
shareToken.burn(address(this), amount);
emit ShareTokensBurned(amount);
}
/**
* @notice Transfers redemption tokens to a user
* @param token The address of the redemption token to transfer
* @param to The address receiving the redemption tokens
* @param amount The amount of redemption tokens to transfer
*/
function transferRedemptionTokens(
address token,
address to,
uint256 amount
) external override nonReentrant whenNotPaused {
if (msg.sender != redemptionManager) {
revert OnlyRedemptionManager();
}
if (to == address(0)) {
revert InvalidRecipient(to);
}
if (amount == 0) {
revert InvalidAmount(amount);
}
if (_lockedRedemptionTokens[token] < amount) {
revert InsufficientLockedRedeemableBalance(
token,
_lockedRedemptionTokens[token],
amount
);
}
_lockedRedemptionTokens[token] -= amount;
IERC20(token).safeTransfer(to, amount);
emit RedemptionTokensTransferred(token, to, amount);
}
/**
* @notice Transfers share tokens back to a user (e.g., on redemption cancellation)
* @param to The address receiving the share tokens back
* @param amount The amount of share tokens to transfer back
*/
function transferShareTokens(
address to,
uint256 amount
) external override nonReentrant whenNotPaused {
if (msg.sender != redemptionManager) {
revert OnlyRedemptionManager();
}
if (to == address(0)) {
revert InvalidRecipient(to);
}
if (amount == 0) {
revert InvalidAmount(amount);
}
if (IERC20(address(shareToken)).balanceOf(address(this)) < amount) {
revert InsufficientBalance(
IERC20(address(shareToken)).balanceOf(address(this)),
amount
);
}
// Update tracking state
userRedemptionDeposits[to] -= amount;
contractShareTokenBalance -= amount;
// Transfer tokens
IERC20(address(shareToken)).safeTransfer(to, amount);
emit ShareTokensTransferred(to, amount);
}
// Add events for tracking
event ShareTokensReceived(address indexed from, uint256 amount);
event ShareTokensBurned(uint256 amount);
event ShareTokensTransferred(address indexed to, uint256 amount);
/**
* @notice Returns the amount of tokens locked for redemption rounds
* @param token The token address to query
* @return lockedAmount The amount of tokens locked
* @return availableAmount The amount of tokens available for new rounds
*/
function getRedemptionTokenStatus(
address token
) external view returns (uint256 lockedAmount, uint256 availableAmount) {
lockedAmount = _lockedRedemptionTokens[token];
availableAmount = IERC20(token).balanceOf(address(this)) - lockedAmount;
return (lockedAmount, availableAmount);
}
/**
* @notice Locks redemption tokens for a round
* @dev Can only be called by the redemption manager
* @param token The token to lock
* @param amount The amount to lock
*/
function lockRedemptionTokens(
address token,
uint256 amount
) external override nonReentrant whenNotPaused {
if (msg.sender != redemptionManager) {
revert OnlyRedemptionManager();
}
uint256 currentBalance = IERC20(token).balanceOf(address(this));
uint256 requiredBalance = amount +
_lockedRedemptionTokens[token] +
_lockedCollateralTokens[token];
if (currentBalance < requiredBalance) {
revert InsufficientAvailableBalance(currentBalance, requiredBalance);
}
_lockedRedemptionTokens[token] += amount;
emit RedemptionTokensLocked(token, amount);
}
// Add events
event RedemptionTokensLocked(address indexed token, uint256 amount);
event RedemptionTokensTransferred(
address indexed token,
address indexed to,
uint256 amount
);
/**
* @notice Returns the total amount of deposit fees collected for a specific token
* @param token The address of the token to query
* @return The total amount of deposit fees for the token
*/
function getTotalDepositFeeAmount(
address token
) external view returns (uint256) {
if (token == address(0)) {
revert InvalidToken(token);
}
return _totalDepositFeeAmount[token];
}
/**
* @notice Claims accumulated deposit fees for a specific token
* @param token The address of the token to claim fees for
* @param recipient The address to receive the claimed fees
* @return amount The amount of fees claimed
*/
function claimDepositFees(
address token,
address recipient
)
external
nonReentrant
whenNotPaused
onlyRole(FEE_MANAGER_ROLE)
returns (uint256)
{
if (token == address(0)) {
revert InvalidToken(token);
}
if (recipient == address(0)) {
revert InvalidRecipient(recipient);
}
uint256 feeAmount = _totalDepositFeeAmount[token];
if (feeAmount == 0) {
revert NoFeesToClaim();
}
// Reset fee amount before transfer to prevent reentrancy
_totalDepositFeeAmount[token] = 0;
// Transfer the fees
IERC20(token).safeTransfer(recipient, feeAmount);
emit DepositFeeClaimed(token, recipient, feeAmount);
return feeAmount;
}
/**
* @dev Locks collateral tokens to prevent them from being used for redemptions
* @param token The address of the token to lock
* @param amount The amount to lock
*/
function lockCollateralTokens(
address token,
uint256 amount
) external override onlyCollateralManager whenNotPaused {
uint256 currentBalance = IERC20(token).balanceOf(address(this));
uint256 requiredBalance = amount +
_lockedCollateralTokens[token] +
_lockedRedemptionTokens[token];
if (currentBalance < requiredBalance) {
revert InsufficientAvailableBalance(currentBalance, requiredBalance);
}
_lockedCollateralTokens[token] += amount;
emit CollateralTokensLocked(token, amount);
}
/**
* @dev Unlocks previously locked collateral tokens
* @param token The address of the token to unlock
* @param amount The amount to unlock
*/
function unlockCollateralTokens(
address token,
uint256 amount
) external override onlyCollateralManager whenNotPaused {
if (_lockedCollateralTokens[token] < amount) {
revert InsufficientLockedCollateral(
token,
_lockedCollateralTokens[token],
amount
);
}
_lockedCollateralTokens[token] -= amount;
emit CollateralTokensUnlocked(token, amount);
}
/**
* @notice Returns the available balance for a given token
* @dev Available balance = total balance - locked redemption tokens - locked collateral tokens
* @param token The address of the token to query
* @return The available balance of the token
*/
function getAvailableBalance(
address token
) external view override returns (uint256) {
uint256 totalBalance = IERC20(token).balanceOf(address(this));
uint256 lockedRedemptions = _lockedRedemptionTokens[token];
uint256 lockedCollateral = _lockedCollateralTokens[token];
uint256 depositFees = _totalDepositFeeAmount[token];
return
totalBalance > (lockedRedemptions + lockedCollateral + depositFees)
? totalBalance - lockedRedemptions - lockedCollateral - depositFees
: 0;
}
// Modify the setShareToken function
function setShareToken(ShareToken token) external onlyRole(OPERATOR_ROLE) {
if (_shareTokenSet) {
revert ShareTokenAlreadySet();
}
shareToken = token;
_shareTokenSet = true;
emit ShareTokenSet(address(token));
}
// Add a storage gap for future upgrades
uint256[50] private __gap;
}// SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
/// @title Collateral Admin Interface
/// @notice Interface for administrative functions related to collateral management
/// @dev All functions are restricted to addresses with COLLATERAL_ADMIN role
interface ICollateralAdmin {
/// @notice Emitted when a collateral reservation request is approved
/// @param requestId The ID of the approved request
/// @param approver The address that approved the request
event CollateralReservationApproved(
uint256 indexed requestId,
address indexed approver
);
/// @notice Emitted when a collateral reservation request is rejected
/// @param requestId The ID of the rejected request
/// @param rejector The address that rejected the request
/// @param reason The reason for rejection
event CollateralReservationRejected(
uint256 indexed requestId,
address indexed rejector,
string reason
);
/// @notice Emitted when a collateral transfer request is approved
/// @param requestId The ID of the approved transfer request
/// @param approver The address that approved the transfer
event CollateralTransferApproved(
uint256 indexed requestId,
address indexed approver
);
/// @notice Emitted when an undeployed collateral return request is approved
/// @param requestId The ID of the approved return request
/// @param approver The address that approved the return
/// @param total The total amount of collateral being returned
event UndeployedCollateralReturnApproved(
uint256 indexed requestId,
address indexed approver,
uint256 total
);
/// @notice Emitted when a deployed collateral return request is approved
/// @param requestId The ID of the approved return request
/// @param approver The address that approved the return
/// @param total The total amount of collateral being returned
event DeployedCollateralReturnApproved(
uint256 indexed requestId,
address indexed approver,
uint256 total
);
/// @notice Emitted when a profit return request is approved
/// @param requestId The ID of the approved profit return request
/// @param approver The address that approved the return
event ProfitReturnApproved(
uint256 indexed requestId,
address indexed approver
);
/// @notice Emitted when a profit return request is rejected
/// @param requestId The ID of the rejected request
/// @param rejector The address that rejected the request
/// @param reason The reason for rejection
event ProfitReturnRejected(
uint256 indexed requestId,
address indexed rejector,
string reason
);
/// @notice Emitted when a collateral limit increase request is approved
/// @param requestId The ID of the approved request
/// @param approver The address that approved the increase
/// @param timestamp The time when the increase was approved
event CollateralLimitIncreaseApproved(
uint256 indexed requestId,
address indexed approver,
uint256 timestamp
);
/// @notice Emitted when a collateral limit increase request is rejected
/// @param requestId The ID of the rejected request
/// @param rejector The address that rejected the request
/// @param reason The reason for rejection
/// @param timestamp The time when the increase was rejected
event CollateralLimitIncreaseRejected(
uint256 indexed requestId,
address indexed rejector,
string reason,
uint256 timestamp
);
/// @notice Approves a pending collateral reservation request
/// @param requestId The ID of the request to approve
function approveCollateralReservation(uint256 requestId) external;
/// @notice Rejects a pending collateral reservation request
/// @param requestId The ID of the request to reject
/// @param reason The reason for rejection
function rejectCollateralReservation(
uint256 requestId,
string calldata reason
) external;
/// @notice Approves a pending collateral transfer request
/// @param requestId The ID of the request to approve
function approveCollateralTransfer(uint256 requestId) external;
/// @notice Approves a pending undeployed collateral return request
/// @param requestId The ID of the request to approve
function approveUndeployedCollateralReturn(uint256 requestId) external;
/// @notice Approves a pending deployed collateral return request
/// @param requestId The ID of the request to approve
function approveDeployedCollateralReturn(uint256 requestId) external;
/// @notice Approves a pending profit return request
/// @param requestId The ID of the request to approve
function approveProfitReturn(uint256 requestId) external;
/// @notice Rejects a pending profit return request
/// @param requestId The ID of the request to reject
/// @param reason The reason for rejection
function rejectProfitReturn(
uint256 requestId,
string calldata reason
) external;
/// @notice Approves a pending collateral limit increase request
/// @param requestId The ID of the request to approve
function approveCollateralLimitIncrease(uint256 requestId) external;
/// @notice Rejects a pending collateral limit increase request
/// @param requestId The ID of the request to reject
/// @param reason The reason for rejection
function rejectCollateralLimitIncrease(
uint256 requestId,
string calldata reason
) external;
}// SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
interface ICollateralManagerFactory {
/**
* @dev Emitted when a new CollateralManager is created
* @param collateralManager Address of the newly created CollateralManager
* @param depositTokenRegistry Address of the DepositTokenRegistry used
* @param poolRegistry Address of the PoolRegistry used
* @param collateralAdmin Address that will have COLLATERAL_ADMIN role
* @param collateralTokenHandler Address of the CollateralTokenHandler used
*/
event CollateralManagerCreated(
address indexed collateralManager,
address indexed depositTokenRegistry,
address indexed poolRegistry,
address collateralAdmin,
address collateralTokenHandler
);
/**
* @dev Creates a new CollateralManager instance
* @param depositTokenRegistry Address of the DepositTokenRegistry
* @param poolRegistry Address of the PoolRegistry
* @param collateralAdmin Address that will have COLLATERAL_ADMIN role
* @param collateralTokenHandler Address of the CollateralTokenHandler
* @return The address of the newly created CollateralManager
*/
function createCollateralManager(
address initialAdmin,
address depositTokenRegistry,
address poolRegistry,
address collateralAdmin,
address collateralTokenHandler
) external returns (address);
}// SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/// @notice Enum representing the possible states of a request
/// @dev Used across all request types (reservation, transfer, return, etc.)
enum RequestStatus {
Pending,
Approved,
Rejected,
Cancelled
}
/// @title Collateral Provider Interface
/// @notice Interface for managing collateral reservations, transfers, and returns
/// @dev Implements IERC165 for interface detection
interface ICollateralProvider is IERC165 {
/// @notice Emitted when a pool requests to reserve collateral
/// @param requestId Unique identifier for the reservation request
/// @param pool Address of the requesting pool
/// @param token Address of the collateral token
/// @param amount Amount of tokens requested
event CollateralReservationRequested(
uint256 indexed requestId,
address indexed pool,
address indexed token,
uint256 amount
);
/// @notice Emitted when a pool requests to transfer reserved collateral
/// @param requestId Unique identifier for the transfer request
/// @param pool Address of the requesting pool
/// @param token Address of the collateral token
/// @param amount Amount of tokens to transfer
event CollateralTransferRequested(
uint256 indexed requestId,
address indexed pool,
address indexed token,
uint256 amount
);
/// @notice Emitted when a collateral transfer request is cancelled
/// @param requestId The ID of the cancelled transfer request
/// @param canceller The address that cancelled the transfer
event CollateralTransferCancelled(
uint256 indexed requestId,
address indexed canceller
);
/// @notice Emitted when a collateral reservation request is cancelled
/// @param requestId The ID of the cancelled reservation request
/// @param canceller The address that cancelled the reservation
event CollateralReservationCancelled(
uint256 indexed requestId,
address indexed canceller
);
/// @notice Emitted when a pool requests to return undeployed collateral
/// @param requestId The ID of the return request
/// @param pool The address of the pool requesting the return
/// @param token The address of the token being returned
/// @param amount The amount of tokens requested to return
/// @param total The total amount including any adjustments
event UndeployedCollateralReturnRequested(
uint256 indexed requestId,
address indexed pool,
address indexed token,
uint256 amount,
uint256 total
);
/// @notice Emitted when a pool requests to return deployed collateral
/// @param requestId The ID of the return request
/// @param pool The address of the pool requesting the return
/// @param token The address of the token being returned
/// @param netAmount The amount of tokens requested to return (excluding fees)
/// @param fees The amount of fees to be kept by the pool
/// @param total The total amount including fees
event DeployedCollateralReturnRequested(
uint256 indexed requestId,
address indexed pool,
address indexed token,
uint256 netAmount,
uint256 fees,
uint256 total
);
/// @notice Emitted when a pool requests to return profits
/// @param requestId The ID of the profit return request
/// @param pool The address of the pool requesting to return profits
/// @param token The address of the profit token
/// @param amount The amount of profits to return
/// @param fees The amount of fees to be kept by the pool
/// @param total The total amount including fees
/// @param timestamp The time when the request was made
event ProfitReturnRequested(
uint256 indexed requestId,
address indexed pool,
address indexed token,
uint256 amount,
uint256 fees,
uint256 total,
uint256 timestamp
);
/// @notice Emitted when a profit return request is cancelled
/// @param requestId The ID of the cancelled request
/// @param canceller The address that cancelled the request
/// @param timestamp The time when the request was cancelled
event ProfitReturnCancelled(
uint256 indexed requestId,
address indexed canceller,
uint256 timestamp
);
/// @notice Emitted when a pool requests to increase its collateral limit
/// @param requestId The ID of the limit increase request
/// @param pool The address of the pool requesting the increase
/// @param currentLimit The pool's current collateral limit
/// @param requestedLimit The new requested limit
/// @param timestamp The time when the request was made
event CollateralLimitIncreaseRequested(
uint256 indexed requestId,
address indexed pool,
uint256 currentLimit,
uint256 requestedLimit,
uint256 timestamp
);
/// @notice Emitted when a collateral limit increase request is cancelled
/// @param requestId The ID of the cancelled request
/// @param canceller The address that cancelled the request
/// @param timestamp The time when the request was cancelled
event CollateralLimitIncreaseCancelled(
uint256 indexed requestId,
address indexed canceller,
uint256 timestamp
);
/// @notice Request to reserve collateral tokens for future transfer
/// @dev Only callable by registered pools
/// @param token The address of the token to reserve
/// @param amount The amount of tokens to reserve
/// @return reservationRequestId The unique identifier for the reservation request
function requestCollateralReservation(
address token,
uint256 amount
) external returns (uint256 reservationRequestId);
/// @notice Request to transfer previously reserved collateral
/// @dev Only callable by registered pools with approved reservations
/// @param token The address of the token to transfer
/// @param amount The amount of tokens to transfer
/// @return transferRequestId The unique identifier for the transfer request
function requestCollateralTransfer(
address token,
uint256 amount
) external returns (uint256 transferRequestId);
/// @notice Cancel a pending collateral reservation request
/// @dev Only callable by the pool that created the request
/// @param requestId The ID of the request to cancel
function cancelCollateralReservation(uint256 requestId) external;
/// @notice Get the amount of tokens reserved for a pool
/// @dev Returns the amount of tokens that have been approved for reservation but not yet transferred
/// @param pool The address of the pool to query
/// @param token The address of the token to query
/// @return amount The amount of tokens currently reserved
function getReservedAmount(
address pool,
address token
) external view returns (uint256 amount);
/// @notice Request to return undeployed collateral back to the provider
/// @param token The address of the token to return
/// @param amount The amount of tokens to return
/// @return requestId The ID of the return request
function requestUndeployedCollateralReturn(
address token,
uint256 amount
) external returns (uint256 requestId);
/// @notice Request to return deployed collateral back to the provider, keeping generated fees
/// @param transferredToken The address of the token to return
/// @param returnToken The address of the token to return
/// @param amount The amount of tokens to return
/// @param fees The amount of fees to keep
/// @return requestId The ID of the return request
function requestDeployedCollateralReturn(
address transferredToken,
address returnToken,
uint256 amount,
uint256 fees
) external returns (uint256 requestId);
/// @notice Returns the total value of all collateral (reserved + transferred) for a pool
/// @param pool The address of the pool
/// @return totalValue The total value normalized to 18 decimals
function getTotalValue(
address pool
) external view returns (uint256 totalValue);
/// @notice Retrieves details of a deployed collateral return request
/// @dev Returns information about the tokens, amounts, and status
/// @param requestId The ID of the request to query
/// @return transferredToken The address of the token that was originally transferred
/// @return amount The amount of tokens requested to return
/// @return fees The amount of fees to be kept by the pool
/// @return status The current status of the request
function getDeployedCollateralReturnRequest(
uint256 requestId
)
external
view
returns (
address transferredToken,
uint256 amount,
uint256 fees,
RequestStatus status
);
/// @notice Request to return profits generated from collateral deployment
/// @dev Only callable by registered pools with deployed collateral
/// @param token The address of the profit token to return
/// @param amount The amount of profits to return
/// @param totalFees The total fees to be kept by the pool
/// @return requestId The unique identifier for the profit return request
function requestProfitReturn(
address token,
uint256 amount,
uint256 totalFees
) external returns (uint256 requestId);
/// @notice Cancel a pending profit return request
/// @dev Only callable by the pool that created the request
/// @param requestId The ID of the request to cancel
function cancelProfitReturn(uint256 requestId) external;
/// @notice Gets the details of a profit return request
/// @dev Returns all relevant information about a specific profit return request
/// @param requestId The ID of the request to query
/// @return token The address of the profit token
/// @return amount The amount of profits requested to return
/// @return fees The amount of fees to be kept by the pool
/// @return total The total amount of profits to be returned
/// @return status The current status of the request
/// @custom:throws RequestDoesNotExist if the request ID is invalid
function getProfitReturnRequest(
uint256 requestId
)
external
view
returns (
address token,
uint256 amount,
uint256 fees,
uint256 total,
RequestStatus status
);
/// @notice Get the total amount of profits returned by a pool for a specific token
/// @dev Returns the cumulative amount of profits returned
/// @param pool The address of the pool to query
/// @param token The address of the token to query
/// @return The total amount of profits returned
function getProfitReturnsAmount(
address pool,
address token
) external view returns (uint256);
/// @notice Returns the total value of all collateral returned by a pool
/// @param pool The address of the pool
/// @return totalValue The total value normalized to 18 decimals
function getTotalReturnedCollateral(
address pool
) external view returns (uint256 totalValue);
/// @notice Request to increase a pool's collateral limit
/// @dev Only callable by registered pools before their transfer date
/// @param requestedLimit The new requested collateral limit
/// @return requestId The unique identifier for the limit increase request
function requestCollateralLimitIncrease(
uint256 requestedLimit
) external returns (uint256 requestId);
/// @notice Cancel a pending collateral limit increase request
/// @dev Only callable by the pool that created the request
/// @param requestId The ID of the request to cancel
function cancelCollateralLimitIncrease(uint256 requestId) external;
/// @notice Get the details of a collateral limit increase request
/// @dev Returns all relevant information about a specific request
/// @param requestId The ID of the request to query
/// @return pool The address of the requesting pool
/// @return currentLimit The pool's current collateral limit
/// @return requestedLimit The requested new limit
/// @return status The current status of the request
/// @return timestamp The timestamp when the request was created
function getCollateralLimitIncreaseRequest(
uint256 requestId
)
external
view
returns (
address pool,
uint256 currentLimit,
uint256 requestedLimit,
RequestStatus status,
uint256 timestamp
);
/// @notice Get the address of the collateral token handler
/// @dev Returns the address of the collateral token handler
/// @return The address of the collateral token handler
function getCollateralTokenHandler() external view returns (address);
}// SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
/**
* @title IInsuranceCapitalLayerTokenHandler
* @dev Combined interface for transferring and receiving ERC20 tokens in InsuranceCapitalLayer.
*/
interface ICollateralTokenHandler {
/**
* @dev Transfers tokens from InsuranceCapitalLayer to a recipient.
* @param token The address of the ERC20 token.
* @param to The recipient address.
* @param amount The amount of tokens to transfer.
*/
function transferTokens(address token, address to, uint256 amount) external;
/**
* @dev Receives tokens into InsuranceCapitalLayer from a sender.
* @param token The address of the ERC20 token.
* @param from The sender address.
* @param amount The amount of tokens to receive.
*/
function receiveTokens(address token, address from, uint256 amount) external;
function getAvailableBalance(address token) external view returns (uint256);
/**
* @dev Locks collateral tokens to prevent them from being used for redemptions
* @param token The address of the token to lock
* @param amount The amount to lock
*/
function lockCollateralTokens(address token, uint256 amount) external;
/**
* @dev Unlocks previously locked collateral tokens
* @param token The address of the token to unlock
* @param amount The amount to unlock
*/
function unlockCollateralTokens(address token, uint256 amount) external;
}// SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
interface ICustodyManager {
// Events
event CustodianAdded(
address indexed source,
address indexed token,
address indexed custodian
);
event CustodianRemoved(
address indexed source,
address indexed token,
address indexed custodian
);
event AssetWithdrawnToCustodian(
address indexed custodian,
address indexed token,
uint256 amount,
uint256 indexed timestamp
);
event AssetDepositedFromCustodian(
address indexed custodian,
address indexed token,
uint256 amount,
uint256 indexed timestamp
);
/**
* @dev Add a custodian for a specific token
* @param token The address of the token
* @param custodian The address of the custodian
*/
function addCustodian(address token, address custodian) external;
/**
* @dev Remove a custodian for a specific token
* @param token The address of the token
* @param custodian The address of the custodian
*/
function removeCustodian(address token, address custodian) external;
/**
* @dev Check if a custodian is valid for a token
* @param token The address of the token
* @param custodian The address of the custodian
* @return True if the custodian is valid for the token, false otherwise
*/
function isCustodian(
address token,
address custodian
) external view returns (bool);
/**
* @dev Get the list of custodians for a specific token
* @param token The address of the token
* @return An array of custodian addresses
*/
function getCustodiansForToken(
address token
) external view returns (address[] memory);
/**
* @dev Transfer a specified token and amount to the designated custodian
* @param custodian The address of the custodian
* @param token The address of the token to transfer
* @param amount The amount of tokens to transfer
*/
function withdrawToCustodian(
address custodian,
address token,
uint256 amount
) external;
}//SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
interface IDecentralizedFund {}//SPDX-License-Identifier:ISC
pragma solidity 0.8.20;
interface IDepositManager {
function deposit(address token, uint256 amount, uint256 minShares) external;
function processPrestakedDeposit(
address token,
uint amount,
uint256 minShares,
address receiver
) external;
function previewDeposit(
address token,
uint256 amount
)
external
view
returns (
uint256 shares,
uint256 depositFees,
bool isAllowed,
string memory errorMessage
);
function previewMint(
address token,
uint256 shares
)
external
view
returns (
uint256 amount,
uint256 depositFees,
bool isAllowed,
string memory errorMessage
);
function setDepositEnabled(bool _depositEnabled) external;
function depositEnabled() external view returns (bool);
function convertToShares(
address token,
uint256 amount
) external view returns (uint256);
function convertFromShares(
address token,
uint256 shares
) external view returns (uint256);
}//SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
interface IDepositTokenRegistry {
struct TokenInfo {
bool isAccepted;
uint256 fixedDepositFee;
uint256 percentageDepositFee;
address priceOracle;
bool paused;
bool eligibleAsCollateral;
uint8 decimals;
string name;
string symbol;
uint256 defaultSlippageTolerance;
uint256 minDeposit;
}
function isDepositEnabled(address token) external view returns (bool);
function getTokenInfo(
address token
)
external
view
returns (
bool isAccepted,
uint256 fixedDepositFee,
uint256 percentageDepositFee,
address priceOracle,
bool paused,
string memory name,
string memory symbol,
uint256 slippage,
uint256 minDeposit,
uint256 decimals,
bool nativeToken
);
/// @notice Returns only the essential token information needed for share price calculations
/// @param token The address of the token to query
/// @return isAccepted Whether the token is accepted
/// @return paused Whether the token is paused
/// @return priceOracle The address of the price oracle for this token
/// @return decimals The number of decimals for the token
function getTokenPriceInfo(
address token
)
external
view
returns (bool isAccepted, bool paused, address priceOracle, uint8 decimals);
function getAcceptedTokens() external view returns (address[] memory);
function previewMintFees(
address token,
uint256 amount
) external view returns (uint256);
function previewDepositFees(
address token,
uint256 amount
) external view returns (uint256);
function isEligibleAsCollateral(address token) external view returns (bool);
function getCollateralTokens() external view returns (address[] memory);
function getWrappedNative() external view returns (address);
}//SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
interface IInsuranceCapitalLayer {
enum RedeemRequestStatus {
REQUESTED,
CLAIMABLE,
CLAIMED,
CANCELLED
}
struct RedeemRequestQueue {
uint id;
address requester;
uint amount;
uint requestTime;
uint claimedTime;
RedeemRequestStatus status;
uint price18;
}
// function deposit(
// address token,
// uint256 amount,
// address receiver,
// bytes32[] calldata proof
// ) external;
// function processPrestakedDeposit(
// address token,
// uint amount,
// address receiver,
// bytes32[] calldata proof
// ) external;
function depositCollateralToPool(address poolAddress_, uint amount_) external;
function withdrawUSDCFromPool(address poolAddress_, uint amount_) external;
// function previewMint(
// address token,
// uint256 shares
// )
// external
// view
// returns (
// uint256 amount,
// uint256 depositFees,
// bool isAllowed,
// string memory errorMessage
// );
function addCustodian(address token, address custodian) external;
function removeCustodian(address token, address custodian) external;
function withdrawToCustodian(
address custodian,
address token,
uint256 amount
) external;
function isCustodian(
address token,
address custodian
) external view returns (bool);
function getCustodiansForToken(
address token
) external view returns (address[] memory);
}//SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
interface IKYCRegistry {
// function isKYCUser(
// bytes32[] calldata proof,
// bytes32 leaf
// ) external view returns (bool);
function isKYCApproved(address user) external view returns (bool);
}//SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
import {PoolToken} from "../token/PoolToken.sol";
interface IPool {
struct FeePercentage {
uint management;
uint performance;
}
/// @dev Get collateral limit amount
function getCollateralLimit() external view returns (uint);
function getTransferDate() external view returns (uint);
function requestCollateralReservation(
address token,
uint amount
) external returns (uint256);
function requestCollateralTransfer(
address token,
uint amount
) external returns (uint256);
function requestProfitReturn(
address token,
uint256 amount
) external returns (uint256);
function cancelProfitReturn(uint256 requestId) external;
/// @dev Request an increase to the pool's collateral limit
function requestCollateralLimitIncrease(
uint256 requestedLimit
) external returns (uint256);
/// @dev Cancel a pending collateral limit increase request
function cancelCollateralLimitIncrease(uint256 requestId) external;
/// @dev Update the pool's collateral limit (called by CollateralProvider after approval)
function updateCollateralLimit(uint256 newLimit) external;
}//SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
interface IPoolRegistry {
event PoolRegistered(address poolAddress, address cellAddress);
event PoolUnregistered(address poolAddress, address cellAddress);
function registerPool(address poolAddress) external;
function unregisterPool(address poolAddress) external;
function isRegisteredPool(address poolAddress) external view returns (bool);
function hasCellRole(address cellAddress) external view returns (bool);
function getPoolsForCell(
address cellAddress
) external view returns (address[] memory);
function isPoolOwnedByCell(
address cellAddress,
address poolAddress
) external view returns (bool);
}pragma solidity 0.8.20;
interface IPriceOracle {
enum PriceStatus {
VALID,
CAPPED,
INVALID,
STALE,
VOLATILE,
CIRCUIT_BREAKER
}
struct PriceInfo {
uint96 price;
PriceStatus status;
}
/**
* @notice Retrieves the latest price information for the configured token
* @return PriceInfo Struct containing the price and its status
*/
function latestPriceInfo() external view returns (PriceInfo memory);
/**
* @notice Retrieves the number of decimals for the token's price feed
* @return uint8 Number of decimals
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
/**
* @title IRedemptionTokenHandler
* @notice Interface for handling token operations in the redemption process
* @dev This interface defines the methods for receiving, burning, and transferring tokens
*/
interface IRedemptionTokenHandler {
/**
* @notice Receives share tokens from a user into the contract
* @dev Called when a user requests redemption
* @param from The address sending the share tokens
* @param amount The amount of share tokens to receive
*/
function receiveShareTokens(address from, uint256 amount) external;
/**
* @notice Burns share tokens from the contract
* @dev Called when a user claims their redemption
* @param amount The amount of share tokens to burn
*/
function burnShareTokens(uint256 amount) external;
/**
* @notice Transfers redemption tokens to a user
* @dev Called when a user claims their redemption
* @param token The address of the redemption token to transfer
* @param to The address receiving the redemption tokens
* @param amount The amount of redemption tokens to transfer
*/
function transferRedemptionTokens(
address token,
address to,
uint256 amount
) external;
/**
* @notice Transfers share tokens back to a user
* @dev Called when a user cancels their redemption request
* @param to The address receiving the share tokens back
* @param amount The amount of share tokens to transfer back
*/
function transferShareTokens(address to, uint256 amount) external;
/**
* @notice Locks redemption tokens for a round
* @dev Can only be called by the redemption manager
* @param token The token to lock
* @param amount The amount to lock
*/
function lockRedemptionTokens(address token, uint256 amount) external;
}// SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
interface ISharePriceCalculator {
event SharePriceSet(uint256 oldPrice, uint256 newPrice);
function setSharePrice(uint256 newPrice) external;
function convertToShares(
address token,
uint256 amount
) external view returns (uint256);
function convertFromShares(
address token,
uint256 shares
) external view returns (uint256);
function getSharePrice() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
interface IWETH {
/**
* @dev Deposits Ether into the contract and mints WETH to the sender's address.
* The contract must be payable to accept ETH.
*/
function deposit() external payable;
/**
* @dev Withdraws WETH from the sender's balance and sends Ether to the sender.
* @param amount The amount of WETH to withdraw (converted back to ETH).
*/
function withdraw(uint256 amount) external;
/**
* @dev Returns the number of decimals used by the token.
* @return The number of decimals.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./interfaces/ISharePriceCalculator.sol";
import "./interfaces/IDepositTokenRegistry.sol";
import "./interfaces/IPriceOracle.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
/// @title SharePriceCalculator
/// @notice Handles conversion between token amounts and shares based on current share price
/// @dev Uses price oracles to determine token values and maintains a global share price
contract SharePriceCalculator is ISharePriceCalculator, AccessControl {
using Math for uint256;
error ZeroAddress();
error InvalidPrice();
error InvalidTokenAddress();
error AmountTooLow();
error TokenNotAcceptedOrPaused();
error InvalidTokenPrice();
bytes32 public constant PRICE_SETTER_ROLE = keccak256("PRICE_SETTER_ROLE");
uint256 private sharePrice;
IDepositTokenRegistry public immutable depositTokenRegistry;
/// @notice Initializes the SharePriceCalculator with a deposit token registry
/// @param _depositTokenRegistry Address of the deposit token registry contract
/// @dev Sets initial share price to 1e18 (1:1 ratio)
constructor(
address _depositTokenRegistry,
uint256 _initialSharePrice,
address _admin,
address _priceSetter
) {
if (_depositTokenRegistry == address(0)) revert ZeroAddress();
if (_initialSharePrice == 0) revert InvalidPrice();
if (_admin == address(0)) revert ZeroAddress();
if (_priceSetter == address(0)) revert ZeroAddress();
depositTokenRegistry = IDepositTokenRegistry(_depositTokenRegistry);
sharePrice = _initialSharePrice; // Initialize with 1:1 ratio
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
_grantRole(PRICE_SETTER_ROLE, _priceSetter);
}
/// @notice Updates the global share price
/// @param newPrice New share price value (scaled by 1e18)
/// @dev Only callable by addresses with PRICE_SETTER_ROLE
/// @dev Emits SharePriceSet event
function setSharePrice(
uint256 newPrice
) external onlyRole(PRICE_SETTER_ROLE) {
if (newPrice == 0) revert InvalidPrice();
uint256 oldPrice = sharePrice;
sharePrice = newPrice;
emit SharePriceSet(oldPrice, newPrice);
}
/// @notice Returns the current share price
/// @return Current share price scaled by 1e18
function getSharePrice() external view returns (uint256) {
return sharePrice;
}
/// @notice Converts a token amount to equivalent shares
/// @param token Address of the token to convert
/// @param amount Amount of tokens to convert (in token's native decimals)
/// @return Number of shares equivalent to the token amount
/// @dev Formula: shares = (amount * tokenPrice * 1e18) / (10^tokenDecimals * 10^oracleDecimals * sharePrice)
function convertToShares(
address token,
uint256 amount
) external view returns (uint256) {
if (token == address(0)) revert InvalidTokenAddress();
if (amount == 0) revert AmountTooLow();
(
bool isAccepted,
bool paused,
address priceOracle,
uint8 tokenDecimals
) = depositTokenRegistry.getTokenPriceInfo(token);
if (!isAccepted || paused) revert TokenNotAcceptedOrPaused();
IPriceOracle oracle = IPriceOracle(priceOracle);
IPriceOracle.PriceInfo memory priceInfo = oracle.latestPriceInfo();
if (
priceInfo.status != IPriceOracle.PriceStatus.VALID &&
priceInfo.status != IPriceOracle.PriceStatus.CAPPED
) revert InvalidTokenPrice();
uint256 tokenPrice = priceInfo.price;
uint8 oracleDecimals = oracle.decimals();
uint256 tokenValue = amount.mulDiv(
tokenPrice.mulDiv(1e18, 10 ** tokenDecimals),
10 ** oracleDecimals
);
return tokenValue.mulDiv(1e18, sharePrice);
}
/// @notice Converts a number of shares to equivalent token amount
/// @param token Address of the token to convert to
/// @param shares Number of shares to convert
/// @return Amount of tokens equivalent to the shares (in token's native decimals)
/// @dev Formula: tokens = (shares * sharePrice * 10^tokenDecimals * 10^oracleDecimals) / (tokenPrice * 1e36)
function convertFromShares(
address token,
uint256 shares
) external view returns (uint256) {
if (token == address(0)) revert InvalidTokenAddress();
if (shares == 0) revert AmountTooLow();
(
bool isAccepted,
bool paused,
address priceOracle,
uint8 tokenDecimals
) = depositTokenRegistry.getTokenPriceInfo(token);
if (!isAccepted || paused) revert TokenNotAcceptedOrPaused();
IPriceOracle oracle = IPriceOracle(priceOracle);
IPriceOracle.PriceInfo memory priceInfo = oracle.latestPriceInfo();
if (
priceInfo.status != IPriceOracle.PriceStatus.VALID &&
priceInfo.status != IPriceOracle.PriceStatus.CAPPED
) revert InvalidTokenPrice();
uint256 tokenPrice = priceInfo.price;
uint8 oracleDecimals = oracle.decimals();
uint256 tokenValue = shares.mulDiv(sharePrice, 1e18);
return
tokenValue.mulDiv(
10 ** tokenDecimals * 10 ** oracleDecimals,
tokenPrice * 1e18
);
}
}pragma solidity 0.8.20;
import "./SharePriceCalculator.sol";
contract SharePriceCalculatorFactory {
function createSharePriceCalculator(
address _depositTokenRegistry,
uint256 _initialSharePrice,
address _admin,
address _priceSetter
) public returns (address) {
SharePriceCalculator newCalculator = new SharePriceCalculator(
_depositTokenRegistry,
_initialSharePrice,
_admin,
_priceSetter
);
return address(newCalculator);
}
}//SPDX-License-Identifier:ISC
pragma solidity 0.8.20;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
contract PoolToken is ERC20, Ownable2Step {
uint8 private _decimals;
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
) ERC20(name_, symbol_) Ownable(msg.sender) Ownable2Step() {
_setupDecimals(decimals_);
}
function decimals() public view override returns (uint8) {
return _decimals;
}
function _setupDecimals(uint8 decimals_) internal {
_decimals = decimals_;
}
function mint(address to, uint amount) external onlyOwner {
_mint(to, amount);
}
function burn(address user, uint amount) external onlyOwner {
_burn(user, amount);
}
}//SPDX-License-Identifier:ISC
pragma solidity 0.8.20;
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
/**
* @title ShareToken
* @notice Upgradeable ERC20 token with role-based minting and burning capabilities
* @dev This contract implements an upgradeable ERC20 token with the following fea ddtures:
* - Role-based access control for minting and burning
* - UUPS upgradeability pattern
* - Secure upgrade process with safety checks
*
* Roles:
* - DEFAULT_ADMIN_ROLE: Can grant and revoke all roles
* - UPGRADER_ROLE: Can upgrade the contract implementation
* - MINTER_ROLE: Can mint and burn tokens
*
* Storage layout:
* - Initializable: uint8 private _initialized, uint8 private _initializing
* - ContextUpgradeable: empty
* - ERC20Upgradeable: mapping(address => uint256) private _balances, mapping(address => mapping(address => uint256)) private _allowances, uint256 private _totalSupply, string private _name, string private _symbol
* - AccessControlUpgradeable: mapping(bytes32 => RoleData) private _roles
* - UUPSUpgradeable: empty
* - USDRE: uint8 private _decimals
*
* Gap sizes:
* - ERC20Upgradeable: uint256[45]
* - AccessControlUpgradeable: uint256[49]
* - UUPSUpgradeable: uint256[50]
* - USDRE: uint256[48]
*
* Security considerations:`
* - Only addresses with UPGRADER_ROLE can upgrade the implementation`
* - New implementations must support IERC20 and IAccessControl interfaces
* - Storage layout must be preserved in upgrades
* - Initializers are disabled in implementation contracts
*/
contract ShareToken is
Initializable,
ERC20Upgradeable,
AccessControlUpgradeable,
UUPSUpgradeable
{
/// @notice Role that allows upgrading the contract implementation
bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");
/// @notice Role that allows minting and burning tokens
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
/// @notice Token decimals (immutable after initialization)
uint8 private _decimals;
/// @dev Error thrown when an unsupported interface is detected
error UnsupportedInterface(string interfaceName);
/// @notice Emitted when UPGRADER_ROLE is granted to an account
event UpgraderRoleGranted(address indexed account);
/// @notice Emitted when the contract implementation is upgraded
event ShareTokenUpgraded(address indexed implementation);
/// @notice Emitted when MINTER_ROLE is granted to an account
event MinterRoleGranted(address indexed account);
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @notice Initializes the ShareToken
* @dev Sets up initial roles and token parameters. Can only be called once.
* @param name_ The name of the token
* @param symbol_ The symbol of the token
* @param decimals_ The number of decimals for the token
* @param admin The address to receive the DEFAULT_ADMIN_ROLE and UPGRADER_ROLE
*/
function initialize(
string memory name_,
string memory symbol_,
uint8 decimals_,
address admin
) public initializer {
__ERC20_init(name_, symbol_);
__AccessControl_init();
__UUPSUpgradeable_init();
_decimals = decimals_;
// Set up admin roles
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(UPGRADER_ROLE, admin);
emit UpgraderRoleGranted(admin);
}
/// @notice Returns the number of decimals used for token amounts
function decimals() public view override returns (uint8) {
return _decimals;
}
/**
* @notice Mints new tokens
* @dev Only callable by addresses with MINTER_ROLE
* @param to The address to receive the minted tokens
* @param amount The amount of tokens to mint
*/
function mint(address to, uint amount) external onlyRole(MINTER_ROLE) {
_mint(to, amount);
}
/**
* @notice Burns tokens from a user's balance
* @dev Only callable by addresses with MINTER_ROLE
* @param user The address to burn tokens from
* @param amount The amount of tokens to burn
*/
function burn(address user, uint amount) external onlyRole(MINTER_ROLE) {
_burn(user, amount);
}
/**
* @notice Authorizes an upgrade to a new implementation
* @dev Includes safety checks for the new implementation
* - Verifies the new implementation is a contract
* - Verifies required interfaces are supported
* - Verifies initialization is properly disabled
* @param newImplementation Address of the new implementation
*/
function _authorizeUpgrade(
address newImplementation
) internal override onlyRole(UPGRADER_ROLE) {
// Verify the new implementation supports the required interfaces
if (
!IERC165(newImplementation).supportsInterface(type(IERC20).interfaceId)
) {
revert UnsupportedInterface("IERC20");
}
if (
!IERC165(newImplementation).supportsInterface(
type(IAccessControl).interfaceId
)
) {
revert UnsupportedInterface("IAccessControl");
}
emit ShareTokenUpgraded(newImplementation);
}
function supportsInterface(
bytes4 interfaceId
) public view virtual override(AccessControlUpgradeable) returns (bool) {
return
interfaceId == type(IERC20).interfaceId ||
interfaceId == type(IAccessControl).interfaceId ||
super.supportsInterface(interfaceId);
}
/// @dev Reserved storage space for future upgrades
uint256[50] private __gap;
}//SPDX-License-Identifier: ISC
pragma solidity 0.8.20;
import "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @title ConvertDecimals
* @author Lyra
* @dev Contract to convert amounts to and from erc20 tokens to 18 dp.
*/
library ConvertDecimals {
/// @dev Converts amount from token native dp to 18 dp. This cuts off precision for decimals > 18.
function convertTo18(
uint amount,
uint8 decimals
) internal pure returns (uint) {
return (amount * 1e18) / (10 ** decimals);
}
/// @dev Converts amount from 18dp to token.decimals(). This cuts off precision for decimals < 18.
function convertFrom18(
uint amount,
uint8 decimals
) internal pure returns (uint) {
return (amount * (10 ** decimals)) / 1e18;
}
/// @dev Converts amount from a given precisionFactor to 18 dp. This cuts off precision for decimals > 18.
function normaliseTo18(
uint amount,
uint precisionFactor
) internal pure returns (uint) {
return (amount * 1e18) / precisionFactor;
}
// Loses precision
/// @dev Converts amount from 18dp to the given precisionFactor. This cuts off precision for decimals < 18.
function normaliseFrom18(
uint amount,
uint precisionFactor
) internal pure returns (uint) {
return (amount * precisionFactor) / 1e18;
}
/// @dev Ensure a value converted from 18dp is rounded up, to ensure the value requested is covered fully.
function convertFrom18AndRoundUp(
uint amount,
uint8 assetDecimals
) internal pure returns (uint amountConverted) {
if (assetDecimals < 18) {
amount =
Math.ceilDiv(amount, 10 ** (18 - assetDecimals)) *
10 ** (18 - assetDecimals);
}
amountConverted = ConvertDecimals.convertFrom18(amount, assetDecimals);
}
}{
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"optimizer": {
"enabled": true,
"runs": 100
},
"viaIR": true,
"evmVersion": "paris",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"kycRegistry_","type":"address"},{"internalType":"address","name":"poolRegistry_","type":"address"},{"internalType":"address","name":"sharePriceCalculatorFactory_","type":"address"},{"internalType":"address","name":"collateralManagerFactory_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"icl","type":"address"},{"indexed":true,"internalType":"address","name":"shareToken","type":"address"}],"name":"InsuranceCapitalLayerCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"COLLATERAL_MANAGER_FACTORY","outputs":[{"internalType":"contract ICollateralManagerFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPLOYER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ICL_IMPLEMENTATION","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"KYC_REGISTRY","outputs":[{"internalType":"contract IKYCRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"POOL_REGISTRY","outputs":[{"internalType":"contract IPoolRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHARE_PRICE_CALCULATOR_FACTORY","outputs":[{"internalType":"contract SharePriceCalculatorFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SHARE_TOKEN_IMPLEMENTATION","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"depositTokenRegistry","type":"address"},{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"address","name":"admin","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"initialSharePrice","type":"uint256"}],"name":"createInsuranceCapitalLayer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
61014034620001ae576001600160401b0390601f6200774c38819003918201601f1916830191908483118484101762000198578160809285926040958652833981010312620001ae576200005382620001b3565b6200006160208401620001b3565b906200007d606062000075858701620001b3565b9501620001b3565b6001600160a01b0391821660805291811660a05292831660c052821660e0528051614a36808201939190858511828610176200019857620012e2823980600094039084f09384156200018c578161010095168552825190611a348083019183831090831117620001785790829162005d188339039084f09283156200016d5750610120921682526200010f33620001c8565b50519061108892836200025a843960805183818161040f01526108d5015260a05183610ab3015260c0518381816101760152610382015260e051836109aa015251828181610489015261091c01525181818161030801526109630152f35b8251903d90823e3d90fd5b634e487b7160e01b86526041600452602486fd5b505051903d90823e3d90fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b51906001600160a01b0382168203620001ae57565b6001600160a01b031660008181527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205490919060ff166200025557818052816020526040822081835260205260408220600160ff1982541617905533917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d8180a4600190565b509056fe60808060405260043610156200001457600080fd5b600090813560e01c90816301ffc9a71462000ae2575080631cda4a8d1462000a9b578063248a9ca31462000a6d5780632f2ff15d1462000a2857806336568abe14620009d9578063439577a014620009925780635db27897146200094b5780637a89862914620009045780638b7cc1e314620008bd57806391d148541462000871578063a217fddf1462000853578063ab6343c414620001a5578063b18fd8a8146200015e578063d547741f14620001155763ecd0026114620000d657600080fd5b34620001125780600319360112620001125760206040517ffc425f2263d0df187444b70e47283d622c70181c5baebb1306a01edba1ce184c8152f35b80fd5b50346200011257604036600319011262000112576200015a6004356200013a62000b3c565b908084528360205262000154600160408620015462000bfa565b62000ca0565b5080f35b503462000112578060031936011262000112576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620001125760c03660031901126200011257600435906001600160a01b038216820362000112576024356001600160401b0381116200067d57620001f090369060040162000b9f565b6044356001600160401b0381116200084f576200021290369060040162000b9f565b6064356001600160a01b03811690036200084f57608435906001600160a01b03821682036200068c577ffc425f2263d0df187444b70e47283d622c70181c5baebb1306a01edba1ce184c808552846020526040852033865260205260ff6040862054161562000831575060405163de7ea79d60e01b602082015260806024820152620002da81620002be620002ab60a483018862000d16565b8281036023190160448401528562000d16565b6012606483015233608483015203601f19810183528262000b7d565b604051906102d382018281106001600160401b038211176200081d5782916200032e916102d362000d8085397f00000000000000000000000000000000000000000000000000000000000000009062000d58565b039085f0948515620006cf57604051633d1c7bfd60e01b81526001600160a01b03828116600483015260a4356024830152606480358216604484015285821690830152909290602090849060849082908a907f0000000000000000000000000000000000000000000000000000000000000000165af192831562000812578693620007c0575b50916200045b939162000405620003f29694604051978896632c021a4360e21b602089015233602489015260e0604489015261010488019062000d16565b8681036023190160648801529062000d16565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116608487015293841660a486015290831660c4850152911660e483015203601f19810183528262000b7d565b604051906102d382018281106001600160401b03821117620007ac578291620004af916102d362000d8085397f00000000000000000000000000000000000000000000000000000000000000009062000d58565b039082f080156200079f5760405163a217fddf60e01b8082526001600160a01b0394851694929092169190602081600481885afa908115620006cf57849162000767575b50843b156200068c57604051632f2ff15d60e01b80825260048201929092526064356001600160a01b031660248201528481604481838a5af18015620006f25762000751575b5060405163d539139360e01b8152602081600481895afa908115620006f257859162000719575b50853b15620007155760405182815260048101919091526001600160a01b03841660248201528481604481838a5af18015620006f257908591620006fd575b5050823b156200068c576040516340f797bb60e01b815260048101869052848160248183885af18015620006f257908591620006da575b5050604051918252602082600481865afa918215620006cf57849262000690575b50823b156200068c5760405190815260048101919091526064356001600160a01b03166024820152828160448183865af18015620006815762000665575b50602092817fc914ea709f3eb6988a1adfd36b9ecfb22a736fce308704c1ed8c596a0301ef8e6040519480a38152f35b62000671839162000b53565b6200067d573862000635565b5080fd5b6040513d85823e3d90fd5b8380fd5b9091506020813d602011620006c6575b81620006af6020938362000b7d565b81010312620006c157519038620005f7565b600080fd5b3d9150620006a0565b6040513d86823e3d90fd5b620006e59062000b53565b6200068c578338620005d6565b6040513d87823e3d90fd5b620007089062000b53565b6200068c5783386200059f565b8480fd5b90506020813d60201162000748575b81620007376020938362000b7d565b81010312620006c157513862000560565b3d915062000728565b6200075f9094919462000b53565b923862000539565b90506020813d60201162000796575b81620007856020938362000b7d565b810103126200068c575138620004f3565b3d915062000776565b50604051903d90823e3d90fd5b634e487b7160e01b84526041600452602484fd5b9092506020813d60201162000809575b81620007df6020938362000b7d565b810103126200080557516001600160a01b03811681036200080557916200045b620003b4565b8580fd5b3d9150620007d0565b6040513d88823e3d90fd5b634e487b7160e01b87526041600452602487fd5b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b8280fd5b50346200011257806003193601126200011257602090604051908152f35b503462000112576040366003190112620001125760ff60406020926200089662000b3c565b60043582528185528282206001600160a01b03909116825284522054604051911615158152f35b503462000112578060031936011262000112576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503462000112578060031936011262000112576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503462000112578060031936011262000112576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503462000112578060031936011262000112576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034620001125760403660031901126200011257620009f762000b3c565b336001600160a01b0382160362000a16576200015a9060043562000ca0565b60405163334bd91960e11b8152600490fd5b50346200011257604036600319011262000112576200015a60043562000a4d62000b3c565b908084528360205262000a67600160408620015462000bfa565b62000c21565b5034620001125760203660031901126200011257600160406020926004358152808452200154604051908152f35b503462000112578060031936011262000112576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b9050346200067d5760203660031901126200067d5760043563ffffffff60e01b81168091036200084f5760209250637965db0b60e01b811490811562000b2a575b5015158152f35b6301ffc9a760e01b1490503862000b23565b602435906001600160a01b0382168203620006c157565b6001600160401b03811162000b6757604052565b634e487b7160e01b600052604160045260246000fd5b90601f801991011681019081106001600160401b0382111762000b6757604052565b81601f82011215620006c1578035906001600160401b03821162000b67576040519262000bd7601f8401601f19166020018562000b7d565b82845260208383010111620006c157816000926020809301838601378301015290565b80600052600060205260406000203360005260205260ff6040600020541615620008315750565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054161560001462000c9b57808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b9060009180835282602052604083209160018060a01b03169182845260205260ff60408420541660001462000c9b5780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b919082519283825260005b84811062000d43575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000d21565b6001600160a01b03909116815260406020820181905262000d7c9291019062000d16565b9056fe60806040526102d38038038061001481610194565b92833981019060408183031261018f5780516001600160a01b03811680820361018f5760208381015190936001600160401b03821161018f570184601f8201121561018f5780519061006d610068836101cf565b610194565b9582875285838301011161018f57849060005b83811061017b57505060009186010152813b15610163577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b03191682179055604051907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600080a28351156101455750600080848461012c96519101845af4903d1561013c573d61011c610068826101cf565b908152600081943d92013e6101ea565b505b6040516085908161024e8239f35b606092506101ea565b9250505034610154575061012e565b63b398979f60e01b8152600490fd5b60249060405190634c9c8ce360e01b82526004820152fd5b818101830151888201840152869201610080565b600080fd5b6040519190601f01601f191682016001600160401b038111838210176101b957604052565b634e487b7160e01b600052604160045260246000fd5b6001600160401b0381116101b957601f01601f191660200190565b9061021157508051156101ff57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580610244575b610222575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561021a56fe60806040527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e15604b573d90f35b3d90fdfea264697066735822122064e05d6b50f7e38be358c5202a9c5465626b986a9ff403ce9b298bc4f4509dbb64736f6c63430008140033a26469706673582212204246a924d2afef461866302df991654983b700d98497f920d6b8d7669d14a81964736f6c6343000814003360a08060405234620000d157306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c16620000c257506001600160401b036002600160401b0319828216016200007c575b60405161495f9081620000d7823960805181818161268301526127260152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880806200005c565b63f92ee8a960e01b8152600490fd5b600080fdfe6080604081815260048036101561001557600080fd5b600092833560e01c90816301ffc9a7146137005750806305db2f41146136c5578063061525371461366c57806306fdde03146135825780630df9ef2c146135225780630efe6a8b14613209578063156c2a6e146131e057806320483dd114613134578063205023d3146130f957806322b6fea914612fd957806323be345c14612fb0578063248a9ca314612f795780632b38e26614612f225780632ccbef8b14612d8d5780632e08ebcd14612d1c5780632e718ab714612ce15780632eebe78e14612cbd5780632f2ff15d14612c945780633278eb0414612c6b57806336568abe14612c24578063383e6d4a14612b6a5780633e5541f114612ab95780633f4ba83a14612a4257806340f797bb146129b65780634788a26b146129565780634b155b971461292d5780634efde8d01461290e5780634f1ef286146126d557806352d1902d1461266e5780635a8f00c81461261e5780635b17d04b146125b95780635c975abb14612588578063608fc37a146121fb578063635b6a3a1461219b57806363f926231461208157806366c4257b146120495780636c24a76f14611f635780636c9fa59e14611f3b57806375b238fc14611f00578063842a05d414611e775780638456cb5914611e0f57806384b594dc14611c405780638795feb814611adf5780638863e5c4146117a55780638dcfbfab146115ba5780639137c1771461157d57806391d148541461152b57806395d89b411461143757806399caf5dd1461120e5780639b18847e1461116d5780639f5bd0f3146110ba578063a217fddf1461109f578063a64b6e5f14610f52578063a819462514610d6a578063a981c7b414610d3d578063ad3cb1cc14610cf8578063b008690c14610a32578063b04b3f57146109f3578063b8b99b7a146109a5578063b8f82b2614610973578063beb96acf14610855578063c5cc6bb314610807578063c79445d0146107cc578063cc2b2523146107ad578063cf177dbc14610655578063d1f810a514610611578063d547741f146105c5578063e63ab1e91461058a578063f5b541a614610561578063f72c0d8b146105345763fade00fe1461033057600080fd5b3461044f57602090816003193601126104415780359061034e614255565b61035661422a565b6009546001600160a01b03908116330361052557821561050f5785541684516370a0823160e01b9081815230848201528581602481865afa801561050557859189916104d4575b501061045357509085916103b384600c54613f89565b600c55803b1561044f578383916103e093838951809681958294632770a7eb60e21b845230908401613eb0565b03925af180156104455761042d575b50507f0158b5c01453469bf562bebba531d45a08d8162682a2d56d652909caf8e9cbaa9251908152a1600160008051602061490a8339815191525580f35b610436906137c4565b6104415783386103ef565b8380fd5b85513d84823e3d90fd5b8280fd5b9380879395602488949851809981938252308a8301525afa9283156104c95792610493575b505163cf47918160e01b815292830152602482015260449150fd5b90915084813d83116104c2575b6104aa8183613823565b810103126104bd57604493519085610478565b600080fd5b503d6104a0565b8251903d90823e3d90fd5b809250878092503d83116104fe575b6104ed8183613823565b810103126104bd578490513861039d565b503d6104e3565b87513d8a823e3d90fd5b50602491845191633728b83d60e01b8352820152fd5b5083516367a3563f60e01b8152fd5b50503461055d578160031936011261055d576020905160008051602061484a8339815191528152f35b5080fd5b50503461055d578160031936011261055d57602090516000805160206148aa8339815191528152f35b50503461055d578160031936011261055d57602090517f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a8152f35b50903461044f578060031936011261044f5761060d913561060860016105e961376a565b938387526000805160206148ca83398151915260205286200154613a6e565b613cda565b5080f35b50503461055d578060031936011261055d5761065190610643610632613754565b61063a61422a565b60243590614363565b919492935194859485613929565b0390f35b503461044f578160031936011261044f5761066e613754565b6024359061067a614255565b61068261422a565b6009546001600160a01b0391908216330361079d5716928051926370a0823160e01b845230818501526020938481602481895afa908115610793578791610766575b50858752600d85526106ed6106dc8489205486614285565b878952600e87528489205490614285565b9081811061074d57505050907f3b3cc6e6e4f41ea6d60a8a018c24e335d06b91f3eaa9dfc6e7f51cd00155c4e49291848652600d8352808620610731838254614285565b905551908152a2600160008051602061490a8339815191525580f35b604493519263adb9e04360e01b84528301526024820152fd5b90508481813d831161078c575b61077d8183613823565b810103126104bd5751386106c4565b503d610773565b83513d89823e3d90fd5b84516367a3563f60e01b81528490fd5b50503461055d578160031936011261055d576020906005549051908152f35b50503461055d578160031936011261055d57602090517fe28434228950b641dbbc0178de89daa359a87c6ee0d8399aeace52a98fe902b98152f35b50503461055d578060031936011261055d57602091610824613754565b8261082d61376a565b6001600160a01b039283168452600f8652922091166000908152908352819020549051908152f35b503461044f5761086436613884565b91939061086f614255565b61087761422a565b6009546001600160a01b039590861633036109635785821695861561094c57841561093557811694858852600d6020528388205485811061091657505050916020916108fc827f466568d995c4d20c1611745da0ee5c713e401f183df24615614ad6ce33e67fce95878a52600d8652838a206108f4838254613f89565b9055876147e4565b51908152a3600160008051602061490a8339815191525580f35b8561093191865194859463162f9a1960e31b8652850161480b565b0390fd5b8351633728b83d60e01b8152808701869052602490fd5b8351630bc2c5df60e11b8152808701889052602490fd5b82516367a3563f60e01b81528590fd5b50503461055d578060031936011261055d5761065190610643610994613754565b61099c61422a565b60243590613fac565b50503461055d578060031936011261055d576020916109c2613754565b826109cb61376a565b6001600160a01b03928316845260108652922091166000908152908352819020549051908152f35b50503461055d578060031936011261055d57602090610a29610a13613754565b610a1b61376a565b90610a2461422a565b614746565b90519015158152f35b503461044f5760e036600319011261044f57610a4c613754565b906001600160401b03602435818111610cf457610a6c903690840161390b565b50604435818111610cf457610a84903690840161390b565b50610a8d613780565b926001600160a01b0392608435908482168083036104bd5760a435868116928382036104bd5760c435938885168095036104bd578a987ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009a60ff8c54809c1c1615998b169a8b1580610ced575b6001809d149081610ce3575b159081610cda575b50610cca5767ffffffffffffffff1981168c178d558a610cab575b50610b32613df0565b610b3a613df0565b8a60008051602061490a83398151915255610b53613df0565b610b5b613df0565b610b63613df0565b6000805160206148ea833981519152805460ff19169055610b82613df0565b81891615610c9b5716938415610c8b5715610c7c5715610c6e578315610c60575086546001600160a01b0319908116909217875560068054610100600160a81b03191660089290921b610100600160a81b0316919091179055600780549091169091179055610c199190610c0990610bf983613aa1565b50610c0333613aa1565b50613b2f565b50610c1333613b2f565b50613bcd565b50610c22578380f35b815460ff60401b191690915590519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880808380f35b8951630538eaff60e51b8152fd5b8951637bfd2e8360e01b8152fd5b50895163eb32d3bf60e01b8152fd5b8b51630201aae160e21b81528390fd5b8c5163016ed19f60e21b81528490fd5b68ffffffffffffffffff191668010000000000000001178c5538610b29565b8d5163f92ee8a960e01b81528590fd5b90501538610b0e565b303b159150610b06565b508a610afa565b8580fd5b50503461055d578160031936011261055d57805161065191610d19826137ed565b60058252640352e302e360dc1b602083015251918291602083526020830190613844565b50503461055d578160031936011261055d57600654905160089190911c6001600160a01b03168152602090f35b5082903461055d578260031936011261055d57610d85613754565b92602490813590610d94614255565b610d9c61422a565b6009546001600160a01b039081163303610f4257808716948515610f2c578315610f175781875416908351956370a0823160e01b808852308389015260209788818481885afa908115610f0d579088918c91610edc575b5010610e6e5750505050828697610e5492877f3a19b6efdb4fa34812cbff59a94c0d953fa352016b0c438a40a5520d69b26492989952600b8752848a20610e3b848254613f89565b9055610e4983600c54613f89565b600c558954166147e4565b51908152a2600160008051602061490a8339815191525580f35b90898689808a95858451809a81938252308b8301525afa9283156104c95792610eab575b505163cf47918160e01b81529384015282015260449150fd5b90915085813d8311610ed5575b610ec28183613823565b810103126104bd57604494519086610e92565b503d610eb8565b8092508a8092503d8311610f06575b610ef58183613823565b810103126104bd578790518d610df3565b503d610eeb565b87513d8d823e3d90fd5b939450505191633728b83d60e01b8352820152fd5b8251630bc2c5df60e11b81529081018690528490fd5b81516367a3563f60e01b81528590fd5b5082903461055d57610f6336613884565b92610f6f929192614255565b610f7761422a565b6008546001600160a01b0392908316330361109057821691821561107a57831680156110645750831561104d5785516370a0823160e01b81523082820152602081602481865afa908115611043578691611012575b50848110610ff6575050610fe19394506147e4565b600160008051602061490a8339815191525580f35b865163adb9e04360e01b81529182015260248101849052604490fd5b90506020813d821161103b575b8161102c60209383613823565b810103126104bd575187610fcc565b3d915061101f565b87513d88823e3d90fd5b8551633728b83d60e01b8152908101849052602490fd5b8651630bc2c5df60e11b81529182015260249150fd5b5060249186519163961c9a4f60e01b8352820152fd5b50855163b693a60960e01b8152fd5b50503461055d578160031936011261055d5751908152602090f35b50503461055d5760208060031936011261044f576110d6613754565b926110df61422a565b6001600160a01b039384168152600a825282812083518154808252918352838320818501949192859190855b8181106111575750505082611121910383613823565b8451948186019282875251809352850193925b8281106111415785850386f35b8351871685529381019392810192600101611134565b82548a168452928401926001928301920161110b565b50913461120b578160031936011261120b57611187613754565b9161119061376a565b60018060a01b03809416938484526010602052828420911690816000526020528160002054938352600f60205281832090600052602052806000205483810392808512828512811690838613901516176111f857506060945081519384526020840152820152f35b634e487b7160e01b815260118652602490fd5b80fd5b50903461044f578060031936011261044f57611228613754565b9060243590611235614255565b61123d61422a565b6001600160a01b03831692831561142057821561140957331580156113f0575b6113d457508051636eb1769f60e11b8082526020959186818061128330338884016145bf565b0381895afa9081156113ca579085918991611399575b501061130d575050828552600f8452808520336000528452806000206112c0838254614285565b90556112ce823033866142b4565b519081527fdad7dfd8821ceb98a6dd83b8f330e604bd0e832f55ca902bbfadabde5e40132342933392a4600160008051602061490a8339815191525580f35b9480919583949593519384918252818061132a30338c84016145bf565b03915afa95861561138e579561135a575b505051630c95cf2760e11b81529283926109319291903390850161480b565b908093929550813d8311611387575b6113738183613823565b810103126104bd579051926109313861133b565b503d611369565b8351903d90823e3d90fd5b809250888092503d83116113c3575b6113b28183613823565b810103126104bd5784905138611299565b503d6113a8565b84513d8a823e3d90fd5b815163188fe79360e21b815290819061093190338389016145bf565b506113f961422a565b6114033382614746565b1561125d565b8151633728b83d60e01b8152808601849052602490fd5b815163961c9a4f60e01b8152808601859052602490fd5b50913461120b578060031936011261120b575080519060009060035491600183811c90808516948515611521575b602095868410811461150e578388528794939291879082156114ec5750506001146114ac575b5050610651929161149d910385613823565b51928284938452830190613844565b90859250600360005282600020916000925b8284106114d4575050508201018161149d61148b565b8054848a0186015288955087949093019281016114be565b60ff19168682015292151560051b8501909201925083915061149d905061148b565b634e487b7160e01b855260228952602485fd5b91607f1691611465565b503461044f578160031936011261044f578160209361154861376a565b923581526000805160206148ca8339815191528552209060018060a01b0316600052825260ff81600020541690519015158152f35b50503461055d5736600319011261120b576115b7611599613754565b6115a161376a565b906115aa61422a565b6115b26139b6565b6145e8565b80f35b5082903461055d578260031936011261055d576115d5613754565b92602435906115e2614255565b6115ea61422a565b6009546001600160a01b0390811633036117955780861693841561177e5783156117675785548351636eb1769f60e11b80825260209695949392841691878180611637308f838a016145bf565b0381865afa90811561175d579087918b9161172c575b50106116af5750505082610e54918798877f6926ad0f7f6f6814be2630ac9d46a2d1988f933254ad20700c1425aaf64a27a4989952600b8752848a20611694848254614285565b90556116a283600c54614285565b600c5530918a54166142b4565b8993508885888089948351968791825281806116ce308d8d84016145bf565b03915afa9283156104c957926116fa575b5051630c95cf2760e11b81529485946109319450850161480b565b90915083813d8311611725575b6117118183613823565b810103126104bd57610931925190866116df565b503d611707565b809250898092503d8311611756575b6117458183613823565b810103126104bd578690518c61164d565b503d61173b565b86513d8c823e3d90fd5b8251633728b83d60e01b8152908101849052602490fd5b82516313053d9360e21b8152908101859052602490fd5b81516367a3563f60e01b81528490fd5b503461044f57608036600319011261044f576117bf613754565b6024803592604435916117d0613780565b926117d9614255565b6117e161422a565b60015487516373bed91960e11b81526001600160a01b0386811686830152939160209190829082908590829089165afa908115611ad5578b91611ab8575b5015611aa85761182d61422a565b60065460ff811615611a98578815611a825789516313a9822560e31b8152888616878201819052949160081c86169083818681855afa9081156119fa578d91611a55575b5015611a3f578a5163146f4ea360e21b815295949392919082908790818061189c8f8f838f01613eb0565b03915afa928315611a35578c93611a04575b6118e09650826118be858d613f89565b8d8c8b8a6007541692519b8c9485938493633e5541f160e01b85528401613eb0565b03915afa9687156119fa578d976119cb575b508187106119b05750508261192692878b938e9a999897968b525261191b8c8a20918254614285565b9055309033906142b4565b845416803b156119ac57611951938580948a51968795869485936340c10f1960e01b85528401613eb0565b03925af1801561044557611998575b505061198060008051602061488a83398151915293519283923384614292565b0390a1600160008051602061490a8339815191525580f35b6119a1906137c4565b610441578338611960565b8480fd5b6044918891888e519363658ec5dd60e11b8552840152820152fd5b9096508281813d83116119f3575b6119e38183613823565b810103126104bd575195386118f2565b503d6119d9565b8c513d8f823e3d90fd5b92508186813d8311611a2e575b611a1b8183613823565b810103126104bd576118e09551926118ae565b503d611a11565b8b513d8e823e3d90fd5b8a51639d42b4b360e01b81528088018690528490fd5b611a759150843d8611611a7b575b611a6d8183613823565b810190613e63565b38611871565b503d611a63565b8951633728b83d60e01b81528087018a90528390fd5b895163bb60b89360e01b81528690fd5b885163110781d160e31b81528590fd5b611acf9150823d8411611a7b57611a6d8183613823565b3861181f565b8a513d8d823e3d90fd5b50903461044f578060031936011261044f57611af9613754565b92611b0261376a565b93611b0b614255565b611b1361422a565b7f6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c94856000526020956000805160206148ca83398151915287528460002033600052875260ff85600020541615611c2257506001600160a01b03918216918215611c0b578116928315611bf45782815285875284812054958615611be657838252875284812055611ba6908590836147e4565b7fd74e462593ae4127bbc183389b3775357426f157e9625f81271612b86b8000a6858451868152a3600160008051602061490a8339815191525551908152f35b855163211b631760e21b8152fd5b8451630bc2c5df60e11b8152808701859052602490fd5b845163961c9a4f60e01b8152808701849052602490fd5b845163e2517d3f60e01b815233818801526024810191909152604490fd5b50903461044f57611c5036613884565b93909192611c5c61394e565b611c64614255565b611c6c61422a565b6001600160a01b0383811694908515611df8578615611de15781169384158015611dc8575b611dac575081516370a0823160e01b815230848201526020939084816024818a5afa908115611da2578991611d73575b50611cda611ceb91888b52600d8752858b205490613f89565b878a52600e8652848a205490613f89565b878110611d5757505090611d3b867ff7924cd170ae143405ff2557ce6b4615c3a29b30bcd8a70b49d4d0b3475efb0c9493878a5260108552838a20876000528552836000206108f4838254614285565b519485524294a4600160008051602061490a8339815191525580f35b835163adb9e04360e01b81529182015260248101879052604490fd5b90508481813d8311611d9b575b611d8a8183613823565b810103126104bd5751611cda611cc1565b503d611d80565b84513d8b823e3d90fd5b915163188fe79360e21b815292839261093192909184016145bf565b50611dd161422a565b611ddb8282614746565b15611c91565b8251633728b83d60e01b8152808501889052602490fd5b825163961c9a4f60e01b8152808501879052602490fd5b50503461055d578160031936011261055d5760207f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25891611e4d613a12565b611e5561422a565b6000805160206148ea833981519152805460ff1916600117905551338152a180f35b50903461044f57602036600319011261044f57611e92613754565b611e9a61422a565b611ea261394e565b6001600160a01b0316918215611ef25750600880546001600160a01b03191683179055519081527f507e88d0e3541203c8e7dba038a3e807ac9121f0872ed9ed895cd7f3358334eb90602090a180f35b9051630d432b0160e21b8152fd5b50503461055d578160031936011261055d57602090517fa49807205ce4d355092ef5a8a18f56e8913cf4a201fbe287825b095693c217758152f35b50503461055d578160031936011261055d57905490516001600160a01b039091168152602090f35b503461044f576020928360031936011261120b576001600160a01b03611f87613754565b84516370a0823160e01b8152308582015291168582602481845afa91821561203f578392612010575b508252600d85528382205490600e8652848320549386528483205492611fdf84611fda8786614285565b614285565b821115612005575092611ff8611ff892611ffd95613f89565b613f89565b905b51908152f35b935050505090611fff565b9091508581813d8311612038575b6120288183613823565b810103126104bd57519038611fb0565b503d61201e565b85513d85823e3d90fd5b50503461055d57602036600319011261055d5760209181906001600160a01b03612071613754565b168152600b845220549051908152f35b503461044f578160031936011261044f5761209a613754565b600854602435916001600160a01b03918216330361218b576120ba61422a565b16928051926370a0823160e01b845230818501526020938481602481895afa90811561079357879161215e575b50858752600e85526121106120ff8489205486614285565b878952600d87528489205490614285565b9081811061074d57505050907f36bea377ee4a4c4516cc608cc232173faafbeb7ee10ad11c6420f2863a97644b9291848652600e8352808620612154838254614285565b905551908152a280f35b90508481813d8311612184575b6121758183613823565b810103126104bd5751386120e7565b503d61216b565b845163b693a60960e01b81528490fd5b503461044f57602036600319011261044f576121b5613754565b6121bd61422a565b6121c561394e565b6001600160a01b03169182156121ee575050600780546001600160a01b03191691909117905580f35b51630538eaff60e51b8152fd5b5082906020928360031936011261044f57813591612217614255565b61221f61422a565b60015482516373bed91960e11b815233838201526001600160a01b03949187908290602490829089165afa90811561257e578691612561575b50156125525734156125435760065460ff81161561253357849060081c1683519463e861e90760e01b865287868581855afa9586156125295787966124ed575b5084516313a9822560e31b815286821685820181905293908981602481875afa9081156124735789916124d0575b50156124b857888651809463146f4ea360e21b825281806122ea348d8c8401613eb0565b03915afa9081156124ae57889161247d575b61232c93508961230c8334613f89565b84600754168a8a51809881948293633e5541f160e01b84528d8401613eb0565b03915afa938415612473578994612444575b508084106124275750848899858a98999a525261235f878720918254614285565b9055845416803b156119ac5785516340c10f1960e01b8152918591839182908490829061238f90338b8401613eb0565b03925af1801561241d57908491612409575b5050803b1561044f578290845192838092630d0e30db60e41b825234905af180156123ff576123eb575b50815160008051602061488a833981519152908061198034873384614292565b6123f4906137c4565b61044f5782846123cb565b83513d84823e3d90fd5b612412906137c4565b61044f5782876123a1565b85513d86823e3d90fd5b93505050604493519263658ec5dd60e11b84528301526024820152fd5b9093508981813d831161246c575b61245c8183613823565b810103126104bd5751928a61233e565b503d612452565b87513d8b823e3d90fd5b90508883813d83116124a7575b6124948183613823565b810103126104bd5761232c9251906122fc565b503d61248a565b86513d8a823e3d90fd5b50509251639d42b4b360e01b81529182015260249150fd5b6124e791508a3d8c11611a7b57611a6d8183613823565b8a6122c6565b9095508781813d8311612522575b6125058183613823565b8101031261251e5751858116810361251e579488612298565b8680fd5b503d6124fb565b85513d89823e3d90fd5b835163bb60b89360e01b81528390fd5b509051633b903ec760e01b8152fd5b50905163110781d160e31b8152fd5b6125789150873d8911611a7b57611a6d8183613823565b87612258565b84513d88823e3d90fd5b50503461055d578160031936011261055d5760209060ff6000805160206148ea833981519152541690519015158152f35b503461044f57602036600319011261044f57803590811515809203610441576125e061422a565b6125e861394e565b600654928260ff851615151461260957505060ff169060ff19161760065580f35b91602492519163ca255b0b60e01b8352820152fd5b503461044f57602036600319011261044f576001600160a01b03612640613754565b16801561265a579282916020948252845220549051908152f35b602492519163961c9a4f60e01b8352820152fd5b50913461120b578060031936011261120b57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630036126c8576020905160008051602061486a8339815191528152f35b5163703e46dd60e11b8152fd5b50908060031936011261044f576126ea613754565b9060249384356001600160401b03811161055d573660238201121561055d5761271b903690878188013591016138d4565b6001600160a01b03937f000000000000000000000000000000000000000000000000000000000000000085163081149081156128f2575b506128e25760008051602061484a83398151915294856000526020956000805160206148ca83398151915287528560002033600052875260ff866000205416156128c5575081169484516352d1902d60e01b8152818189818a5afa859181612896575b506127d05750505050505191634c9c8ce360e01b8352820152fd5b868996899260008051602061486a833981519152908181036128815750853b1561286c5780546001600160a01b0319168317905551869392917fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8580a2855115612850575050835161060d94839201845af461284a613d5d565b91613d8d565b9350935050503461286057505080f35b63b398979f60e01b8152fd5b5051634c9c8ce360e01b815291820152859150fd5b848a91845191632a87526960e21b8352820152fd5b9091508281813d83116128be575b6128ae8183613823565b81010312610cf4575190386127b5565b503d6128a4565b855163e2517d3f60e01b815233818a0152808a0191909152604490fd5b835163703e46dd60e11b81528690fd5b90508560008051602061486a8339815191525416141538612752565b50503461055d578160031936011261055d57602090600c549051908152f35b50503461055d578160031936011261055d5760015490516001600160a01b039091168152602090f35b503461044f57602036600319011261044f57612970613754565b61297861422a565b61298061394e565b6001600160a01b03169182156129a9575050600180546001600160a01b03191691909117905580f35b51630201aae160e21b8152fd5b50903461044f57602036600319011261044f5781356001600160a01b0381169290839003610441576129e661394e565b6011549160ff8316612a3557505082546001600160a01b0319168217835560ff19166001176011557fdb40a5259506fa101ac4485d4ccbd019c99ada5fb7212fe1254374f7a10e02dc8280a280f35b51630f4bac1b60e21b8152fd5b503461044f578260031936011261044f57612a5b613a12565b6000805160206148ea8339815191529081549060ff821615612aab575060ff19169055513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa90602090a180f35b8351638dfc202b60e01b8152fd5b5091903461055d578060031936011261055d576020612b1093612ada613754565b612ae261422a565b6007548451633e5541f160e01b81529687936001600160a01b03909216928492839291602435918401613eb0565b03915afa918215612b5f5791612b2b575b6020925051908152f35b90506020823d8211612b57575b81612b4560209383613823565b810103126104bd576020915190612b21565b3d9150612b38565b9051903d90823e3d90fd5b50903461044f578060031936011261044f57612b84613754565b6008546024359291906001600160a01b039081163303612c1457612ba661422a565b811693848652600e60205282862054848110612bf9575050507fbcb639e764c5cd7d736acef943167eb10be8fc46f9e33b29c69c89473fb79fd791602091848652600e8352808620612154838254613f89565b84610931918551948594631664bf6360e01b8652850161480b565b825163b693a60960e01b81528590fd5b5091903461055d578060031936011261055d57612c3f61376a565b90336001600160a01b03831603612c5c575061060d919235613cda565b5163334bd91960e11b81528390fd5b50503461055d578160031936011261055d5760095490516001600160a01b039091168152602090f35b50903461044f578060031936011261044f5761060d9135612cb860016105e961376a565b613c65565b50503461055d578160031936011261055d5760209060ff6006541690519015158152f35b50503461055d578160031936011261055d57602090517f85e8f2d6819d6b24108062d87ea08f54651bcb8960d98062d3faf96e7873b8b98152f35b503461044f57602036600319011261044f57612d36613754565b91612d3f61422a565b612d4761394e565b6001600160a01b03831615612d8057505060068054610100600160a81b03191660089290921b610100600160a81b031691909117905580f35b51637bfd2e8360e01b8152fd5b50903461044f57612d9d36613884565b93909192612da9614255565b612db161422a565b6008546001600160a01b03949085163303612f14578416938415612efd5783168015612ee857508415612ed2578051636eb1769f60e11b808252602091828180612dfe308a8a84016145bf565b03818a5afa908115611da2579088918a91612ea1575b5010612e275787610fe18830888a6142b4565b81908398949795985196879182528180612e44308d8d84016145bf565b03915afa9283156104c95792612e6f575051630c95cf2760e11b81529485946109319450850161480b565b90915083813d8311612e9a575b612e868183613823565b810103126104bd57610931925190386116df565b503d612e7c565b809250848092503d8311612ecb575b612eba8183613823565b810103126104bd5787905138612e14565b503d612eb0565b51633728b83d60e01b8152908101849052602490fd5b90516313053d9360e21b815291820152602490fd5b815163961c9a4f60e01b8152808401869052602490fd5b505163b693a60960e01b8152fd5b5091903461055d578060031936011261055d576020612b1093612f43613754565b612f4b61422a565b600754845163159c713360e11b81529687936001600160a01b03909216928492839291602435918401613eb0565b503461044f57602036600319011261044f57816020936001923581526000805160206148ca83398151915285522001549051908152f35b50503461055d578160031936011261055d5760085490516001600160a01b039091168152602090f35b50903461044f578060031936011261044f57612ff3613754565b91612ffc61376a565b9061300561422a565b61300d6139b6565b6001600160a01b0384811693908415612efd5783169485156130e05761303161422a565b61303b8482614746565b6130c75750838652600a6020528520805491600160401b8310156130b457508161306e91600161308d9594018155613796565b90919082549060031b9160018060a01b03809116831b921b1916179055565b7f2c79891dd909910a9ed2116eb868a4783a4abeb6da7c8dc217cd5b198b329b5d8380a380f35b634e487b7160e01b875260419052602486fd5b610931915193849363aebbe4f960e01b855284016145bf565b610931915193849363188fe79360e21b855284016145bf565b50503461055d578160031936011261055d57602090517f0792b37891b8244bb8149106fc05e84f10f266ef581c099bf3d880350e979b2f8152f35b50823461120b576020908160031936011261120b576001600160a01b03613159613754565b1690818152600d835282858220549460248751809581936370a0823160e01b835230908301525afa9081156131d55784916131a4575b6131999250613f89565b908351928352820152f35b50508181813d83116131ce575b6131bb8183613823565b810103126104bd5782613199915161318f565b503d6131b1565b8551903d90823e3d90fd5b50503461055d578160031936011261055d5760075490516001600160a01b039091168152602090f35b50913461120b57606036600319011261120b57613224613754565b92602493843591604435613236614255565b61323e61422a565b60015486516373bed91960e11b815233848201526020986001600160a01b0393929091908a9082908490829088165afa9081156135185788916134fb575b50156134eb5761328a61422a565b60065460ff8116156134db5786156134c657839060081c1698899289516313a9822560e31b815282818581898c1698898c8301525afa9081156134bc578a9161349f575b501561348957818a51809c63146f4ea360e21b825281806132f28d8d8d8401613eb0565b03915afa92831561347f578993613449575b88999a9b50826133188561333b9a9b613f89565b6007548e51633e5541f160e01b81529a8b92918a1691839182918f838f01613eb0565b03915afa97881561343f578b9861340c575b508188106133f15750509187918561337194838c525261191b8b8b20918254614285565b85541690813b15610cf45791859161339f938389518096819582946340c10f1960e01b845233908401613eb0565b03925af1801561241d576133ce575b5061198060008051602061488a8339815191529394519283923384614292565b60008051602061488a833981519152936133ea611980926137c4565b93506133ae565b6044918791898e519363658ec5dd60e11b8552840152820152fd5b9097508281813d8311613438575b6134248183613823565b810103126134345751963861334d565b8a80fd5b503d61341a565b8c513d8d823e3d90fd5b9250818b813d8311613478575b6134608183613823565b8101031261347457995198998a9992613304565b8880fd5b503d613456565b8a513d8b823e3d90fd5b8951639d42b4b360e01b81528087018590528390fd5b6134b69150833d8511611a7b57611a6d8183613823565b386132ce565b8b513d8c823e3d90fd5b508751633728b83d60e01b8152808501879052fd5b885163bb60b89360e01b81528590fd5b875163110781d160e31b81528490fd5b61351291508a3d8c11611a7b57611a6d8183613823565b3861327c565b89513d8a823e3d90fd5b503461044f57602036600319011261044f5761353c613754565b61354461422a565b61354c61394e565b6001600160a01b0316918215613575575050600980546001600160a01b03191691909117905580f35b516348db2b6f60e11b8152fd5b50913461120b578060031936011261120b578151918160025492600184811c91818616958615613662575b602096878510811461364f578899509688969785829a5291826000146136285750506001146135ea575b505050610651929161149d910385613823565b9190869350600283528383205b828410613610575050508201018161149d6106516135d7565b8054848a0186015288955087949093019281016135f7565b60ff19168782015293151560051b8601909301935084925061149d915061065190506135d7565b634e487b7160e01b835260228a52602483fd5b92607f16926135ad565b50503461055d578060031936011261055d57613686613754565b6001600160a01b039081168352600a6020528183208054602435949085101561120b57506020936136b691613796565b92905490519260031b1c168152f35b50503461055d578160031936011261055d57602090517f6c0757dc3e6b28b2580c03fd9e96c274acf4f99d91fbec9b418fa1d70604ff1c8152f35b9250503461044f57602036600319011261044f573563ffffffff60e01b811680910361044f5760209250637965db0b60e01b8114908115613743575b5015158152f35b6301ffc9a760e01b1490503861373c565b600435906001600160a01b03821682036104bd57565b602435906001600160a01b03821682036104bd57565b606435906001600160a01b03821682036104bd57565b80548210156137ae5760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b6001600160401b0381116137d757604052565b634e487b7160e01b600052604160045260246000fd5b604081019081106001600160401b038211176137d757604052565b602081019081106001600160401b038211176137d757604052565b90601f801991011681019081106001600160401b038211176137d757604052565b919082519283825260005b848110613870575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520161384f565b60609060031901126104bd576001600160a01b039060043582811681036104bd579160243590811681036104bd579060443590565b6001600160401b0381116137d757601f01601f191660200190565b9291926138e0826138b9565b916138ee6040519384613823565b8294818452818301116104bd578281602093846000960137010152565b9080601f830112156104bd57816020613926933591016138d4565b90565b9092608092613926959483526020830152151560408201528160608201520190613844565b3360009081527f448256db8f8fb95ee3eaaf89c1051414494e85cebb6057fcf996cc3d0ccfb45660205260409020546000805160206148aa8339815191529060ff16156139985750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b3360009081527f4e5d4d14b1d64b65403760d275670011644f8f7998f1ef964b0082bdadeaa41460205260409020547f0792b37891b8244bb8149106fc05e84f10f266ef581c099bf3d880350e979b2f9060ff16156139985750565b3360009081527f75442b0a96088b5456bc4ed01394c96a4feec0f883c9494257d76b96ab1c9b6b60205260409020547f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a9060ff16156139985750565b806000526000805160206148ca83398151915260205260406000203360005260205260ff60406000205416156139985750565b6001600160a01b031660008181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260408120549091906000805160206148ca8339815191529060ff16613b2a578280526020526040822081835260205260408220600160ff19825416179055339160008051602061482a8339815191528180a4600190565b505090565b6001600160a01b031660008181527f448256db8f8fb95ee3eaaf89c1051414494e85cebb6057fcf996cc3d0ccfb45660205260408120549091906000805160206148aa833981519152906000805160206148ca8339815191529060ff16613bc7578184526020526040832082845260205260408320600160ff1982541617905560008051602061482a833981519152339380a4600190565b50505090565b6001600160a01b031660008181527fab71e3f32666744d246edff3f96e4bdafee2e9867098cdd118a979a7464786a8602052604081205490919060008051602061484a833981519152906000805160206148ca8339815191529060ff16613bc7578184526020526040832082845260205260408320600160ff1982541617905560008051602061482a833981519152339380a4600190565b906000918083526000805160206148ca83398151915280602052604084209260018060a01b03169283855260205260ff60408520541615600014613bc7578184526020526040832082845260205260408320600160ff1982541617905560008051602061482a833981519152339380a4600190565b906000918083526000805160206148ca83398151915280602052604084209260018060a01b03169283855260205260ff604085205416600014613bc757818452602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b3d15613d88573d90613d6e826138b9565b91613d7c6040519384613823565b82523d6000602084013e565b606090565b90613db45750805115613da257805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580613de7575b613dc5575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15613dbd565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c1615613e1f57565b604051631afcd79f60e31b8152600490fd5b60405190613e3e826137ed565b601682527511195c1bdcda5d081a5cc81b9bdd08195b98589b195960521b6020830152565b908160209103126104bd575180151581036104bd5790565b60405190613e88826137ed565b6019825278151bdad95b8819195c1bdcda5d081b9bdd08195b98589b1959603a1b6020830152565b6001600160a01b039091168152602081019190915260400190565b60009060033d11613ed857565b905060046000803e60005160e01c90565b600060443d1061392657604051600319913d83016004833e81516001600160401b03918282113d602484011117613f4657818401948551938411613f4e573d85010160208487010111613f46575061392692910160200190613823565b949350505050565b50949350505050565b60405190613f64826137ed565b60168252754572726f722063616c63756c6174696e67206665657360501b6020830152565b91908203918211613f9657565b634e487b7160e01b600052601160045260246000fd5b6006549160ff83161561421657604080516313a9822560e31b81526001600160a01b03848116600483015260209592949260081c8116918681602481865afa90811561420b576000916141ee575b50156141d75783156141a357858551809363146f4ea360e21b82528180614025898960048401613eb0565b03915afa60009281614174575b5061407d57505050505050614045613ecb565b6308c379a014614061575b600090600090600090613926613f57565b614069613ee9565b806140745750614050565b60009182918291565b6140ae9261408d87938096613f89565b9160075416908651809581948293633e5541f160e01b845260048401613eb0565b03915afa60009181614145575b5061412d5750506140ca613ecb565b6308c379a01461410f575b600092600092774572726f722063616c63756c6174696e672073686172657360401b6000935191614105836137ed565b6018835282015290565b614117613ee9565b8061412257506140d5565b600093849350839250565b909250926001915161413e81613808565b6000815290565b90918582813d831161416d575b61415c8183613823565b8101031261120b57505190386140bb565b503d614152565b90928782813d831161419c575b61418b8183613823565b8101031261120b5750519138614032565b503d614181565b505050506000926000926d125b9d985b1a5908185b5bdd5b9d60921b60009351916141cd836137ed565b600e835282015290565b505050505050600090600090600090613926613e7b565b6142059150873d8911611a7b57611a6d8183613823565b38613ffa565b86513d6000823e3d90fd5b505050600090600090600090613926613e31565b60ff6000805160206148ea833981519152541661424357565b60405163d93c066560e01b8152600490fd5b60008051602061490a83398151915260028154146142735760029055565b604051633ee5aeb560e01b8152600490fd5b91908201809211613f9657565b6001600160a01b03918216815291166020820152604081019190915260600190565b906142ea906142dc6142ef956040519586936323b872dd60e01b602086015260248501614292565b03601f198101845283613823565b6142f1565b565b60008061431a9260018060a01b03169360208151910182865af1614313613d5d565b9083613d8d565b8051908115159182614348575b50506143305750565b60249060405190635274afe760e01b82526004820152fd5b61435b9250602080918301019101613e63565b153880614327565b906006549160ff83161561421657604080516313a9822560e31b81526001600160a01b038381166004830152602095929460089390931c81169392918681602481885afa90811561420b576000916145a2575b50156141d757858215614566576143e992600092600754168751808096819463159c713360e11b83528960048401613eb0565b03915afa90918282614534575b505061446457505050614407613ecb565b6308c379a014614451575b6000926000927f4572726f722063616c63756c6174696e6720746f6b656e20616d6f756e7400006000935191614447836137ed565b601e835282015290565b614459613ee9565b806141225750614412565b9380916144889386918651809681948293637b897f3960e01b845260048401613eb0565b03915afa918291600093614503575b50506144dd5750506144a7613ecb565b6308c379a0146144c1575b90600090600090613926613f57565b6144c9613ee9565b806144d457506144b2565b90916000918291565b806144ea91949293614285565b90600192516144f881613808565b600081529193929190565b8181949293943d831161452d575b61451b8183613823565b8101031261120b575051903880614497565b503d614511565b909192508682813d831161455f575b61454d8183613823565b8101031261120b5750519038806143f6565b503d614543565b505050505060009260009274125b9d985b1a59081cda185c995cc8185b5bdd5b9d605a1b6000935191614598836137ed565b6015835282015290565b6145b99150873d8911611a7b57611a6d8183613823565b386143b6565b6001600160a01b0391821681529116602082015260400190565b6000198114613f965760010190565b90916001600160a01b03808316801561472e5781851691821561471257600090828252600a60205260408220825b8154808210156146f657838761462c8486613796565b929054600393841b1c161461464b575050614646906145d9565b614616565b9495969798509850600092919219988981019081116146e2579061306e836146766146849487613796565b905490881b1c169185613796565b81549788156146ce577f1c0cdcc74010449d4477f9576aaf31cee9e18d2611031462fcd5bf5329dec88e9596979801926146be8484613796565b81939154921b1b191690555580a3565b634e487b7160e01b85526031600452602485fd5b634e487b7160e01b86526011600452602486fd5b60405163188fe79360e21b8152806109318b8d600484016145bf565b60405163188fe79360e21b8152806109318789600484016145bf565b6024906040519063961c9a4f60e01b82526004820152fd5b9060018060a01b03806000931683526020600a815260408420604051808284829454938481520190885284882092885b868282106147cc5750505061478d92500382613823565b845b81518110156147c45783838260051b8401015116848616146147b9576147b4906145d9565b61478f565b505050505050600190565b505050505090565b85548916845260019586019587955093019201614776565b6142ea6142ef93926142dc60405194859263a9059cbb60e01b602085015260248401613eb0565b604091949392606082019560018060a01b031682526020820152015256fe2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e3360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8752a472e571a816aea92eec8dae9baf628e840f4929fbcc2d155e6233ff68a797667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92902dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f033009b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220be2fce2831af35267343bb59637196e7fb5fc02321f7ed193c9efa949d4b67c464736f6c6343000814003360a080604052346100cc57306080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c166100bd57506001600160401b036002600160401b031982821601610078575b60405161196290816100d28239608051818181610a080152610aaf0152f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1388080610059565b63f92ee8a960e01b8152600490fd5b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816301ffc9a7146111e95750806306fdde031461114b578063095ea7b3146110ca57806318160ddd1461109f57806323b872dd14610fba578063248a9ca314610f7e5780632f2ff15d14610f32578063313ce56714610f1157806336568abe14610eca57806340c10f1914610e0c5780634f1ef28614610a6057806352d1902d146109f557806370a08231146109ae57806391d148541461095457806395d89b411461086f5780639dc29fac1461078a578063a217fddf1461076e578063a9059cbb1461073d578063ad3cb1cc146106e9578063d5391393146106ae578063d547741f14610660578063dd62ed3e14610617578063de7ea79d146101555763f72c0d8b1461012757600080fd5b3461015057600036600319011261015057602060405160008051602061188d8339815191528152f35b600080fd5b34610150576080366003190112610150576004356001600160401b03811161015057610185903690600401611356565b6024356001600160401b038111610150576101a4903690600401611356565b60443560ff81168103610150576064356001600160a01b03811690036101505760008051602061190d83398151915254906001600160401b03821680159081610607575b60011490816105fd575b1590816105f4575b506105e25760016001600160401b031983161760008051602061190d8339815191525560ff8260401c16156105b5575b6102326117c5565b61023a6117c5565b83516001600160401b0381116104a25761026260008051602061182d8339815191525461167e565b601f8111610547575b506020601f82116001146104c35781929394956000926104b8575b50508160011b916000199060031b1c19161760008051602061182d833981519152555b82516001600160401b0381116104a2577f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace04906102e5825461167e565b601f811161045a575b506020601f82116001146103f157819060ff95966000926103e6575b50508160011b916000199060031b1c19161790555b6103276117c5565b61032f6117c5565b1660ff19600054161760005561034660643561145a565b506103503361145a565b5061035c6064356114e8565b506040519060ff906064356001600160a01b03167fa479080e7e939829babda407ef044045c57ee261eb6bebe01946a649d8a8735e600080a260401c16156103a057005b60008051602061190d833981519152805460ff60401b19169055600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a1005b01519050868061030a565b601f1982169583600052816000209660005b81811061044257509160ff969791846001959410610429575b505050811b01905561031f565b015160001960f88460031b161c1916905586808061041c565b83830151895560019098019760209384019301610403565b826000526020600020601f830160051c81019160208410610498575b601f0160051c01905b81811061048c57506102ee565b6000815560010161047f565b9091508190610476565b634e487b7160e01b600052604160045260246000fd5b015190508580610286565b60008051602061182d833981519152600052806000209060005b601f198416811061052f575060019394959683601f19811610610516575b505050811b0160008051602061182d833981519152556102a9565b015160001960f88460031b161c191690558580806104fb565b9091602060018192858b0151815501930191016104dd565b60008051602061182d8339815191526000527f2ae08a8e29253f69ac5d979a101956ab8f8d9d7ded63fa7a83b16fc47648eab0601f830160051c8101602084106105ae575b601f830160051c820181106105a257505061026b565b6000815560010161058c565b508061058c565b68ffffffffffffffffff198216680100000000000000011760008051602061190d8339815191525561022a565b60405163f92ee8a960e01b8152600490fd5b905015856101fa565b303b1591506101f2565b604084901c60ff161591506101e8565b34610150576040366003190112610150576106306112b7565b61064161063b6112cd565b91611374565b9060018060a01b03166000526020526020604060002054604051908152f35b34610150576040366003190112610150576106ac60043561067f6112cd565b90806000526000805160206118ed8339815191526020526106a7600160406000200154611427565b6115fb565b005b346101505760003660031901126101505760206040517f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a68152f35b346101505760003660031901126101505760405160408101908082106001600160401b038311176104a2576107399160405260058152640352e302e360dc1b60208201526040519182918261126e565b0390f35b34610150576040366003190112610150576107636107596112b7565b60243590336116b8565b602060405160018152f35b3461015057600036600319011261015057602060405160008152f35b34610150576040366003190112610150576107a36112b7565b6024356107ae6113ad565b6001600160a01b038216908115610856578160005260008051602061184d83398151915280602052604060002054938285106108255750816000805160206118cd83398151915292600095602093868852845203604086205560008051602061186d833981519152818154039055604051908152a3005b60405163391434e360e21b81526001600160a01b039190911660048201526024810185905260448101839052606490fd5b604051634b637e8f60e11b815260006004820152602490fd5b34610150576000366003190112610150576040516000907f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace048054906108b38261167e565b90818452600192838116908160001461092c57506001146108eb575b610739846108df818803826112e3565b6040519182918261126e565b90935060005260209283600020916000925b8284106109195750505081610739936108df92820101936108cf565b80548585018701529285019281016108fd565b61073996506108df9450602092508593915060ff191682840152151560051b820101936108cf565b346101505760403660031901126101505761096d6112cd565b6004356000526000805160206118ed83398151915260205260406000209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b34610150576020366003190112610150576001600160a01b036109cf6112b7565b1660005260008051602061184d8339815191526020526020604060002054604051908152f35b34610150576000366003190112610150577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163003610a4e5760206040516000805160206118ad8339815191528152f35b60405163703e46dd60e11b8152600490fd5b604036600319011261015057610a746112b7565b60249081356001600160401b038111610150573660238201121561015057610aa5903690848160040135910161131f565b6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116308114908115610df0575b50610a4e573360009081527fab71e3f32666744d246edff3f96e4bdafee2e9867098cdd118a979a7464786a8602090815260409091205490919060008051602061188d8339815191529060ff1615610dd357506040516301ffc9a760e01b8082526336372b0760e01b600483015291851694919083818881895afa908115610d7c57600091610db6575b5015610d8857604051908152637965db0b60e01b600482015282818781885afa908115610d7c57600091610d4f575b5015610d1957604051847f7961c8f9895a011e1bdea60033539eb2f075513509777f3f37b4c6ec745f45fd600080a26352d1902d60e01b81528281600481885afa60009181610cea575b50610bfb57604051634c9c8ce360e01b8152600481018690528690fd5b8490866000805160206118ad83398151915291828103610cd55750833b15610cbf575080546001600160a01b03191682179055604051907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600080a2835115610ca5575060008084846106ac96519101845af4903d15610c9c573d610c7f81611304565b90610c8d60405192836112e3565b8152600081943d92013e611762565b60609250611762565b9250505034610cb057005b63b398979f60e01b8152600490fd5b604051634c9c8ce360e01b815260048101849052fd5b60405190632a87526960e21b82526004820152fd5b9091508381813d8311610d12575b610d0281836112e3565b8101031261015057519087610bde565b503d610cf8565b6040516301a1fdbb60e41b815260048101839052600e818701526d125058d8d95cdcd0dbdb9d1c9bdb60921b6044820152606490fd5b610d6f9150833d8511610d75575b610d6781836112e3565b8101906117f4565b86610b94565b503d610d5d565b6040513d6000823e3d90fd5b6040516301a1fdbb60e41b8152600481018490526006818801526504945524332360d41b6044820152606490fd5b610dcd9150843d8611610d7557610d6781836112e3565b87610b65565b856044916040519163e2517d3f60e01b8352336004840152820152fd5b9050816000805160206118ad8339815191525416141585610adb565b3461015057604036600319011261015057610e256112b7565b60243590610e316113ad565b6001600160a01b0316908115610eb15760008051602061186d833981519152805490828201809211610e9b576000926000805160206118cd833981519152926020925584845260008051602061184d833981519152825260408420818154019055604051908152a3005b634e487b7160e01b600052601160045260246000fd5b60405163ec442f0560e01b815260006004820152602490fd5b3461015057604036600319011261015057610ee36112cd565b336001600160a01b03821603610eff576106ac906004356115fb565b60405163334bd91960e11b8152600490fd5b3461015057600036600319011261015057602060ff60005416604051908152f35b34610150576040366003190112610150576106ac600435610f516112cd565b90806000526000805160206118ed833981519152602052610f79600160406000200154611427565b611586565b34610150576020366003190112610150576004356000526000805160206118ed8339815191526020526020600160406000200154604051908152f35b3461015057606036600319011261015057610fd36112b7565b610fdb6112cd565b60443590610fe883611374565b3360005260205260406000205492600019840361100a575b61076393506116b8565b828410611079576001600160a01b0381161561106057331561104757826107639461103483611374565b3360005260205203604060002055611000565b604051634a1406b160e11b815260006004820152602490fd5b60405163e602df0560e01b815260006004820152602490fd5b604051637dc7a0d960e11b81523360048201526024810185905260448101849052606490fd5b3461015057600036600319011261015057602060008051602061186d83398151915254604051908152f35b34610150576040366003190112610150576110e36112b7565b602435903315611060576001600160a01b03169081156110475761110633611374565b82600052602052806040600020556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b346101505760003660031901126101505760405160009060008051602061182d83398151915280549061117d8261167e565b90818452600192838116908160001461092c57506001146111a857610739846108df818803826112e3565b90935060005260209283600020916000925b8284106111d65750505081610739936108df92820101936108cf565b80548585018701529285019281016111ba565b34610150576020366003190112610150576004359063ffffffff60e01b8216809203610150576020916336372b0760e01b811490811561125d575b8115611232575b5015158152f35b637965db0b60e01b81149150811561124c575b508361122b565b6301ffc9a760e01b14905083611245565b637965db0b60e01b81149150611224565b6020808252825181830181905290939260005b8281106112a357505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501611281565b600435906001600160a01b038216820361015057565b602435906001600160a01b038216820361015057565b90601f801991011681019081106001600160401b038211176104a257604052565b6001600160401b0381116104a257601f01601f191660200190565b92919261132b82611304565b9161133960405193846112e3565b829481845281830111610150578281602093846000960137010152565b9080601f83011215610150578160206113719335910161131f565b90565b6001600160a01b031660009081527f52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace016020526040902090565b3360009081527f549fe2656c81d2947b3b913f0a53b9ea86c71e049f3a1b8aa23c09a8a05cb8d460205260409020547f9f2df0fed2c77648de5860a4cc508cd0818c85b8b8a1ab4ceeef8d981c8956a69060ff16156114095750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b806000526000805160206118ed83398151915260205260406000203360005260205260ff60406000205416156114095750565b6001600160a01b031660008181527fb7db2dd08fcb62d0c9e08c51941cae53c267786a0b75803fb7960902fc8ef97d60205260408120549091906000805160206118ed8339815191529060ff166114e3578280526020526040822081835260205260408220600160ff19825416179055339160008051602061180d8339815191528180a4600190565b505090565b6001600160a01b031660008181527fab71e3f32666744d246edff3f96e4bdafee2e9867098cdd118a979a7464786a8602052604081205490919060008051602061188d833981519152906000805160206118ed8339815191529060ff16611580578184526020526040832082845260205260408320600160ff1982541617905560008051602061180d833981519152339380a4600190565b50505090565b906000918083526000805160206118ed83398151915280602052604084209260018060a01b03169283855260205260ff60408520541615600014611580578184526020526040832082845260205260408320600160ff1982541617905560008051602061180d833981519152339380a4600190565b906000918083526000805160206118ed83398151915280602052604084209260018060a01b03169283855260205260ff60408520541660001461158057818452602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b90600182811c921680156116ae575b602083101461169857565b634e487b7160e01b600052602260045260246000fd5b91607f169161168d565b916001600160a01b038084169283156108565716928315610eb15760009083825260008051602061184d8339815191528060205260408320549184831061172f575082846000805160206118cd833981519152959360409388602097528652038282205586815220818154019055604051908152a3565b60405163391434e360e21b81526001600160a01b0391909116600482015260248101929092525060448101839052606490fd5b90611789575080511561177757805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806117bc575b61179a575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15611792565b60ff60008051602061190d8339815191525460401c16156117e257565b604051631afcd79f60e31b8152600490fd5b9081602091031261015057518015158103610150579056fe2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0352c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace0052c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace02189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e3360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbcddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00a264697066735822122064f2754949c7a5616102acfc1983cac826bd20810243de24114c019913508da264736f6c6343000814003300000000000000000000000082f1806aeab5ecb9a485eb041d5ed4940b123995000000000000000000000000cbd8dbcbdde849188bacbf19313043d102413985000000000000000000000000071df66df17f894606988f237ecb6ee36ab0512e000000000000000000000000cf5ca0b3d358c39daff24142d1bbc7c6b96395d3
Deployed Bytecode
0x60808060405260043610156200001457600080fd5b600090813560e01c90816301ffc9a71462000ae2575080631cda4a8d1462000a9b578063248a9ca31462000a6d5780632f2ff15d1462000a2857806336568abe14620009d9578063439577a014620009925780635db27897146200094b5780637a89862914620009045780638b7cc1e314620008bd57806391d148541462000871578063a217fddf1462000853578063ab6343c414620001a5578063b18fd8a8146200015e578063d547741f14620001155763ecd0026114620000d657600080fd5b34620001125780600319360112620001125760206040517ffc425f2263d0df187444b70e47283d622c70181c5baebb1306a01edba1ce184c8152f35b80fd5b50346200011257604036600319011262000112576200015a6004356200013a62000b3c565b908084528360205262000154600160408620015462000bfa565b62000ca0565b5080f35b503462000112578060031936011262000112576040517f000000000000000000000000071df66df17f894606988f237ecb6ee36ab0512e6001600160a01b03168152602090f35b5034620001125760c03660031901126200011257600435906001600160a01b038216820362000112576024356001600160401b0381116200067d57620001f090369060040162000b9f565b6044356001600160401b0381116200084f576200021290369060040162000b9f565b6064356001600160a01b03811690036200084f57608435906001600160a01b03821682036200068c577ffc425f2263d0df187444b70e47283d622c70181c5baebb1306a01edba1ce184c808552846020526040852033865260205260ff6040862054161562000831575060405163de7ea79d60e01b602082015260806024820152620002da81620002be620002ab60a483018862000d16565b8281036023190160448401528562000d16565b6012606483015233608483015203601f19810183528262000b7d565b604051906102d382018281106001600160401b038211176200081d5782916200032e916102d362000d8085397f000000000000000000000000b5276c436f65913cd5332de745d04fedeb4a21d49062000d58565b039085f0948515620006cf57604051633d1c7bfd60e01b81526001600160a01b03828116600483015260a4356024830152606480358216604484015285821690830152909290602090849060849082908a907f000000000000000000000000071df66df17f894606988f237ecb6ee36ab0512e165af192831562000812578693620007c0575b50916200045b939162000405620003f29694604051978896632c021a4360e21b602089015233602489015260e0604489015261010488019062000d16565b8681036023190160648801529062000d16565b6001600160a01b037f00000000000000000000000082f1806aeab5ecb9a485eb041d5ed4940b1239958116608487015293841660a486015290831660c4850152911660e483015203601f19810183528262000b7d565b604051906102d382018281106001600160401b03821117620007ac578291620004af916102d362000d8085397f00000000000000000000000006d4acc104b974cd99bf22e4572f48a051e596709062000d58565b039082f080156200079f5760405163a217fddf60e01b8082526001600160a01b0394851694929092169190602081600481885afa908115620006cf57849162000767575b50843b156200068c57604051632f2ff15d60e01b80825260048201929092526064356001600160a01b031660248201528481604481838a5af18015620006f25762000751575b5060405163d539139360e01b8152602081600481895afa908115620006f257859162000719575b50853b15620007155760405182815260048101919091526001600160a01b03841660248201528481604481838a5af18015620006f257908591620006fd575b5050823b156200068c576040516340f797bb60e01b815260048101869052848160248183885af18015620006f257908591620006da575b5050604051918252602082600481865afa918215620006cf57849262000690575b50823b156200068c5760405190815260048101919091526064356001600160a01b03166024820152828160448183865af18015620006815762000665575b50602092817fc914ea709f3eb6988a1adfd36b9ecfb22a736fce308704c1ed8c596a0301ef8e6040519480a38152f35b62000671839162000b53565b6200067d573862000635565b5080fd5b6040513d85823e3d90fd5b8380fd5b9091506020813d602011620006c6575b81620006af6020938362000b7d565b81010312620006c157519038620005f7565b600080fd5b3d9150620006a0565b6040513d86823e3d90fd5b620006e59062000b53565b6200068c578338620005d6565b6040513d87823e3d90fd5b620007089062000b53565b6200068c5783386200059f565b8480fd5b90506020813d60201162000748575b81620007376020938362000b7d565b81010312620006c157513862000560565b3d915062000728565b6200075f9094919462000b53565b923862000539565b90506020813d60201162000796575b81620007856020938362000b7d565b810103126200068c575138620004f3565b3d915062000776565b50604051903d90823e3d90fd5b634e487b7160e01b84526041600452602484fd5b9092506020813d60201162000809575b81620007df6020938362000b7d565b810103126200080557516001600160a01b03811681036200080557916200045b620003b4565b8580fd5b3d9150620007d0565b6040513d88823e3d90fd5b634e487b7160e01b87526041600452602487fd5b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b8280fd5b50346200011257806003193601126200011257602090604051908152f35b503462000112576040366003190112620001125760ff60406020926200089662000b3c565b60043582528185528282206001600160a01b03909116825284522054604051911615158152f35b503462000112578060031936011262000112576040517f00000000000000000000000082f1806aeab5ecb9a485eb041d5ed4940b1239956001600160a01b03168152602090f35b503462000112578060031936011262000112576040517f00000000000000000000000006d4acc104b974cd99bf22e4572f48a051e596706001600160a01b03168152602090f35b503462000112578060031936011262000112576040517f000000000000000000000000b5276c436f65913cd5332de745d04fedeb4a21d46001600160a01b03168152602090f35b503462000112578060031936011262000112576040517f000000000000000000000000cf5ca0b3d358c39daff24142d1bbc7c6b96395d36001600160a01b03168152602090f35b5034620001125760403660031901126200011257620009f762000b3c565b336001600160a01b0382160362000a16576200015a9060043562000ca0565b60405163334bd91960e11b8152600490fd5b50346200011257604036600319011262000112576200015a60043562000a4d62000b3c565b908084528360205262000a67600160408620015462000bfa565b62000c21565b5034620001125760203660031901126200011257600160406020926004358152808452200154604051908152f35b503462000112578060031936011262000112576040517f000000000000000000000000cbd8dbcbdde849188bacbf19313043d1024139856001600160a01b03168152602090f35b9050346200067d5760203660031901126200067d5760043563ffffffff60e01b81168091036200084f5760209250637965db0b60e01b811490811562000b2a575b5015158152f35b6301ffc9a760e01b1490503862000b23565b602435906001600160a01b0382168203620006c157565b6001600160401b03811162000b6757604052565b634e487b7160e01b600052604160045260246000fd5b90601f801991011681019081106001600160401b0382111762000b6757604052565b81601f82011215620006c1578035906001600160401b03821162000b67576040519262000bd7601f8401601f19166020018562000b7d565b82845260208383010111620006c157816000926020809301838601378301015290565b80600052600060205260406000203360005260205260ff6040600020541615620008315750565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054161560001462000c9b57808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b9060009180835282602052604083209160018060a01b03169182845260205260ff60408420541660001462000c9b5780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b919082519283825260005b84811062000d43575050826000602080949584010152601f8019910116010190565b60208183018101518483018201520162000d21565b6001600160a01b03909116815260406020820181905262000d7c9291019062000d16565b9056fe60806040526102d38038038061001481610194565b92833981019060408183031261018f5780516001600160a01b03811680820361018f5760208381015190936001600160401b03821161018f570184601f8201121561018f5780519061006d610068836101cf565b610194565b9582875285838301011161018f57849060005b83811061017b57505060009186010152813b15610163577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b03191682179055604051907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b600080a28351156101455750600080848461012c96519101845af4903d1561013c573d61011c610068826101cf565b908152600081943d92013e6101ea565b505b6040516085908161024e8239f35b606092506101ea565b9250505034610154575061012e565b63b398979f60e01b8152600490fd5b60249060405190634c9c8ce360e01b82526004820152fd5b818101830151888201840152869201610080565b600080fd5b6040519190601f01601f191682016001600160401b038111838210176101b957604052565b634e487b7160e01b600052604160045260246000fd5b6001600160401b0381116101b957601f01601f191660200190565b9061021157508051156101ff57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580610244575b610222575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561021a56fe60806040527f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc54600090819081906001600160a01b0316368280378136915af43d82803e15604b573d90f35b3d90fdfea264697066735822122064e05d6b50f7e38be358c5202a9c5465626b986a9ff403ce9b298bc4f4509dbb64736f6c63430008140033a26469706673582212204246a924d2afef461866302df991654983b700d98497f920d6b8d7669d14a81964736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000082f1806aeab5ecb9a485eb041d5ed4940b123995000000000000000000000000cbd8dbcbdde849188bacbf19313043d102413985000000000000000000000000071df66df17f894606988f237ecb6ee36ab0512e000000000000000000000000cf5ca0b3d358c39daff24142d1bbc7c6b96395d3
-----Decoded View---------------
Arg [0] : kycRegistry_ (address): 0x82F1806AEab5Ecb9a485eb041d5Ed4940b123995
Arg [1] : poolRegistry_ (address): 0xCbd8DbcBDDe849188BACbF19313043d102413985
Arg [2] : sharePriceCalculatorFactory_ (address): 0x071df66DF17F894606988F237Ecb6ee36aB0512e
Arg [3] : collateralManagerFactory_ (address): 0xcf5Ca0b3d358c39DAfF24142d1BBc7c6b96395d3
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 00000000000000000000000082f1806aeab5ecb9a485eb041d5ed4940b123995
Arg [1] : 000000000000000000000000cbd8dbcbdde849188bacbf19313043d102413985
Arg [2] : 000000000000000000000000071df66df17f894606988f237ecb6ee36ab0512e
Arg [3] : 000000000000000000000000cf5ca0b3d358c39daff24142d1bbc7c6b96395d3
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.