Source Code
Latest 7 from a total of 7 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Revoke Role | 13746789 | 410 days ago | IN | 0 FRAX | 0.00000083 | ||||
| Revoke Role | 13746769 | 410 days ago | IN | 0 FRAX | 0.00000082 | ||||
| Grant Role | 13746707 | 410 days ago | IN | 0 FRAX | 0.00000078 | ||||
| Grant Role | 13746696 | 410 days ago | IN | 0 FRAX | 0.00000084 | ||||
| Grant Role | 13746673 | 410 days ago | IN | 0 FRAX | 0.0000009 | ||||
| Allocate Amo | 13028892 | 426 days ago | IN | 0 FRAX | 0.00000075 | ||||
| Enable Amo Vault | 13028202 | 426 days ago | IN | 0 FRAX | 0.00000068 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
AmoManager
Compiler Version
v0.8.24+commit.e11b9ed9
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— dtrinity.org ————————————————————————————————— *
* *
* ▲ *
* ▲ ▲ *
* *
* ———————————————————————————————————————————————————————————————————————————————— *
* dTRINITY Protocol: https://github.com/dtrinity *
* ———————————————————————————————————————————————————————————————————————————————— */
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-5/access/AccessControl.sol";
import "@openzeppelin/contracts-5/utils/structs/EnumerableMap.sol";
import "contracts/shared/Constants.sol";
import "contracts/token/IERC20Stablecoin.sol";
import "contracts/dusd/AmoVault.sol";
/**
* @title AmoManager
* @dev Manages AMOs for dUSD
* Handles allocation, deallocation, collateral management, and profit management for AMO vaults.
*/
contract AmoManager is AccessControl, OracleAware {
using EnumerableMap for EnumerableMap.AddressToUintMap;
/* Core state */
EnumerableMap.AddressToUintMap private _amoVaults;
uint256 public totalAllocated;
IERC20Stablecoin public dusd;
CollateralVault public collateralHolderVault;
uint256 public immutable USD_UNIT;
/* Events */
event AmoVaultSet(address indexed amoVault, bool isActive);
event AmoAllocated(address indexed amoVault, uint256 dusdAmount);
event AmoDeallocated(address indexed amoVault, uint256 dusdAmount);
event ProfitsWithdrawn(address indexed amoVault, uint256 amount);
/* Roles */
bytes32 public constant AMO_ALLOCATOR_ROLE =
keccak256("AMO_ALLOCATOR_ROLE");
bytes32 public constant FEE_COLLECTOR_ROLE =
keccak256("FEE_COLLECTOR_ROLE");
/* Errors */
error InactiveAmoVault(address amoVault);
error AmoSupplyInvariantViolation(
uint256 startingSupply,
uint256 endingSupply
);
error AmoVaultAlreadyEnabled(address amoVault);
error CannotTransferDUSD();
error InsufficientProfits(
uint256 takeProfitValueInUsd,
int256 availableProfitInUsd
);
/**
* @notice Initializes the AmoManager contract.
* @param _dusd The address of the dUSD stablecoin.
* @param _collateralHolderVault The address of the collateral holder vault.
* @param _oracle The oracle for price feeds.
*/
constructor(
address _dusd,
address _collateralHolderVault,
IPriceOracleGetter _oracle
) OracleAware(_oracle, Constants.ORACLE_BASE_CURRENCY_UNIT) {
dusd = IERC20Stablecoin(_dusd);
collateralHolderVault = CollateralVault(_collateralHolderVault);
USD_UNIT = oracle.BASE_CURRENCY_UNIT();
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
grantRole(AMO_ALLOCATOR_ROLE, msg.sender);
grantRole(FEE_COLLECTOR_ROLE, msg.sender);
}
/* AMO */
/**
* @notice Allocates AMO tokens to an AMO vault.
* @param amoVault The address of the AMO vault.
* @param dusdAmount The amount of dUSD to allocate.
*/
function allocateAmo(
address amoVault,
uint256 dusdAmount
) public onlyRole(AMO_ALLOCATOR_ROLE) {
uint256 startingAmoSupply = totalAmoSupply();
// Make sure the vault is active
if (!isAmoActive(amoVault)) {
revert InactiveAmoVault(amoVault);
}
// Update the allocation for this vault
(, uint256 currentAllocation) = _amoVaults.tryGet(amoVault);
_amoVaults.set(amoVault, currentAllocation + dusdAmount);
// Make the deposit
totalAllocated += dusdAmount;
dusd.transfer(amoVault, dusdAmount);
// Check invariants
uint256 endingAmoSupply = totalAmoSupply();
if (endingAmoSupply != startingAmoSupply) {
revert AmoSupplyInvariantViolation(
startingAmoSupply,
endingAmoSupply
);
}
emit AmoAllocated(amoVault, dusdAmount);
}
/**
* @notice Deallocates AMO tokens from an AMO vault.
* @param amoVault The address of the AMO vault.
* @param dusdAmount The amount of dUSD to deallocate.
*/
function deallocateAmo(
address amoVault,
uint256 dusdAmount
) public onlyRole(AMO_ALLOCATOR_ROLE) {
uint256 startingAmoSupply = totalAmoSupply();
// We don't require that the vault is active or has allocation, since we want to allow withdrawing from inactive vaults
// If the vault is still active, make sure it has enough allocation and decrease it
(, uint256 currentAllocation) = _amoVaults.tryGet(amoVault);
if (currentAllocation > 0) {
// Update the allocation for this vault
_amoVaults.set(amoVault, currentAllocation - dusdAmount);
}
// Make the withdrawal
totalAllocated -= dusdAmount;
dusd.transferFrom(amoVault, address(this), dusdAmount);
// Check invariants
uint256 endingAmoSupply = totalAmoSupply();
if (endingAmoSupply != startingAmoSupply) {
revert AmoSupplyInvariantViolation(
startingAmoSupply,
endingAmoSupply
);
}
emit AmoDeallocated(amoVault, dusdAmount);
}
/**
* @notice Returns the total AMO supply.
* @return The total AMO supply.
*/
function totalAmoSupply() public view returns (uint256) {
uint256 freeBalance = dusd.balanceOf(address(this));
return freeBalance + totalAllocated;
}
/**
* @notice Decreases the AMO supply by burning dUSD.
* @param dusdAmount The amount of dUSD to burn.
*/
function decreaseAmoSupply(
uint256 dusdAmount
) public onlyRole(AMO_ALLOCATOR_ROLE) {
dusd.burn(dusdAmount);
}
/**
* @notice Checks if an AMO vault is active.
* @param amoVault The address of the AMO vault to check.
* @return True if the AMO vault is active, false otherwise.
*/
function isAmoActive(address amoVault) public view returns (bool) {
return _amoVaults.contains(amoVault);
}
/**
* @notice Returns the allocation for a specific AMO vault.
* @param amoVault The address of the AMO vault.
* @return The current allocation for the vault.
*/
function amoVaultAllocation(
address amoVault
) public view returns (uint256) {
(bool exists, uint256 allocation) = _amoVaults.tryGet(amoVault);
return exists ? allocation : 0;
}
/**
* @notice Returns the list of all AMO vaults.
* @return The list of AMO vault addresses.
*/
function amoVaults() public view returns (address[] memory) {
return _amoVaults.keys();
}
/**
* @notice Enables an AMO vault.
* @param amoVault The address of the AMO vault.
*/
function enableAmoVault(
address amoVault
) public onlyRole(DEFAULT_ADMIN_ROLE) {
if (_amoVaults.contains(amoVault)) {
revert AmoVaultAlreadyEnabled(amoVault);
}
_amoVaults.set(amoVault, 0);
emit AmoVaultSet(amoVault, true);
}
/**
* @notice Disables an AMO vault.
* @param amoVault The address of the AMO vault.
*/
function disableAmoVault(
address amoVault
) public onlyRole(DEFAULT_ADMIN_ROLE) {
if (!_amoVaults.contains(amoVault)) {
revert InactiveAmoVault(amoVault);
}
_amoVaults.remove(amoVault);
emit AmoVaultSet(amoVault, false);
}
/* Collateral Management */
/**
* @notice Returns the total collateral value of all active AMO vaults.
* @return The total collateral value in USD.
*/
function totalCollateralValue() public view returns (uint256) {
uint256 totalUsdValue = 0;
for (uint256 i = 0; i < _amoVaults.length(); i++) {
(address vaultAddress, ) = _amoVaults.at(i);
if (isAmoActive(vaultAddress)) {
totalUsdValue += AmoVault(vaultAddress).totalCollateralValue();
}
}
return totalUsdValue;
}
/**
* @notice Transfers collateral from an AMO vault to the holding vault.
* @param amoVault The address of the AMO vault.
* @param token The address of the collateral token to transfer.
* @param amount The amount of collateral to transfer.
*/
function transferFromAmoVaultToHoldingVault(
address amoVault,
address token,
uint256 amount
) public onlyRole(AMO_ALLOCATOR_ROLE) {
if (token == address(dusd)) {
revert CannotTransferDUSD();
}
// Update allocation
// A note on why we modify AMO allocation when we withdraw collateral:
// 1. When dUSD AMO enters the AMO vault, the dUSD is initially unbacked
// 2. Over time the AMO vault accrues collateral in exchange for distributing dUSD
// 3. We may be able to make better use of that collateral in a different collateral vault
// 4. So we transfer the collateral out of the AMO vault, but at that point the dUSD that
// converted to that collateral is now free-floating and fully backed
// 5. Thus we decrement the AMO allocation to reflect the fact that the dUSD is no longer
// unbacked, but is actually fully backed and circulating
uint256 collateralUsdValue = collateralHolderVault.assetValueFromAmount(
amount,
token
);
uint256 collateralInDusd = usdValueToDusdAmount(collateralUsdValue);
(, uint256 currentAllocation) = _amoVaults.tryGet(amoVault);
_amoVaults.set(amoVault, currentAllocation - collateralInDusd);
totalAllocated -= collateralInDusd;
// Transfer the collateral
AmoVault(amoVault).withdrawTo(
address(collateralHolderVault),
amount,
token
);
}
/**
* @notice Transfers collateral from the holding vault to an AMO vault.
* @param amoVault The address of the AMO vault.
* @param token The address of the collateral token to transfer.
* @param amount The amount of collateral to transfer.
*/
function transferFromHoldingVaultToAmoVault(
address amoVault,
address token,
uint256 amount
) public onlyRole(AMO_ALLOCATOR_ROLE) {
if (token == address(dusd)) {
revert CannotTransferDUSD();
}
if (!_amoVaults.contains(amoVault)) {
revert InactiveAmoVault(amoVault);
}
// Update allocation
// A note on why we modify AMO allocation when we deposit collateral:
// 1. When we deposit collateral, it can be used to buy back dUSD
// 2. When we buy back dUSD, the dUSD is now unbacked (a redemption)
// 3. Thus any collateral deposited to an AMO vault can create unbacked dUSD,
// which means the AMO allocation for that vault must be increased to reflect this
uint256 collateralUsdValue = collateralHolderVault.assetValueFromAmount(
amount,
token
);
uint256 collateralInDusd = usdValueToDusdAmount(collateralUsdValue);
(, uint256 currentAllocation) = _amoVaults.tryGet(amoVault);
_amoVaults.set(amoVault, currentAllocation + collateralInDusd);
totalAllocated += collateralInDusd;
// Transfer the collateral
collateralHolderVault.withdrawTo(amoVault, amount, token);
}
/* Profit Management */
/**
* @notice Returns the available profit for a specific vault in USD.
* @param vaultAddress The address of the AMO vault to check.
* @return The available profit in USD (can be negative).
*/
function availableVaultProfitsInUsd(
address vaultAddress
) public view returns (int256) {
uint256 totalVaultValueInUsd = AmoVault(vaultAddress).totalValue();
uint256 allocatedDusd = amoVaultAllocation(vaultAddress);
uint256 allocatedValueInUsd = dusdAmountToUsdValue(allocatedDusd);
return int256(totalVaultValueInUsd) - int256(allocatedValueInUsd);
}
/**
* @notice Withdraws profits from an AMO vault to a recipient.
* @param amoVault The AMO vault from which to withdraw profits.
* @param recipient The address to receive the profits.
* @param takeProfitToken The collateral token to withdraw.
* @param takeProfitAmount The amount of collateral to withdraw.
* @return takeProfitValueInUsd The value of the withdrawn profits in USD.
*/
function withdrawProfits(
AmoVault amoVault,
address recipient,
address takeProfitToken,
uint256 takeProfitAmount
)
public
onlyRole(FEE_COLLECTOR_ROLE)
returns (uint256 takeProfitValueInUsd)
{
// Leave open the possibility of withdrawing profits from inactive vaults
takeProfitValueInUsd = amoVault.assetValueFromAmount(
takeProfitAmount,
takeProfitToken
);
int256 _availableProfitInUsd = availableVaultProfitsInUsd(
address(amoVault)
);
// Make sure we are withdrawing less than the available profit
if (
_availableProfitInUsd <= 0 ||
int256(takeProfitValueInUsd) > _availableProfitInUsd
) {
revert InsufficientProfits(
takeProfitValueInUsd,
_availableProfitInUsd
);
}
// Withdraw profits from the vault
amoVault.withdrawTo(recipient, takeProfitAmount, takeProfitToken);
emit ProfitsWithdrawn(address(amoVault), takeProfitValueInUsd);
return takeProfitValueInUsd;
}
/**
* @notice Returns the total available profit across all AMO vaults in USD.
* @return The total available profit in USD.
*/
function availableProfitInUsd() public view returns (int256) {
int256 totalProfit = 0;
// Iterate through all AMO vaults
for (uint256 i = 0; i < _amoVaults.length(); i++) {
(address vaultAddress, ) = _amoVaults.at(i);
if (isAmoActive(vaultAddress)) {
totalProfit += availableVaultProfitsInUsd(vaultAddress);
}
}
return totalProfit;
}
/* Utility */
/**
* @notice Converts a USD value to an equivalent amount of dUSD tokens.
* @param usdValue The amount of USD value to convert.
* @return The equivalent amount of dUSD tokens.
*/
function usdValueToDusdAmount(
uint256 usdValue
) public view returns (uint256) {
uint8 dusdDecimals = dusd.decimals();
return (usdValue * (10 ** dusdDecimals)) / USD_UNIT;
}
/**
* @notice Converts an amount of dUSD tokens to an equivalent USD value.
* @param dusdAmount The amount of dUSD tokens to convert.
* @return The equivalent amount of USD value.
*/
function dusdAmountToUsdValue(
uint256 dusdAmount
) public view returns (uint256) {
uint8 dusdDecimals = dusd.decimals();
return
(dusdAmount * oracle.getAssetPrice(address(dusd))) /
(10 ** dusdDecimals);
}
}
/**
* @title ICollateralSum
* @dev Interface for contracts that can provide total collateral value.
*/
interface ICollateralSum {
/**
* @notice Returns the total collateral value of the implementing contract.
* @return The total collateral value in base value (e.g., USD).
*/
function totalCollateralValue() external view returns (uint256);
}// 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) (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/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/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/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/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/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.
pragma solidity ^0.8.20;
import {EnumerableSet} from "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* The following map types are supported:
*
* - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
* - `address -> uint256` (`AddressToUintMap`) since v4.6.0
* - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
* - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
* - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableMap.
* ====
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code repetition as possible, we write it in
// terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
// and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit in bytes32.
/**
* @dev Query for a nonexistent map key.
*/
error EnumerableMapNonexistentKey(bytes32 key);
struct Bytes32ToBytes32Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 key => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool, bytes32) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
bytes32 value = map._values[key];
if (value == 0 && !contains(map, key)) {
revert EnumerableMapNonexistentKey(key);
}
return value;
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
return map._keys.values();
}
// UintToUintMap
struct UintToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToUintMap storage map, uint256 index) internal view returns (uint256, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(key)));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintToAddressMap
struct UintToAddressMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(get(map._inner, bytes32(key)))));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
bytes32[] memory store = keys(map._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressToUintMap
struct AddressToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(AddressToUintMap storage map, address key) internal returns (bool) {
return remove(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
return contains(map._inner, bytes32(uint256(uint160(key))));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(AddressToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressToUintMap storage map, uint256 index) internal view returns (address, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (address(uint160(uint256(key))), uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(AddressToUintMap storage map, address key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, bytes32(uint256(uint160(key))));
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
bytes32[] memory store = keys(map._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// Bytes32ToUintMap
struct Bytes32ToUintMap {
Bytes32ToBytes32Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
return set(map._inner, key, bytes32(value));
}
/**
* @dev Removes a value from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
return remove(map._inner, key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
return contains(map._inner, key);
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
return length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the map. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32, uint256) {
(bytes32 key, bytes32 value) = at(map._inner, index);
return (key, uint256(value));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool, uint256) {
(bool success, bytes32 value) = tryGet(map._inner, key);
return (success, uint256(value));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
return uint256(get(map._inner, key));
}
/**
* @dev Return the an array containing all the keys
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
bytes32[] memory store = keys(map._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— dtrinity.org ————————————————————————————————— *
* *
* ▲ *
* ▲ ▲ *
* *
* ———————————————————————————————————————————————————————————————————————————————— *
* dTRINITY Protocol: https://github.com/dtrinity *
* ———————————————————————————————————————————————————————————————————————————————— */
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-5/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-5/token/ERC20/IERC20.sol";
import "contracts/token/IERC20Stablecoin.sol";
import "contracts/dusd/AmoManager.sol";
import "@openzeppelin/contracts-5/utils/Address.sol";
import "@openzeppelin/contracts-5/utils/ReentrancyGuard.sol";
import "contracts/dusd/CollateralVault.sol";
interface IRecoverable {
function recoverERC20(address token, address to, uint256 amount) external;
function recoverETH(address to, uint256 amount) external;
}
/**
* @title AmoVault
* @notice Base contract for AMO (Algorithmic Market Operations) vaults that manage dUSD and collateral assets
*/
abstract contract AmoVault is CollateralVault, IRecoverable, ReentrancyGuard {
using SafeERC20 for IERC20;
using Address for address payable;
/* Core state */
IERC20Stablecoin public immutable dusd;
uint8 public immutable dusdDecimals;
AmoManager public amoManager;
/* Roles */
bytes32 public constant RECOVERER_ROLE = keccak256("RECOVERER_ROLE");
/* Errors */
error CannotRecoverVaultToken(address token);
error InvalidAmoManager();
constructor(
address _dusd,
address _amoManager,
address _admin,
address _collateralWithdrawer,
address _recoverer,
IPriceOracleGetter _oracle
) CollateralVault(_oracle) {
dusd = IERC20Stablecoin(_dusd);
dusdDecimals = IERC20Metadata(_dusd).decimals();
amoManager = AmoManager(_amoManager);
_grantRole(DEFAULT_ADMIN_ROLE, _admin);
grantRole(COLLATERAL_WITHDRAWER_ROLE, _collateralWithdrawer);
grantRole(RECOVERER_ROLE, _recoverer);
approveAmoManager();
}
/**
* @notice Approves the AmoManager to spend dUSD on behalf of this contract
* @dev Only callable by the contract owner or an account with the DEFAULT_ADMIN_ROLE
*/
function approveAmoManager() public onlyRole(DEFAULT_ADMIN_ROLE) {
dusd.approve(address(amoManager), type(uint256).max);
}
/**
* @notice Sets a new AmoManager address
* @param _newAmoManager The address of the new AmoManager
* @dev Only callable by an account with the DEFAULT_ADMIN_ROLE
*/
function setAmoManager(
address _newAmoManager
) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (_newAmoManager == address(0)) revert InvalidAmoManager();
// Reset allowance for old AMO manager
dusd.approve(address(amoManager), 0);
// Set new AMO manager
amoManager = AmoManager(_newAmoManager);
// Approve new AMO manager
approveAmoManager();
}
/* Recovery */
/**
* @notice Recovers ERC20 tokens accidentally sent to the contract
* @param token The address of the token to recover
* @param to The address to send the tokens to
* @param amount The amount of tokens to recover
*/
function recoverERC20(
address token,
address to,
uint256 amount
) external onlyRole(RECOVERER_ROLE) nonReentrant {
if (token == address(dusd) || isCollateralSupported(token)) {
revert CannotRecoverVaultToken(token);
}
IERC20(token).safeTransfer(to, amount);
}
/**
* @notice Recovers ETH accidentally sent to the contract
* @param to The address to send the ETH to
* @param amount The amount of ETH to recover
*/
function recoverETH(
address to,
uint256 amount
) external onlyRole(RECOVERER_ROLE) {
payable(to).sendValue(amount);
}
/* Virtual functions */
/**
* @notice Calculates the total value of non-dUSD collateral assets in the vault
* @return The total value of collateral assets denominated in the base currency (e.g., USD)
* @dev Must be implemented by derived contracts
*/
function totalCollateralValue() public view virtual returns (uint256);
/**
* @notice Calculates the total value of dUSD holdings in the vault
* @return The total value of dUSD holdings denominated in the base currency (e.g., USD)
* @dev Must be implemented by derived contracts
*/
function totalDusdValue() public view virtual returns (uint256);
}// SPDX-License-Identifier: MIT
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— dtrinity.org ————————————————————————————————— *
* *
* ▲ *
* ▲ ▲ *
* *
* ———————————————————————————————————————————————————————————————————————————————— *
* dTRINITY Protocol: https://github.com/dtrinity *
* ———————————————————————————————————————————————————————————————————————————————— */
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-5/access/AccessControl.sol";
import "@openzeppelin/contracts-5/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts-5/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts-5/utils/structs/EnumerableSet.sol";
import "contracts/shared/Constants.sol";
import "contracts/lending/core/interfaces/IPriceOracleGetter.sol";
import "contracts/dusd/OracleAware.sol";
/**
* @title CollateralVault
* @notice Abstract contract for any contract that manages collateral assets
\ */
abstract contract CollateralVault is AccessControl, OracleAware {
using SafeERC20 for IERC20Metadata;
using EnumerableSet for EnumerableSet.AddressSet;
/* Core state */
EnumerableSet.AddressSet internal _supportedCollaterals;
/* Events */
event CollateralAllowed(address indexed collateralAsset);
event CollateralDisallowed(address indexed collateralAsset);
/* Roles */
bytes32 public constant COLLATERAL_MANAGER_ROLE =
keccak256("COLLATERAL_MANAGER_ROLE");
bytes32 public constant COLLATERAL_STRATEGY_ROLE =
keccak256("COLLATERAL_STRATEGY_ROLE");
bytes32 public constant COLLATERAL_WITHDRAWER_ROLE =
keccak256("COLLATERAL_WITHDRAWER_ROLE");
/* Errors */
error UnsupportedCollateral(address collateralAsset);
error CollateralAlreadyAllowed(address collateralAsset);
error NoOracleSupport(address collateralAsset);
error FailedToAddCollateral(address collateralAsset);
error CollateralNotSupported(address collateralAsset);
error MustSupportAtLeastOneCollateral();
error FailedToRemoveCollateral(address collateralAsset);
/**
* @notice Initializes the vault with an oracle and sets up initial roles
* @dev Grants all roles to the contract deployer initially
* @param oracle The price oracle to use for collateral valuation
*/
constructor(
IPriceOracleGetter oracle
) OracleAware(oracle, Constants.ORACLE_BASE_CURRENCY_UNIT) {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender); // This is the super admin
grantRole(COLLATERAL_MANAGER_ROLE, msg.sender);
grantRole(COLLATERAL_WITHDRAWER_ROLE, msg.sender);
grantRole(COLLATERAL_STRATEGY_ROLE, msg.sender);
}
/* Deposit */
/**
* @notice Deposit collateral into the vault
* @param collateralAmount The amount of collateral to deposit
* @param collateralAsset The address of the collateral asset
*/
function deposit(uint256 collateralAmount, address collateralAsset) public {
return _deposit(msg.sender, collateralAmount, collateralAsset);
}
/**
* @notice Deposit collateral into the vault from a specific address
* @param depositer The address providing the collateral
* @param collateralAmount The amount of collateral to deposit
* @param collateralAsset The address of the collateral asset
*/
function depositFrom(
address depositer,
uint256 collateralAmount,
address collateralAsset
) public {
return _deposit(depositer, collateralAmount, collateralAsset);
}
/**
* @notice Internal function to deposit collateral into the vault
* @param depositer The address providing the collateral
* @param collateralAmount The amount of collateral to deposit
* @param collateralAsset The address of the collateral asset
*/
function _deposit(
address depositer,
uint256 collateralAmount,
address collateralAsset
) internal {
if (!_supportedCollaterals.contains(collateralAsset)) {
revert UnsupportedCollateral(collateralAsset);
}
IERC20Metadata(collateralAsset).safeTransferFrom(
depositer,
address(this),
collateralAmount
);
}
/* Withdrawal */
/**
* @notice Withdraws collateral from the vault
* @param collateralAmount The amount of collateral to withdraw
* @param collateralAsset The address of the collateral asset
*/
function withdraw(
uint256 collateralAmount,
address collateralAsset
) public onlyRole(COLLATERAL_WITHDRAWER_ROLE) {
return _withdraw(msg.sender, collateralAmount, collateralAsset);
}
/**
* @notice Withdraws collateral from the vault to a specific address
* @param recipient The address receiving the collateral
* @param collateralAmount The amount of collateral to withdraw
* @param collateralAsset The address of the collateral asset
*/
function withdrawTo(
address recipient,
uint256 collateralAmount,
address collateralAsset
) public onlyRole(COLLATERAL_WITHDRAWER_ROLE) {
return _withdraw(recipient, collateralAmount, collateralAsset);
}
/**
* @notice Internal function to withdraw collateral from the vault
* @param withdrawer The address withdrawing the collateral
* @param collateralAmount The amount of collateral to withdraw
* @param collateralAsset The address of the collateral asset
*/
function _withdraw(
address withdrawer,
uint256 collateralAmount,
address collateralAsset
) internal {
IERC20Metadata(collateralAsset).safeTransfer(
withdrawer,
collateralAmount
);
}
/* Collateral Info */
/**
* @notice Calculates the total value of all assets in the vault
* @return usdValue The total value of all assets in USD
*/
function totalValue() public view virtual returns (uint256 usdValue);
/**
* @notice Calculates the USD value of a given amount of an asset
* @param assetAmount The amount of the asset
* @param asset The address of the asset
* @return usdValue The USD value of the asset
*/
function assetValueFromAmount(
uint256 assetAmount,
address asset
) public view returns (uint256 usdValue) {
uint256 assetPrice = oracle.getAssetPrice(asset);
uint8 assetDecimals = IERC20Metadata(asset).decimals();
return (assetPrice * assetAmount) / (10 ** assetDecimals);
}
/**
* @notice Calculates the amount of an asset that corresponds to a given USD value
* @param usdValue The USD value
* @param asset The address of the asset
* @return assetAmount The amount of the asset
*/
function assetAmountFromValue(
uint256 usdValue,
address asset
) public view returns (uint256 assetAmount) {
uint256 assetPrice = oracle.getAssetPrice(asset);
uint8 assetDecimals = IERC20Metadata(asset).decimals();
return (usdValue * (10 ** assetDecimals)) / assetPrice;
}
/* Collateral management */
/**
* @notice Allows a new collateral asset
* @param collateralAsset The address of the collateral asset
*/
function allowCollateral(
address collateralAsset
) public onlyRole(COLLATERAL_MANAGER_ROLE) {
if (_supportedCollaterals.contains(collateralAsset)) {
revert CollateralAlreadyAllowed(collateralAsset);
}
if (oracle.getAssetPrice(collateralAsset) == 0) {
revert NoOracleSupport(collateralAsset);
}
if (!_supportedCollaterals.add(collateralAsset)) {
revert FailedToAddCollateral(collateralAsset);
}
emit CollateralAllowed(collateralAsset);
}
/**
* @notice Disallows a previously supported collateral asset
* @dev Requires at least one collateral asset to remain supported
* @param collateralAsset The address of the collateral asset to disallow
*/
function disallowCollateral(
address collateralAsset
) public onlyRole(COLLATERAL_MANAGER_ROLE) {
if (!_supportedCollaterals.contains(collateralAsset)) {
revert CollateralNotSupported(collateralAsset);
}
if (_supportedCollaterals.length() <= 1) {
revert MustSupportAtLeastOneCollateral();
}
if (!_supportedCollaterals.remove(collateralAsset)) {
revert FailedToRemoveCollateral(collateralAsset);
}
emit CollateralDisallowed(collateralAsset);
}
/**
* @notice Checks if a given asset is supported as collateral
* @param collateralAsset The address of the collateral asset to check
* @return bool True if the asset is supported, false otherwise
*/
function isCollateralSupported(
address collateralAsset
) public view returns (bool) {
return _supportedCollaterals.contains(collateralAsset);
}
/**
* @notice Returns a list of all supported collateral assets
* @return address[] Array of collateral asset addresses
*/
function listCollateral() public view returns (address[] memory) {
return _supportedCollaterals.values();
}
/**
* @notice Calculates the total USD value of all supported collateral assets in the vault
* @dev Iterates through all supported collaterals and sums their USD values
* @return uint256 The total value in USD
*/
function _totalValueOfSupportedCollaterals()
internal
view
returns (uint256)
{
uint256 totalUsdValue = 0;
for (uint256 i = 0; i < _supportedCollaterals.length(); i++) {
address collateral = _supportedCollaterals.at(i);
uint256 collateralPrice = oracle.getAssetPrice(collateral);
uint8 collateralDecimals = IERC20Metadata(collateral).decimals();
uint256 collateralValue = (collateralPrice *
IERC20Metadata(collateral).balanceOf(address(this))) /
(10 ** collateralDecimals);
totalUsdValue += collateralValue;
}
return totalUsdValue;
}
}// SPDX-License-Identifier: MIT
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— dtrinity.org ————————————————————————————————— *
* *
* ▲ *
* ▲ ▲ *
* *
* ———————————————————————————————————————————————————————————————————————————————— *
* dTRINITY Protocol: https://github.com/dtrinity *
* ———————————————————————————————————————————————————————————————————————————————— */
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-5/access/AccessControl.sol";
import "contracts/lending/core/interfaces/IPriceOracleGetter.sol";
/**
* @title OracleAware
* @notice Abstract contract that provides oracle functionality to other contracts
*/
abstract contract OracleAware is AccessControl {
/* Core state */
IPriceOracleGetter public oracle;
uint256 public baseCurrencyUnit;
/* Events */
event OracleSet(address indexed newOracle);
/* Errors */
error IncorrectBaseCurrencyUnit(uint256 baseCurrencyUnit);
/**
* @notice Initializes the contract with an oracle and base currency unit
* @param initialOracle The initial oracle to use for price feeds
* @param _baseCurrencyUnit The base currency unit for price calculations
* @dev Sets up the initial oracle and base currency unit values
*/
constructor(IPriceOracleGetter initialOracle, uint256 _baseCurrencyUnit) {
oracle = initialOracle;
baseCurrencyUnit = _baseCurrencyUnit;
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/**
* @notice Sets the oracle to use for collateral valuation
* @param newOracle The new oracle to use
*/
function setOracle(
IPriceOracleGetter newOracle
) public onlyRole(DEFAULT_ADMIN_ROLE) {
if (newOracle.BASE_CURRENCY_UNIT() != baseCurrencyUnit) {
revert IncorrectBaseCurrencyUnit(baseCurrencyUnit);
}
oracle = newOracle;
emit OracleSet(address(newOracle));
}
/**
* @notice Updates the base currency unit used for price calculations
* @param _newBaseCurrencyUnit The new base currency unit to set
* @dev Only used if the oracle's base currency unit changes
*/
function setBaseCurrencyUnit(
uint256 _newBaseCurrencyUnit
) public onlyRole(DEFAULT_ADMIN_ROLE) {
baseCurrencyUnit = _newBaseCurrencyUnit;
}
}// SPDX-License-Identifier: AGPL-3.0
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— dtrinity.org ————————————————————————————————— *
* *
* ▲ *
* ▲ ▲ *
* *
* ———————————————————————————————————————————————————————————————————————————————— *
* dTRINITY Protocol: https://github.com/dtrinity *
* ———————————————————————————————————————————————————————————————————————————————— */
pragma solidity ^0.8.0;
/**
* @title IPriceOracleGetter
* @author Aave
* @notice Interface for the Aave price oracle.
*/
interface IPriceOracleGetter {
/**
* @notice Returns the base currency address
* @dev Address 0x0 is reserved for USD as base currency.
* @return Returns the base currency address.
*/
function BASE_CURRENCY() external view returns (address);
/**
* @notice Returns the base currency unit
* @dev 1 ether for ETH, 1e8 for USD.
* @return Returns the base currency unit.
*/
function BASE_CURRENCY_UNIT() external view returns (uint256);
/**
* @notice Returns the asset price in the base currency
* @param asset The address of the asset
* @return The price of the asset
*/
function getAssetPrice(address asset) external view returns (uint256);
}// SPDX-License-Identifier: MIT
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— dtrinity.org ————————————————————————————————— *
* *
* ▲ *
* ▲ ▲ *
* *
* ———————————————————————————————————————————————————————————————————————————————— *
* dTRINITY Protocol: https://github.com/dtrinity *
* ———————————————————————————————————————————————————————————————————————————————— */
pragma solidity ^0.8.0;
library Constants {
// Shared definitions of how we represent percentages and basis points
uint16 public constant ONE_BPS = 100; // 1 basis point with 2 decimals
uint32 public constant ONE_PERCENT_BPS = ONE_BPS * 100;
uint32 public constant ONE_HUNDRED_PERCENT_BPS = ONE_PERCENT_BPS * 100;
uint32 public constant ORACLE_BASE_CURRENCY_UNIT = 1e8;
}// SPDX-License-Identifier: Unlicense
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— dtrinity.org ————————————————————————————————— *
* *
* ▲ *
* ▲ ▲ *
* *
* ———————————————————————————————————————————————————————————————————————————————— *
* dTRINITY Protocol: https://github.com/dtrinity *
* ———————————————————————————————————————————————————————————————————————————————— */
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-5/token/ERC20/IERC20.sol";
interface IERC20Stablecoin is IERC20 {
function mint(address to, uint256 amount) external;
function burn(uint256 amount) external;
function burnFrom(address account, uint256 amount) external;
function decimals() external view returns (uint8);
}{
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"viaIR": true,
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_dusd","type":"address"},{"internalType":"address","name":"_collateralHolderVault","type":"address"},{"internalType":"contract IPriceOracleGetter","name":"_oracle","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"},{"inputs":[{"internalType":"uint256","name":"startingSupply","type":"uint256"},{"internalType":"uint256","name":"endingSupply","type":"uint256"}],"name":"AmoSupplyInvariantViolation","type":"error"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"}],"name":"AmoVaultAlreadyEnabled","type":"error"},{"inputs":[],"name":"CannotTransferDUSD","type":"error"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"}],"name":"InactiveAmoVault","type":"error"},{"inputs":[{"internalType":"uint256","name":"baseCurrencyUnit","type":"uint256"}],"name":"IncorrectBaseCurrencyUnit","type":"error"},{"inputs":[{"internalType":"uint256","name":"takeProfitValueInUsd","type":"uint256"},{"internalType":"int256","name":"availableProfitInUsd","type":"int256"}],"name":"InsufficientProfits","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"amoVault","type":"address"},{"indexed":false,"internalType":"uint256","name":"dusdAmount","type":"uint256"}],"name":"AmoAllocated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"amoVault","type":"address"},{"indexed":false,"internalType":"uint256","name":"dusdAmount","type":"uint256"}],"name":"AmoDeallocated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"amoVault","type":"address"},{"indexed":false,"internalType":"bool","name":"isActive","type":"bool"}],"name":"AmoVaultSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOracle","type":"address"}],"name":"OracleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"amoVault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ProfitsWithdrawn","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":"AMO_ALLOCATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_COLLECTOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"USD_UNIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"},{"internalType":"uint256","name":"dusdAmount","type":"uint256"}],"name":"allocateAmo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"}],"name":"amoVaultAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"amoVaults","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableProfitInUsd","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vaultAddress","type":"address"}],"name":"availableVaultProfitsInUsd","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseCurrencyUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralHolderVault","outputs":[{"internalType":"contract CollateralVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"},{"internalType":"uint256","name":"dusdAmount","type":"uint256"}],"name":"deallocateAmo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"dusdAmount","type":"uint256"}],"name":"decreaseAmoSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"}],"name":"disableAmoVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dusd","outputs":[{"internalType":"contract IERC20Stablecoin","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"dusdAmount","type":"uint256"}],"name":"dusdAmountToUsdValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"}],"name":"enableAmoVault","outputs":[],"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":"address","name":"amoVault","type":"address"}],"name":"isAmoActive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IPriceOracleGetter","name":"","type":"address"}],"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":"uint256","name":"_newBaseCurrencyUnit","type":"uint256"}],"name":"setBaseCurrencyUnit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPriceOracleGetter","name":"newOracle","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAllocated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAmoSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateralValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFromAmoVaultToHoldingVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"amoVault","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFromHoldingVaultToAmoVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"usdValue","type":"uint256"}],"name":"usdValueToDusdAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract AmoVault","name":"amoVault","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"takeProfitToken","type":"address"},{"internalType":"uint256","name":"takeProfitAmount","type":"uint256"}],"name":"withdrawProfits","outputs":[{"internalType":"uint256","name":"takeProfitValueInUsd","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60a060409080825234620001ef5760608162001ea0803803809162000025828562000207565b833981010312620001ef576200003b8162000241565b602091836200004c84830162000241565b9101516001600160a01b039081811690819003620001ef578185938160049660018060a01b0319948560015416176001556305f5e100600255620000903362000256565b50168360075416176007551690600854161760085560015416845192838092638c89b64f60e01b82525afa908115620001fc57600091620001c4575b50608052620000db3362000256565b5060008051602062001e60833981519152600052600081526001826000200154806000528260002033600052825260ff83600020541615620001a757506200012333620002d6565b5060008051602062001e8083398151915260005260008152600182600020015490816000528260002090336000525260ff826000205416156200018b57506200016c3362000367565b5051611a4c9081620003f48239608051818181610f9101526114a70152f35b604491519063e2517d3f60e01b82523360048301526024820152fd5b60449083519063e2517d3f60e01b82523360048301526024820152fd5b90508181813d8311620001f4575b620001de818362000207565b81010312620001ef575138620000cc565b600080fd5b503d620001d2565b83513d6000823e3d90fd5b601f909101601f19168101906001600160401b038211908210176200022b57604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001ef57565b6001600160a01b031660008181527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205490919060ff16620002d257818052816020526040822081835260205260408220600160ff19825416179055339160008051602062001e408339815191528180a4600190565b5090565b6001600160a01b031660008181527f3e202b319ce29fc3d407c763c9df930d4929e19b1d8f64d08ac659c98dd25ce3602052604081205490919060008051602062001e608339815191529060ff166200036257808352826020526040832082845260205260408320600160ff1982541617905560008051602062001e40833981519152339380a4600190565b505090565b6001600160a01b031660008181527f92227d48d66cabccdfe59bda1e8cb95f286820615128bd3f8666d1f5ec8b70aa602052604081205490919060008051602062001e808339815191529060ff166200036257808352826020526040832082845260205260408320600160ff1982541617905560008051602062001e40833981519152339380a460019056fe6040608081526004908136101561001557600080fd5b600091823560e01c8063012f257a146110a557806301ffc9a7146110505780631661190914610fb45780631baf72bf14610f795780631c364f7f14610f59578063248a9ca314610f305780632a6739a214610e5d5780632f2ff15d14610e3457806336568abe14610ded578063375f52e414610dcd5780633a2e845a14610d115780633c2df49914610cd657806345f7f24914610cb75780634a0bbabb14610c955780634fc6c75314610a985780635dd26add146109ff57806362a2a47c146109c457806371f3588d1461099e578063764c42301461088457806377003194146107e9578063773df151146107cc5780637adbf973146106dc5780637dc0d1d0146106b357806385b30e181461066d57806391d1485414610629578063a217fddf1461060e578063d547741f146105d0578063de1409ce146105a7578063e67736bf1461057e578063ed1f3e4c1461041d578063f3bddde1146103fe578063fab9300e1461024a578063fb5b3db7146102195763fdc9a0381461019757600080fd5b829134610215576020366003190112610215576101b2611632565b6007546001600160a01b031691823b156102105783926024849284519586938492630852cd8d60e31b84528035908401525af190811561020757506101f45750f35b6101fd90611285565b6102045780f35b80fd5b513d84823e3d90fd5b505050fd5b5050fd5b5050346102465760203660031901126102465760209061023f61023a611224565b611611565b9051908152f35b5080fd5b508290346102465761025b36611250565b610269959295939193611632565b6007546001600160a01b0396908716858816146103ee578681169661029b886000526004602052604060002054151590565b156103d7578697816008541660208751809263e00cb4a560e01b825281806102db8d8b8d84019092916020906040830194835260018060a01b0316910152565b03915afa9081156103cd57899161038f575b50906103246102fe61032d93611458565b916103138361030c8361182e565b905061144b565b818c526005602052898c20556118ab565b5060065461144b565b6006556008541690813b1561038b57845163627160f360e11b81526001600160a01b039182169481019485526020850193909352909416604083015292849184919082908490829060600103925af190811561020757506101f45750f35b8680fd5b919850506020813d6020116103c5575b816103ac602093836112af565b810103126103c057518897906103246102ed565b600080fd5b3d915061039f565b87513d8b823e3d90fd5b8451631c5620f360e31b8152808501899052602490fd5b8351633e1b3a6d60e01b81528390fd5b5050346102465781600319360112610246576020906002549051908152f35b503461057a578160031936011261057a57610436611224565b9160243591610443611632565b61044b611595565b6001600160a01b0394851660008181526004602052604090205490959015610563576020879161047e8761030c8a61182e565b8884526005835286842055610492886118ab565b5061049f8760065461144b565b60065560075416604486518094819363a9059cbb60e01b83528b898401528a60248401525af180156105595761052a575b506104d9611595565b90808203610511575050507f9b8105582b12481c6193ec3b651067ba5c410f1474d24704b0a67f89086da6149160209151908152a280f35b6044935192637614d04360e11b84528301526024820152fd5b61054b9060203d602011610552575b61054381836112af565b81019061157d565b50386104d0565b503d610539565b84513d89823e3d90fd5b8351631c5620f360e31b8152808401879052602490fd5b8280fd5b50503461024657816003193601126102465760085490516001600160a01b039091168152602090f35b50503461024657816003193601126102465760075490516001600160a01b039091168152602090f35b50903461057a578060031936011261057a5761060a913561060560016105f461123a565b9383875286602052862001546116e6565b6117b9565b5080f35b50503461024657816003193601126102465751908152602090f35b503461057a578160031936011261057a578160209361064661123a565b92358152808552209060018060a01b0316600052825260ff81600020541690519015158152f35b505034610246576020366003190112610246576020906106aa6001600160a01b03610696611224565b166000526004602052604060002054151590565b90519015158152f35b50503461024657816003193601126102465760015490516001600160a01b039091168152602090f35b503461057a57602036600319011261057a5780356001600160a01b03811692908390036107c85761070b6116ac565b8051638c89b64f60e01b8152906020828481875afa9182156107be57859261078a575b50600254809203610775575050600180546001600160a01b03191683179055507f3f32684a32a11dabdbb8c0177de80aa3ae36a004d75210335b49e544e48cd0aa8280a280f35b51639b6812b960e01b81529182015260249150fd5b9091506020813d6020116107b6575b816107a6602093836112af565b810103126103c05751903861072e565b3d9150610799565b81513d87823e3d90fd5b8380fd5b50503461024657816003193601126102465760209061023f611595565b50903461057a57602036600319011261057a57610804611224565b61080c6116ac565b6001600160a01b031660008181526004602052604090205490929061086f575060207f2017a03d63122e396be484633b7d9ed729e8fabe2e0587ccd5e42d8bfde8670091838552600582528481812055610865846118ab565b505160018152a280f35b82602492519163ad2a3c3160e01b8352820152fd5b503461057a578160031936011261057a5761089d611224565b91602435916108aa611632565b6108b2611595565b6001600160a01b039485169486906020906108cc8861182e565b90508781610979575b6108e291506006546112d1565b6006556007541660648651809481936323b872dd60e01b83528b898401523060248401528a60448401525af180156105595761095a575b50610922611595565b90808203610511575050507fc57e1ebcdc3ba533a9852552e139d8119d9599c48177705d00b4100a32c046d89160209151908152a280f35b6109729060203d6020116105525761054381836112af565b5038610919565b610982916112d1565b8884526005835286842055610996886118ab565b5038876108d5565b5050346102465760203660031901126102465760209061023f6109bf611224565b6114eb565b505034610246578160031936011261024657602090517f2dca0f5ce7e75a4b43fe2b0d6f5d0b7a2bf92ecf89f8f0aa17b8308b670388218152f35b5091903461024657816003193601126102465760035482805b828110610a29576020858551908152f35b610a328161170c565b506001600160a01b038116600090815260046020526040902054610a5a575b50600101610a18565b610a66909591956114eb565b90828282019283129112908015821691151617610a8557936001610a51565b634e487b7160e01b825260118652602482fd5b50919034610246576080366003190112610246576001600160a01b03918335838116919082900361020457610acb61123a565b60443594851685036103c057606435907f2dca0f5ce7e75a4b43fe2b0d6f5d0b7a2bf92ecf89f8f0aa17b8308b670388219687600052602097600089528660002033600052895260ff87600020541615610c785750855163e00cb4a560e01b81528181018481526001600160a01b03891660208201529097908990899081906040010381895afa978815610c6e578598610c3f575b50610b6a866114eb565b858113801590610c36575b610c1a5750853b15610c1657865163627160f360e11b81526001600160a01b0393841692810192835260208301949094529091166040820152829082908190606001038183875af18015610c0c57610bf8575b50507f124fd12bda4dcb813885ca782fb4ea9350d4f752b1a881cadf8742d8f128bb8e848351858152a251908152f35b610c028291611285565b6102045780610bc8565b84513d84823e3d90fd5b8480fd5b826044918a8a5192631d7ed6ef60e11b84528301526024820152fd5b50808913610b75565b9097508881813d8311610c67575b610c5781836112af565b810103126103c057519638610b60565b503d610c4d565b87513d87823e3d90fd5b6044925086519163e2517d3f60e01b835233908301526024820152fd5b83823461024657602036600319011261024657610cb06116ac565b3560025580f35b5050346102465781600319360112610246576020906006549051908152f35b505034610246578160031936011261024657602090517fc304524b36c64136ce5783ff1658be193c8352a90708830b9863880958c1fddd8152f35b8284346102045780600319360112610204579080519182906003549182855260208095018093600384527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90845b818110610db95750505081610d759103826112af565b83519485948186019282875251809352850193925b828110610d9957505050500390f35b83516001600160a01b031685528695509381019392810192600101610d8a565b825484529288019260019283019201610d5f565b509134610204576020366003190112610204575061023f60209235611458565b50919034610246578060031936011261024657610e0861123a565b90336001600160a01b03831603610e25575061060a9192356117b9565b5163334bd91960e11b81528390fd5b50903461057a578060031936011261057a5761060a9135610e5860016105f461123a565b61173b565b50919034610246578160031936011261024657818093600354945b858110610e89576020858551908152f35b610e928161170c565b506001600160a01b0316600081815260046020526040902054610eb9575b50600101610e78565b849591955180916315339cd160e11b8252818560209485935afa918215610f26578592610ef6575b5050610eef9060019261144b565b9490610eb0565b90809250813d8311610f1f575b610f0d81836112af565b810103126103c0575181610eef610ee1565b503d610f03565b86513d87823e3d90fd5b503461057a57602036600319011261057a57816020936001923581528085522001549051908152f35b509134610204576020366003190112610204575061023f60209235611351565b505034610246578160031936011261024657602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b50903461057a57602036600319011261057a57610fcf611224565b610fd76116ac565b6001600160a01b03166000818152600460205260409020549092901561103b575060207f2017a03d63122e396be484633b7d9ed729e8fabe2e0587ccd5e42d8bfde8670091838552600582526000818620556110328461192d565b5051848152a280f35b826024925191631c5620f360e31b8352820152fd5b503461057a57602036600319011261057a57359063ffffffff60e01b821680920361057a5760209250637965db0b60e01b8214918215611094575b50519015158152f35b6301ffc9a760e01b1491503861108b565b50903461057a576110b536611250565b9190936110c0611632565b6007546001600160a01b039081168682161461121557600854855163e00cb4a560e01b81528084018681526001600160a01b0389166020828101919091528a9796959493909183918516908290819060400103915afa90811561120b57829087926111d1575b5061113361116892611458565b94169361114a816111438761182e565b90506112d1565b85885260056020528888205561115f856118ab565b506006546112d1565b6006556008541690823b15610c1657855163627160f360e11b81526001600160a01b03928316918101918252602082019490945295166040860152909384919082908490829060600103925af190811561020757506111c5575080f35b6111ce90611285565b80f35b9150506020813d602011611203575b816111ed602093836112af565b810103126111ff575181611133611126565b8580fd5b3d91506111e0565b87513d88823e3d90fd5b508351633e1b3a6d60e01b8152fd5b600435906001600160a01b03821682036103c057565b602435906001600160a01b03821682036103c057565b60609060031901126103c0576001600160a01b039060043582811681036103c0579160243590811681036103c0579060443590565b67ffffffffffffffff811161129957604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761129957604052565b919082039182116112de57565b634e487b7160e01b600052601160045260246000fd5b908160209103126103c0575160ff811681036103c05790565b818102929181159184041417156112de57565b60ff16604d81116112de57600a0a90565b811561133b570490565b634e487b7160e01b600052601260045260246000fd5b60075460405163313ce56760e01b81526020926001600160a01b03929091908316908481600481855afa93841561141057859160009561141c575b50600154169160246040518094819363b3596f0760e01b835260048301525afa938415611410576000946113dc575b50506113cd6113d9936113d39261130d565b91611320565b90611331565b90565b90809450813d8311611409575b6113f381836112af565b810103126103c0579151916113cd6113d36113bb565b503d6113e9565b6040513d6000823e3d90fd5b61143d919550823d8411611444575b61143581836112af565b8101906112f4565b933861138c565b503d61142b565b919082018092116112de57565b60075460405163313ce56760e01b81529190602090839060049082906001600160a01b03165afa9081156114105761149f6114a5926113d9946000916114cc575b50611320565b9061130d565b7f000000000000000000000000000000000000000000000000000000000000000090611331565b6114e5915060203d6020116114445761143581836112af565b38611499565b6040516306a61f7560e51b8152906020826004816001600160a01b0385165afa91821561141057600092611547575b5061152761152c91611611565b611351565b90600082820392128183128116918313901516176112de5790565b90916020823d602011611575575b81611562602093836112af565b810103126102045750519061152761151a565b3d9150611555565b908160209103126103c0575180151581036103c05790565b6007546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa8015611410576000906115dd575b6113d991506006549061144b565b506020813d602011611609575b816115f7602093836112af565b810103126103c0576113d990516115cf565b3d91506115ea565b611623906001600160a01b031661182e565b901561162c5790565b50600090565b3360009081527f3e202b319ce29fc3d407c763c9df930d4929e19b1d8f64d08ac659c98dd25ce360205260409020547fc304524b36c64136ce5783ff1658be193c8352a90708830b9863880958c1fddd9060ff161561168e5750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff161561168e5750565b80600052600060205260406000203360005260205260ff604060002054161561168e5750565b6117159061185e565b905460039190911b1c6000818152600560205260409020546001600160a01b0390911691565b9060009180835282602052604083209160018060a01b03169182845260205260ff604084205416156000146117b457808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054166000146117b45780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b6000526005602052604060002054801560001461185957506004602052604060002054151590600090565b600191565b6003548110156118955760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b634e487b7160e01b600052603260045260246000fd5b60008181526004602052604081205461192857600354680100000000000000008110156119145790826119006118e98460016040960160035561185e565b819391549060031b91821b91600019901b19161790565b905560035492815260046020522055600190565b634e487b7160e01b82526041600452602482fd5b905090565b600090808252600490816020526040832054801515600014611a1057600019908082018181116119fd57600354908382019182116119ea578181036119b7575b50505060035480156119a4578101906119858261185e565b909182549160031b1b1916905560035582526020526040812055600190565b634e487b7160e01b855260318452602485fd5b6119d56119c66118e99361185e565b90549060031b1c92839261185e565b9055855283602052604085205538808061196d565b634e487b7160e01b875260118652602487fd5b634e487b7160e01b865260118552602486fd5b5050509056fea2646970667358221220ed0cc9e4793e2f66363a17dcb405995b63abfe7b608c846ccc9e71ce65de574c64736f6c634300081800332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0dc304524b36c64136ce5783ff1658be193c8352a90708830b9863880958c1fddd2dca0f5ce7e75a4b43fe2b0d6f5d0b7a2bf92ecf89f8f0aa17b8308b67038821000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a0000000000000000000000007d3d464c37fa2d80407cc940490c6e4d763909bc000000000000000000000000fa7560956807d95dcef22990ddd92e38dbaf5cdd
Deployed Bytecode
0x6040608081526004908136101561001557600080fd5b600091823560e01c8063012f257a146110a557806301ffc9a7146110505780631661190914610fb45780631baf72bf14610f795780631c364f7f14610f59578063248a9ca314610f305780632a6739a214610e5d5780632f2ff15d14610e3457806336568abe14610ded578063375f52e414610dcd5780633a2e845a14610d115780633c2df49914610cd657806345f7f24914610cb75780634a0bbabb14610c955780634fc6c75314610a985780635dd26add146109ff57806362a2a47c146109c457806371f3588d1461099e578063764c42301461088457806377003194146107e9578063773df151146107cc5780637adbf973146106dc5780637dc0d1d0146106b357806385b30e181461066d57806391d1485414610629578063a217fddf1461060e578063d547741f146105d0578063de1409ce146105a7578063e67736bf1461057e578063ed1f3e4c1461041d578063f3bddde1146103fe578063fab9300e1461024a578063fb5b3db7146102195763fdc9a0381461019757600080fd5b829134610215576020366003190112610215576101b2611632565b6007546001600160a01b031691823b156102105783926024849284519586938492630852cd8d60e31b84528035908401525af190811561020757506101f45750f35b6101fd90611285565b6102045780f35b80fd5b513d84823e3d90fd5b505050fd5b5050fd5b5050346102465760203660031901126102465760209061023f61023a611224565b611611565b9051908152f35b5080fd5b508290346102465761025b36611250565b610269959295939193611632565b6007546001600160a01b0396908716858816146103ee578681169661029b886000526004602052604060002054151590565b156103d7578697816008541660208751809263e00cb4a560e01b825281806102db8d8b8d84019092916020906040830194835260018060a01b0316910152565b03915afa9081156103cd57899161038f575b50906103246102fe61032d93611458565b916103138361030c8361182e565b905061144b565b818c526005602052898c20556118ab565b5060065461144b565b6006556008541690813b1561038b57845163627160f360e11b81526001600160a01b039182169481019485526020850193909352909416604083015292849184919082908490829060600103925af190811561020757506101f45750f35b8680fd5b919850506020813d6020116103c5575b816103ac602093836112af565b810103126103c057518897906103246102ed565b600080fd5b3d915061039f565b87513d8b823e3d90fd5b8451631c5620f360e31b8152808501899052602490fd5b8351633e1b3a6d60e01b81528390fd5b5050346102465781600319360112610246576020906002549051908152f35b503461057a578160031936011261057a57610436611224565b9160243591610443611632565b61044b611595565b6001600160a01b0394851660008181526004602052604090205490959015610563576020879161047e8761030c8a61182e565b8884526005835286842055610492886118ab565b5061049f8760065461144b565b60065560075416604486518094819363a9059cbb60e01b83528b898401528a60248401525af180156105595761052a575b506104d9611595565b90808203610511575050507f9b8105582b12481c6193ec3b651067ba5c410f1474d24704b0a67f89086da6149160209151908152a280f35b6044935192637614d04360e11b84528301526024820152fd5b61054b9060203d602011610552575b61054381836112af565b81019061157d565b50386104d0565b503d610539565b84513d89823e3d90fd5b8351631c5620f360e31b8152808401879052602490fd5b8280fd5b50503461024657816003193601126102465760085490516001600160a01b039091168152602090f35b50503461024657816003193601126102465760075490516001600160a01b039091168152602090f35b50903461057a578060031936011261057a5761060a913561060560016105f461123a565b9383875286602052862001546116e6565b6117b9565b5080f35b50503461024657816003193601126102465751908152602090f35b503461057a578160031936011261057a578160209361064661123a565b92358152808552209060018060a01b0316600052825260ff81600020541690519015158152f35b505034610246576020366003190112610246576020906106aa6001600160a01b03610696611224565b166000526004602052604060002054151590565b90519015158152f35b50503461024657816003193601126102465760015490516001600160a01b039091168152602090f35b503461057a57602036600319011261057a5780356001600160a01b03811692908390036107c85761070b6116ac565b8051638c89b64f60e01b8152906020828481875afa9182156107be57859261078a575b50600254809203610775575050600180546001600160a01b03191683179055507f3f32684a32a11dabdbb8c0177de80aa3ae36a004d75210335b49e544e48cd0aa8280a280f35b51639b6812b960e01b81529182015260249150fd5b9091506020813d6020116107b6575b816107a6602093836112af565b810103126103c05751903861072e565b3d9150610799565b81513d87823e3d90fd5b8380fd5b50503461024657816003193601126102465760209061023f611595565b50903461057a57602036600319011261057a57610804611224565b61080c6116ac565b6001600160a01b031660008181526004602052604090205490929061086f575060207f2017a03d63122e396be484633b7d9ed729e8fabe2e0587ccd5e42d8bfde8670091838552600582528481812055610865846118ab565b505160018152a280f35b82602492519163ad2a3c3160e01b8352820152fd5b503461057a578160031936011261057a5761089d611224565b91602435916108aa611632565b6108b2611595565b6001600160a01b039485169486906020906108cc8861182e565b90508781610979575b6108e291506006546112d1565b6006556007541660648651809481936323b872dd60e01b83528b898401523060248401528a60448401525af180156105595761095a575b50610922611595565b90808203610511575050507fc57e1ebcdc3ba533a9852552e139d8119d9599c48177705d00b4100a32c046d89160209151908152a280f35b6109729060203d6020116105525761054381836112af565b5038610919565b610982916112d1565b8884526005835286842055610996886118ab565b5038876108d5565b5050346102465760203660031901126102465760209061023f6109bf611224565b6114eb565b505034610246578160031936011261024657602090517f2dca0f5ce7e75a4b43fe2b0d6f5d0b7a2bf92ecf89f8f0aa17b8308b670388218152f35b5091903461024657816003193601126102465760035482805b828110610a29576020858551908152f35b610a328161170c565b506001600160a01b038116600090815260046020526040902054610a5a575b50600101610a18565b610a66909591956114eb565b90828282019283129112908015821691151617610a8557936001610a51565b634e487b7160e01b825260118652602482fd5b50919034610246576080366003190112610246576001600160a01b03918335838116919082900361020457610acb61123a565b60443594851685036103c057606435907f2dca0f5ce7e75a4b43fe2b0d6f5d0b7a2bf92ecf89f8f0aa17b8308b670388219687600052602097600089528660002033600052895260ff87600020541615610c785750855163e00cb4a560e01b81528181018481526001600160a01b03891660208201529097908990899081906040010381895afa978815610c6e578598610c3f575b50610b6a866114eb565b858113801590610c36575b610c1a5750853b15610c1657865163627160f360e11b81526001600160a01b0393841692810192835260208301949094529091166040820152829082908190606001038183875af18015610c0c57610bf8575b50507f124fd12bda4dcb813885ca782fb4ea9350d4f752b1a881cadf8742d8f128bb8e848351858152a251908152f35b610c028291611285565b6102045780610bc8565b84513d84823e3d90fd5b8480fd5b826044918a8a5192631d7ed6ef60e11b84528301526024820152fd5b50808913610b75565b9097508881813d8311610c67575b610c5781836112af565b810103126103c057519638610b60565b503d610c4d565b87513d87823e3d90fd5b6044925086519163e2517d3f60e01b835233908301526024820152fd5b83823461024657602036600319011261024657610cb06116ac565b3560025580f35b5050346102465781600319360112610246576020906006549051908152f35b505034610246578160031936011261024657602090517fc304524b36c64136ce5783ff1658be193c8352a90708830b9863880958c1fddd8152f35b8284346102045780600319360112610204579080519182906003549182855260208095018093600384527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b90845b818110610db95750505081610d759103826112af565b83519485948186019282875251809352850193925b828110610d9957505050500390f35b83516001600160a01b031685528695509381019392810192600101610d8a565b825484529288019260019283019201610d5f565b509134610204576020366003190112610204575061023f60209235611458565b50919034610246578060031936011261024657610e0861123a565b90336001600160a01b03831603610e25575061060a9192356117b9565b5163334bd91960e11b81528390fd5b50903461057a578060031936011261057a5761060a9135610e5860016105f461123a565b61173b565b50919034610246578160031936011261024657818093600354945b858110610e89576020858551908152f35b610e928161170c565b506001600160a01b0316600081815260046020526040902054610eb9575b50600101610e78565b849591955180916315339cd160e11b8252818560209485935afa918215610f26578592610ef6575b5050610eef9060019261144b565b9490610eb0565b90809250813d8311610f1f575b610f0d81836112af565b810103126103c0575181610eef610ee1565b503d610f03565b86513d87823e3d90fd5b503461057a57602036600319011261057a57816020936001923581528085522001549051908152f35b509134610204576020366003190112610204575061023f60209235611351565b505034610246578160031936011261024657602090517f0000000000000000000000000000000000000000000000000000000005f5e1008152f35b50903461057a57602036600319011261057a57610fcf611224565b610fd76116ac565b6001600160a01b03166000818152600460205260409020549092901561103b575060207f2017a03d63122e396be484633b7d9ed729e8fabe2e0587ccd5e42d8bfde8670091838552600582526000818620556110328461192d565b5051848152a280f35b826024925191631c5620f360e31b8352820152fd5b503461057a57602036600319011261057a57359063ffffffff60e01b821680920361057a5760209250637965db0b60e01b8214918215611094575b50519015158152f35b6301ffc9a760e01b1491503861108b565b50903461057a576110b536611250565b9190936110c0611632565b6007546001600160a01b039081168682161461121557600854855163e00cb4a560e01b81528084018681526001600160a01b0389166020828101919091528a9796959493909183918516908290819060400103915afa90811561120b57829087926111d1575b5061113361116892611458565b94169361114a816111438761182e565b90506112d1565b85885260056020528888205561115f856118ab565b506006546112d1565b6006556008541690823b15610c1657855163627160f360e11b81526001600160a01b03928316918101918252602082019490945295166040860152909384919082908490829060600103925af190811561020757506111c5575080f35b6111ce90611285565b80f35b9150506020813d602011611203575b816111ed602093836112af565b810103126111ff575181611133611126565b8580fd5b3d91506111e0565b87513d88823e3d90fd5b508351633e1b3a6d60e01b8152fd5b600435906001600160a01b03821682036103c057565b602435906001600160a01b03821682036103c057565b60609060031901126103c0576001600160a01b039060043582811681036103c0579160243590811681036103c0579060443590565b67ffffffffffffffff811161129957604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761129957604052565b919082039182116112de57565b634e487b7160e01b600052601160045260246000fd5b908160209103126103c0575160ff811681036103c05790565b818102929181159184041417156112de57565b60ff16604d81116112de57600a0a90565b811561133b570490565b634e487b7160e01b600052601260045260246000fd5b60075460405163313ce56760e01b81526020926001600160a01b03929091908316908481600481855afa93841561141057859160009561141c575b50600154169160246040518094819363b3596f0760e01b835260048301525afa938415611410576000946113dc575b50506113cd6113d9936113d39261130d565b91611320565b90611331565b90565b90809450813d8311611409575b6113f381836112af565b810103126103c0579151916113cd6113d36113bb565b503d6113e9565b6040513d6000823e3d90fd5b61143d919550823d8411611444575b61143581836112af565b8101906112f4565b933861138c565b503d61142b565b919082018092116112de57565b60075460405163313ce56760e01b81529190602090839060049082906001600160a01b03165afa9081156114105761149f6114a5926113d9946000916114cc575b50611320565b9061130d565b7f0000000000000000000000000000000000000000000000000000000005f5e10090611331565b6114e5915060203d6020116114445761143581836112af565b38611499565b6040516306a61f7560e51b8152906020826004816001600160a01b0385165afa91821561141057600092611547575b5061152761152c91611611565b611351565b90600082820392128183128116918313901516176112de5790565b90916020823d602011611575575b81611562602093836112af565b810103126102045750519061152761151a565b3d9150611555565b908160209103126103c0575180151581036103c05790565b6007546040516370a0823160e01b815230600482015290602090829060249082906001600160a01b03165afa8015611410576000906115dd575b6113d991506006549061144b565b506020813d602011611609575b816115f7602093836112af565b810103126103c0576113d990516115cf565b3d91506115ea565b611623906001600160a01b031661182e565b901561162c5790565b50600090565b3360009081527f3e202b319ce29fc3d407c763c9df930d4929e19b1d8f64d08ac659c98dd25ce360205260409020547fc304524b36c64136ce5783ff1658be193c8352a90708830b9863880958c1fddd9060ff161561168e5750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff161561168e5750565b80600052600060205260406000203360005260205260ff604060002054161561168e5750565b6117159061185e565b905460039190911b1c6000818152600560205260409020546001600160a01b0390911691565b9060009180835282602052604083209160018060a01b03169182845260205260ff604084205416156000146117b457808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054166000146117b45780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b6000526005602052604060002054801560001461185957506004602052604060002054151590600090565b600191565b6003548110156118955760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b634e487b7160e01b600052603260045260246000fd5b60008181526004602052604081205461192857600354680100000000000000008110156119145790826119006118e98460016040960160035561185e565b819391549060031b91821b91600019901b19161790565b905560035492815260046020522055600190565b634e487b7160e01b82526041600452602482fd5b905090565b600090808252600490816020526040832054801515600014611a1057600019908082018181116119fd57600354908382019182116119ea578181036119b7575b50505060035480156119a4578101906119858261185e565b909182549160031b1b1916905560035582526020526040812055600190565b634e487b7160e01b855260318452602485fd5b6119d56119c66118e99361185e565b90549060031b1c92839261185e565b9055855283602052604085205538808061196d565b634e487b7160e01b875260118652602487fd5b634e487b7160e01b865260118552602486fd5b5050509056fea2646970667358221220ed0cc9e4793e2f66363a17dcb405995b63abfe7b608c846ccc9e71ce65de574c64736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a0000000000000000000000007d3d464c37fa2d80407cc940490c6e4d763909bc000000000000000000000000fa7560956807d95dcef22990ddd92e38dbaf5cdd
-----Decoded View---------------
Arg [0] : _dusd (address): 0x788D96f655735f52c676A133f4dFC53cEC614d4A
Arg [1] : _collateralHolderVault (address): 0x7D3d464c37FA2D80407cc940490C6E4d763909Bc
Arg [2] : _oracle (address): 0xFA7560956807d95DCeF22990DdD92e38DbAf5cDd
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a
Arg [1] : 0000000000000000000000007d3d464c37fa2d80407cc940490c6e4d763909bc
Arg [2] : 000000000000000000000000fa7560956807d95dcef22990ddd92e38dbaf5cdd
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in FRAX
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.