Latest 9 from a total of 9 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Revoke Role | 13747496 | 410 days ago | IN | 0 FRAX | 0.00000116 | ||||
| Revoke Role | 13747486 | 410 days ago | IN | 0 FRAX | 0.00000127 | ||||
| Revoke Role | 13747476 | 410 days ago | IN | 0 FRAX | 0.00000129 | ||||
| Grant Role | 13747457 | 410 days ago | IN | 0 FRAX | 0.00000113 | ||||
| Grant Role | 13747447 | 410 days ago | IN | 0 FRAX | 0.00000127 | ||||
| Grant Role | 13747437 | 410 days ago | IN | 0 FRAX | 0.00000124 | ||||
| Grant Role | 13747427 | 410 days ago | IN | 0 FRAX | 0.00000124 | ||||
| Add Liquidity | 13029026 | 427 days ago | IN | 0 FRAX | 0.00000097 | ||||
| Allow Collateral | 13028988 | 427 days ago | IN | 0 FRAX | 0.00000066 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CurveStableSwapNGAmoVault
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/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts-5/token/ERC20/extensions/IERC20Metadata.sol";
import "contracts/dusd/AmoManager.sol";
import "contracts/dusd/AmoVault.sol";
import "contracts/curve/interfaces/ICurveRouterNgPoolsOnlyV1.sol";
import "contracts/curve/interfaces/IStableSwapNG.sol";
import "@openzeppelin/contracts-5/utils/structs/EnumerableSet.sol";
/**
* @title CurveStableSwapNGAmoVault
* @notice Implementation of AmoVault for Curve StableSwap NG pools
*/
contract CurveStableSwapNGAmoVault is AmoVault {
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
/* Core state */
ICurveRouterNgPoolsOnlyV1 public immutable router;
EnumerableSet.AddressSet private _lpTokens;
/* Roles */
bytes32 public constant AMO_TRADER_ROLE = keccak256("AMO_TRADER_ROLE");
/* Custom Errors */
error TokenNotAllowedAsCollateral(address token);
/**
* @notice Initializes the CurveStableSwapNGAmoVault
* @param _dusd Address of the DUSD token
* @param _amoManager Address of the AMO manager
* @param _oracle Address of the price oracle
* @param _router Address of the Curve Router NG
* @param _admin Address of the admin
* @param _collateralWithdrawer Address allowed to withdraw collateral
* @param _recoverer Address allowed to recover tokens
* @param _amoTrader Address allowed to perform AMO trading operations
*/
constructor(
address _dusd,
address _amoManager,
IPriceOracleGetter _oracle,
ICurveRouterNgPoolsOnlyV1 _router,
address _admin,
address _collateralWithdrawer,
address _recoverer,
address _amoTrader
)
AmoVault(
_dusd,
_amoManager,
_admin,
_collateralWithdrawer,
_recoverer,
_oracle
)
{
router = ICurveRouterNgPoolsOnlyV1(_router);
grantRole(AMO_TRADER_ROLE, _amoTrader);
}
/**
* @dev Calculates the total collateral value of the vault.
* @return The total collateral value in BASE_CURRENCY_UNIT.
*/
function totalCollateralValue() public view override returns (uint256) {
uint256 totalUsdValue = 0;
// Add the value of all collateral tokens
address[] memory collateralTokens = listCollateral();
for (uint256 i = 0; i < collateralTokens.length; i++) {
address collateralToken = collateralTokens[i];
uint256 collateralBalance = IERC20(collateralToken).balanceOf(
address(this)
);
totalUsdValue += _getTokenValue(collateralToken, collateralBalance);
}
// Add the collateral value from LP tokens
(uint256 lpCollateralValue, ) = _totalLpValues();
totalUsdValue += lpCollateralValue;
return totalUsdValue;
}
/**
* @notice Calculates the total value of dUSD in the vault
* @return The total value of dUSD in the vault, denominated in the base value (e.g., USD)
*/
function totalDusdValue() public view override returns (uint256) {
uint256 dusdUsdValue = 0;
// Get the value of dUSD in this contract
uint256 dusdBalance = dusd.balanceOf(address(this));
dusdUsdValue += _getTokenValue(address(dusd), dusdBalance);
// Get the value of dUSD in LP tokens
(, uint256 lpDusdValue) = _totalLpValues();
dusdUsdValue += lpDusdValue;
return dusdUsdValue;
}
/**
* @notice Calculates the total value of the vault
* @return The total value of the vault, denominated in the base value (e.g., USD)
*/
function totalValue() public view override returns (uint256) {
uint256 _totalValue = 0;
// Add the value of all collateral tokens
address[] memory collateralTokens = listCollateral();
for (uint256 i = 0; i < collateralTokens.length; i++) {
address collateralToken = collateralTokens[i];
uint256 collateralBalance = IERC20(collateralToken).balanceOf(
address(this)
);
_totalValue += _getTokenValue(collateralToken, collateralBalance);
}
// Add the value of dUSD
uint256 dusdBalance = dusd.balanceOf(address(this));
_totalValue += _getTokenValue(address(dusd), dusdBalance);
// Add the value of LP tokens
(uint256 lpCollateralValue, uint256 lpDusdValue) = _totalLpValues();
_totalValue += lpCollateralValue + lpDusdValue;
return _totalValue;
}
/**
* @dev Helper for allowing multiple collateral tokens.
* @param tokens An array of token addresses to be added as collateral.
*/
function allowCollaterals(
address[] calldata tokens
) external onlyRole(COLLATERAL_MANAGER_ROLE) {
for (uint256 i = 0; i < tokens.length; i++) {
super.allowCollateral(tokens[i]);
}
}
/**
* @dev Helper for disallowing multiple collateral tokens.
* @param tokens An array of token addresses to be removed from collateral.
*/
function disallowCollaterals(
address[] calldata tokens
) external onlyRole(COLLATERAL_MANAGER_ROLE) {
for (uint256 i = 0; i < tokens.length; i++) {
super.disallowCollateral(tokens[i]);
}
}
/* Curve Interaction */
/**
* @notice Executes a swap with exact input amount on Curve
* @param route Array of token addresses representing the swap path
* @param swapParams Array of swap parameters for each hop
* @param amountIn The exact amount of input tokens to swap
* @param minAmountOut The minimum amount of output tokens to receive
* @return uint256 The amount of output tokens received
*/
function swapExactIn(
address[11] calldata route,
uint256[4][5] calldata swapParams,
uint256 amountIn,
uint256 minAmountOut
) external onlyRole(AMO_TRADER_ROLE) returns (uint256) {
address tokenIn = route[0];
IERC20(tokenIn).approve(address(router), amountIn);
return
router.exchange(
route,
swapParams,
amountIn,
minAmountOut,
address(this)
);
}
/**
* @notice Gets the expected output amount for a swap with exact input
* @param route Array of token addresses representing the swap path
* @param swapParams Array of swap parameters for each hop
* @param amountIn The amount of input tokens
* @return uint256 The expected amount of output tokens
*/
function getExpectedOutput(
address[11] calldata route,
uint256[4][5] calldata swapParams,
uint256 amountIn
) external view returns (uint256) {
return router.get_dy(route, swapParams, amountIn);
}
/**
* @notice Gets the expected input amount for a desired output amount
* @param route Array of token addresses representing the swap path
* @param swapParams Array of swap parameters for each hop
* @param amountOut The desired amount of output tokens
* @return uint256 The expected amount of input tokens required
*/
function getExpectedInput(
address[11] calldata route,
uint256[4][5] calldata swapParams,
uint256 amountOut
) external view returns (uint256) {
return router.get_dx(route, swapParams, amountOut);
}
/**
* @dev Adds liquidity to a Curve pool.
* @param poolAddress The address of the Curve pool
* @param amounts The amounts of tokens to add as liquidity
* @param minMintAmount The minimum amount of LP tokens to receive
* @return The amount of LP tokens received
*/
function addLiquidity(
address poolAddress,
uint256[] calldata amounts,
uint256 minMintAmount
) external onlyRole(AMO_TRADER_ROLE) returns (uint256) {
ICurveStableSwapNG pool = ICurveStableSwapNG(poolAddress);
uint256 nCoins = pool.N_COINS();
// Approve tokens
for (uint256 i = 0; i < nCoins; i++) {
address token = pool.coins(i);
if (amounts[i] > 0) {
IERC20(token).approve(poolAddress, amounts[i]);
}
}
// Add liquidity to the pool
uint256 lpTokensReceived = pool.add_liquidity(amounts, minMintAmount);
// Update position tracking
// Note that add returns false if the value is already in the set, so it's safe to call it multiple times for the same pool
_lpTokens.add(poolAddress);
return lpTokensReceived;
}
/**
* @dev Removes liquidity from a Curve pool.
* @param poolAddress The address of the Curve pool
* @param lpTokenAmount The amount of LP tokens to burn
* @param minAmounts The minimum amounts of tokens to receive
* @return The amounts of tokens received
*/
function removeLiquidity(
address poolAddress,
uint256 lpTokenAmount,
uint256[] calldata minAmounts
) external onlyRole(AMO_TRADER_ROLE) returns (uint256[] memory) {
ICurveStableSwapNG pool = ICurveStableSwapNG(poolAddress);
// Approve LP tokens
IERC20(poolAddress).approve(poolAddress, lpTokenAmount);
// Remove liquidity from the pool
uint256[] memory receivedAmounts = pool.remove_liquidity(
lpTokenAmount,
minAmounts
);
// Update position tracking
if (IERC20(poolAddress).balanceOf(address(this)) == 0) {
_lpTokens.remove(poolAddress);
}
return receivedAmounts;
}
/**
* @dev Removes liquidity from a Curve pool in a single coin.
* @param poolAddress The address of the Curve pool
* @param lpTokenAmount The amount of LP tokens to burn
* @param i The index of the coin to receive
* @param minAmount The minimum amount of tokens to receive
* @return The amount of tokens received
*/
function removeLiquidityOneCoin(
address poolAddress,
uint256 lpTokenAmount,
int128 i,
uint256 minAmount
) external onlyRole(AMO_TRADER_ROLE) returns (uint256) {
ICurveStableSwapNG pool = ICurveStableSwapNG(poolAddress);
// Approve LP tokens
IERC20(poolAddress).approve(poolAddress, lpTokenAmount);
// Remove liquidity from the pool
uint256 receivedAmount = pool.remove_liquidity_one_coin(
lpTokenAmount,
i,
minAmount
);
// Update position tracking
if (IERC20(poolAddress).balanceOf(address(this)) == 0) {
_lpTokens.remove(poolAddress);
}
return receivedAmount;
}
/**
* @dev Removes liquidity from a Curve pool imbalanced.
* @param poolAddress The address of the Curve pool
* @param amounts The amounts of tokens to withdraw
* @param maxBurnAmount The maximum amount of LP tokens to burn
* @return The actual amount of LP tokens burned
*/
function removeLiquidityImbalance(
address poolAddress,
uint256[] calldata amounts,
uint256 maxBurnAmount
) external onlyRole(AMO_TRADER_ROLE) returns (uint256) {
ICurveStableSwapNG pool = ICurveStableSwapNG(poolAddress);
// Approve LP tokens
IERC20(poolAddress).approve(poolAddress, maxBurnAmount);
// Remove liquidity from the pool
uint256 burnedAmount = pool.remove_liquidity_imbalance(
amounts,
maxBurnAmount
);
// Update position tracking
if (IERC20(poolAddress).balanceOf(address(this)) == 0) {
_lpTokens.remove(poolAddress);
}
return burnedAmount;
}
/**
* @notice Returns the number of LP token positions.
* @return The number of LP tokens.
*/
function getLpTokenCount() public view returns (uint256) {
return _lpTokens.length();
}
/**
* @notice Checks if the given LP token is in the set.
* @param lpToken The LP token address to check.
* @return True if the LP token is in the set, false otherwise.
*/
function hasLpToken(address lpToken) public view returns (bool) {
return _lpTokens.contains(lpToken);
}
/**
* @notice Returns a list of all LP token addresses.
* @return An array of all LP token addresses.
*/
function getAllLpTokens() public view returns (address[] memory) {
return _lpTokens.values();
}
/**
* @dev Calculates the value of an LP excluding DUSD.
* @param lpToken The address of the LP token.
* @return collateralValue The value of the collateral in BASE_CURRENCY_UNIT
* @return dusdValue The value of the dUSD in BASE_CURRENCY_UNIT
*/
function _getLpValues(
address lpToken
) internal view returns (uint256 collateralValue, uint256 dusdValue) {
ICurveStableSwapNG pool = ICurveStableSwapNG(lpToken);
uint256 myLpBalance = IERC20(lpToken).balanceOf(address(this));
uint256 totalLpSupply = IERC20(lpToken).totalSupply();
uint256 nCoins = pool.N_COINS();
for (uint256 i = 0; i < nCoins; i++) {
address token = pool.coins(i);
uint256 poolTokenBalance = pool.balances(i);
uint256 myTokenShare = (poolTokenBalance * myLpBalance) /
totalLpSupply;
uint256 tokenValue = _getTokenValue(token, myTokenShare);
if (token == address(dusd)) {
dusdValue += tokenValue;
} else {
collateralValue += tokenValue;
}
}
return (collateralValue, dusdValue);
}
/**
* @dev Gets the value of a token in BASE_CURRENCY_UNIT
* @param token The address of the token
* @param amount The amount of tokens
* @return The value of the tokens in BASE_CURRENCY_UNIT
*/
function _getTokenValue(
address token,
uint256 amount
) internal view returns (uint256) {
uint256 price = oracle.getAssetPrice(token);
uint256 decimals = IERC20Metadata(token).decimals();
return (amount * price) / (10 ** decimals);
}
/**
* @dev Calculates the total values across all LP positions
* @return _totalCollateralValue The sum of all non-DUSD token values in BASE_CURRENCY_UNIT
* @return _totalDusdValue The sum of all DUSD values in BASE_CURRENCY_UNIT
*/
function _totalLpValues()
internal
view
returns (uint256 _totalCollateralValue, uint256 _totalDusdValue)
{
uint256 lpTokenCount = _lpTokens.length();
for (uint256 i = 0; i < lpTokenCount; i++) {
address lpToken = _lpTokens.at(i);
(uint256 collateralValue, uint256 dusdValue) = _getLpValues(
lpToken
);
_totalCollateralValue += collateralValue;
_totalDusdValue += dusdValue;
}
return (_totalCollateralValue, _totalDusdValue);
}
}// 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;
/**
* @dev Interface for Curve.Fi RouterNG contract (pools-only version 1).
* @dev Generated from original ABI: https://fraxscan.com/address/0x9f2Fa7709B30c75047980a0d70A106728f0Ef2db#code
*/
interface ICurveRouterNgPoolsOnlyV1 {
event Exchange(
address indexed sender,
address indexed receiver,
address[11] route,
uint256[4][5] swap_params,
uint256 in_amount,
uint256 out_amount
);
function exchange(
address[11] calldata _route,
uint256[4][5] calldata _swap_params,
uint256 _amount,
uint256 _min_dy
) external payable returns (uint256);
function exchange(
address[11] calldata _route,
uint256[4][5] calldata _swap_params,
uint256 _amount,
uint256 _min_dy,
address _receiver
) external payable returns (uint256);
function get_dy(
address[11] calldata _route,
uint256[4][5] calldata _swap_params,
uint256 _amount
) external view returns (uint256);
function get_dx(
address[11] calldata _route,
uint256[4][5] calldata _swap_params,
uint256 _out_amount
) external view returns (uint256);
function version() external view returns (string memory);
}// SPDX-License-Identifier: MIT
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— dtrinity.org ————————————————————————————————— *
* *
* ▲ *
* ▲ ▲ *
* *
* ———————————————————————————————————————————————————————————————————————————————— *
* dTRINITY Protocol: https://github.com/dtrinity *
* ———————————————————————————————————————————————————————————————————————————————— */
pragma solidity ^0.8.20;
/**
* @dev Interface for Curve.Fi StableSwapNG contract.
* @dev Generated October 2024 based https://etherscan.io/address/0x02950460e2b9529d0e00284a5fa2d7bdf3fa4d72#code
*/
interface ICurveStableSwapNG {
// Events
event Transfer(
address indexed sender,
address indexed receiver,
uint256 value
);
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
event TokenExchange(
address indexed buyer,
int128 sold_id,
uint256 tokens_sold,
int128 bought_id,
uint256 tokens_bought
);
event TokenExchangeUnderlying(
address indexed buyer,
int128 sold_id,
uint256 tokens_sold,
int128 bought_id,
uint256 tokens_bought
);
event AddLiquidity(
address indexed provider,
uint256[] token_amounts,
uint256[] fees,
uint256 invariant,
uint256 token_supply
);
event RemoveLiquidity(
address indexed provider,
uint256[] token_amounts,
uint256[] fees,
uint256 token_supply
);
event RemoveLiquidityOne(
address indexed provider,
int128 token_id,
uint256 token_amount,
uint256 coin_amount,
uint256 token_supply
);
event RemoveLiquidityImbalance(
address indexed provider,
uint256[] token_amounts,
uint256[] fees,
uint256 invariant,
uint256 token_supply
);
event RampA(
uint256 old_A,
uint256 new_A,
uint256 initial_time,
uint256 future_time
);
event StopRampA(uint256 A, uint256 t);
event ApplyNewFee(uint256 fee, uint256 offpeg_fee_multiplier);
// State Variables
function coins(uint256 i) external view returns (address);
function balances(uint256 i) external view returns (uint256);
function fee() external view returns (uint256);
function offpeg_fee_multiplier() external view returns (uint256);
function admin_fee() external view returns (uint256);
function initial_A() external view returns (uint256);
function future_A() external view returns (uint256);
function initial_A_time() external view returns (uint256);
function future_A_time() external view returns (uint256);
function admin_balances(uint256 i) external view returns (uint256);
function ma_exp_time() external view returns (uint256);
function D_ma_time() external view returns (uint256);
function ma_last_time() external view returns (uint256);
// Public functions
function A() external view returns (uint256);
function A_precise() external view returns (uint256);
function get_virtual_price() external view returns (uint256);
function calc_token_amount(
uint256[] calldata amounts,
bool is_deposit
) external view returns (uint256);
function add_liquidity(
uint256[] calldata amounts,
uint256 min_mint_amount
) external returns (uint256);
function add_liquidity(
uint256[] calldata amounts,
uint256 min_mint_amount,
address receiver
) external returns (uint256);
function get_dy(
int128 i,
int128 j,
uint256 dx
) external view returns (uint256);
function get_dx(
int128 i,
int128 j,
uint256 dy
) external view returns (uint256);
function exchange(
int128 i,
int128 j,
uint256 dx,
uint256 min_dy
) external returns (uint256);
function exchange(
int128 i,
int128 j,
uint256 dx,
uint256 min_dy,
address receiver
) external returns (uint256);
function exchange_received(
int128 i,
int128 j,
uint256 dx,
uint256 min_dy
) external returns (uint256);
function exchange_received(
int128 i,
int128 j,
uint256 dx,
uint256 min_dy,
address receiver
) external returns (uint256);
function remove_liquidity(
uint256 amount,
uint256[] calldata min_amounts
) external returns (uint256[] memory);
function remove_liquidity(
uint256 amount,
uint256[] calldata min_amounts,
address receiver
) external returns (uint256[] memory);
function remove_liquidity(
uint256 amount,
uint256[] calldata min_amounts,
address receiver,
bool claim_admin_fees
) external returns (uint256[] memory);
function remove_liquidity_imbalance(
uint256[] calldata amounts,
uint256 max_burn_amount
) external returns (uint256);
function remove_liquidity_imbalance(
uint256[] calldata amounts,
uint256 max_burn_amount,
address receiver
) external returns (uint256);
function calc_withdraw_one_coin(
uint256 token_amount,
int128 i
) external view returns (uint256);
function remove_liquidity_one_coin(
uint256 token_amount,
int128 i,
uint256 min_amount
) external returns (uint256);
function remove_liquidity_one_coin(
uint256 token_amount,
int128 i,
uint256 min_amount,
address receiver
) external returns (uint256);
// Admin functions
function ramp_A(uint256 future_A, uint256 future_time) external;
function stop_ramp_A() external;
function set_new_fee(
uint256 new_fee,
uint256 new_offpeg_fee_multiplier
) external;
function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;
function withdraw_admin_fees() external;
// Additional view functions
function get_balances() external view returns (uint256[] memory);
function last_price(uint256 i) external view returns (uint256);
function ema_price(uint256 i) external view returns (uint256);
function get_p(uint256 i) external view returns (uint256);
function price_oracle(uint256 i) external view returns (uint256);
function D_oracle() external view returns (uint256);
function dynamic_fee(int128 i, int128 j) external view returns (uint256);
function stored_rates() external view returns (uint256[] memory);
// ERC20 functions
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(
address owner,
address spender
) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external returns (bool);
// Additional view functions
function N_COINS() external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function nonces(address owner) external view returns (uint256);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function version() external view returns (string memory);
function salt() external view returns (bytes32);
}// 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
/* ———————————————————————————————————————————————————————————————————————————————— *
* _____ ______ ______ __ __ __ __ ______ __ __ *
* /\ __-. /\__ _\ /\ == \ /\ \ /\ "-.\ \ /\ \ /\__ _\ /\ \_\ \ *
* \ \ \/\ \ \/_/\ \/ \ \ __< \ \ \ \ \ \-. \ \ \ \ \/_/\ \/ \ \____ \ *
* \ \____- \ \_\ \ \_\ \_\ \ \_\ \ \_\\"\_\ \ \_\ \ \_\ \/\_____\ *
* \/____/ \/_/ \/_/ /_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ *
* *
* ————————————————————————————————— 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":"_amoManager","type":"address"},{"internalType":"contract IPriceOracleGetter","name":"_oracle","type":"address"},{"internalType":"contract ICurveRouterNgPoolsOnlyV1","name":"_router","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"address","name":"_collateralWithdrawer","type":"address"},{"internalType":"address","name":"_recoverer","type":"address"},{"internalType":"address","name":"_amoTrader","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":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"CannotRecoverVaultToken","type":"error"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"CollateralAlreadyAllowed","type":"error"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"CollateralNotSupported","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"FailedToAddCollateral","type":"error"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"FailedToRemoveCollateral","type":"error"},{"inputs":[{"internalType":"uint256","name":"baseCurrencyUnit","type":"uint256"}],"name":"IncorrectBaseCurrencyUnit","type":"error"},{"inputs":[],"name":"InvalidAmoManager","type":"error"},{"inputs":[],"name":"MustSupportAtLeastOneCollateral","type":"error"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"NoOracleSupport","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenNotAllowedAsCollateral","type":"error"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"UnsupportedCollateral","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collateralAsset","type":"address"}],"name":"CollateralAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collateralAsset","type":"address"}],"name":"CollateralDisallowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newOracle","type":"address"}],"name":"OracleSet","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_TRADER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COLLATERAL_MANAGER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COLLATERAL_STRATEGY_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COLLATERAL_WITHDRAWER_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":"RECOVERER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"minMintAmount","type":"uint256"}],"name":"addLiquidity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"allowCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"allowCollaterals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"amoManager","outputs":[{"internalType":"contract AmoManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"approveAmoManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"usdValue","type":"uint256"},{"internalType":"address","name":"asset","type":"address"}],"name":"assetAmountFromValue","outputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"},{"internalType":"address","name":"asset","type":"address"}],"name":"assetValueFromAmount","outputs":[{"internalType":"uint256","name":"usdValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseCurrencyUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"depositer","type":"address"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"depositFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"disallowCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"disallowCollaterals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dusd","outputs":[{"internalType":"contract IERC20Stablecoin","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dusdDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllLpTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[11]","name":"route","type":"address[11]"},{"internalType":"uint256[4][5]","name":"swapParams","type":"uint256[4][5]"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"getExpectedInput","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[11]","name":"route","type":"address[11]"},{"internalType":"uint256[4][5]","name":"swapParams","type":"uint256[4][5]"},{"internalType":"uint256","name":"amountIn","type":"uint256"}],"name":"getExpectedOutput","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLpTokenCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"address","name":"lpToken","type":"address"}],"name":"hasLpToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","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":"collateralAsset","type":"address"}],"name":"isCollateralSupported","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listCollateral","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IPriceOracleGetter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverETH","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"internalType":"uint256[]","name":"minAmounts","type":"uint256[]"}],"name":"removeLiquidity","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint256","name":"maxBurnAmount","type":"uint256"}],"name":"removeLiquidityImbalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"poolAddress","type":"address"},{"internalType":"uint256","name":"lpTokenAmount","type":"uint256"},{"internalType":"int128","name":"i","type":"int128"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"name":"removeLiquidityOneCoin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","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":[],"name":"router","outputs":[{"internalType":"contract ICurveRouterNgPoolsOnlyV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_newAmoManager","type":"address"}],"name":"setAmoManager","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":[{"internalType":"address[11]","name":"route","type":"address[11]"},{"internalType":"uint256[4][5]","name":"swapParams","type":"uint256[4][5]"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"minAmountOut","type":"uint256"}],"name":"swapExactIn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalCollateralValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDusdValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalValue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"address","name":"collateralAsset","type":"address"}],"name":"withdrawTo","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60e0604081815234620003f857610100826200361e8038038091620000258285620003fd565b833981010312620003f8576200003b8262000437565b60206200004a81850162000437565b838501519094906001600160a01b039081811690819003620003f8576060830151928284168403620003f857620000846080820162000437565b956200009360a0830162000437565b90620000b060e0620000a860c0860162000437565b940162000437565b600180546001600160a01b031990811690961790556305f5e10060025597620000d9336200044c565b50620000e5336200044c565b506000809b6000805160206200359e8339815191528252818a526200011060018d842001546200079e565b6200011b33620004cc565b5060048a8d8a600080516020620035be8339815191529788875286845262000149600184892001546200079e565b62000154336200055d565b50600080516020620035fe83398151915287528684526200017b600184892001546200079e565b6200018633620005e9565b506001600555169081608052519283809263313ce56760e01b82525afa908115620003ee5789939162000399575b5096620001db92620001f896959492620002259960a052169060065416176006556200044c565b508a52898752620001f260018a8c2001546200079e565b6200055d565b50600080516020620035de83398151915288528785526200021f6001888a2001546200079e565b62000675565b50858052858352848620338752835260ff8587205416156200037b57608051600654865163095ea7b360e01b815290831660048201526000196024820152918491839160449183918b91165af1801562000371576200032f575b508385620002c69596620002c09460019460c0527fc9d160ddf8721202a3f3e2bd663b1fdb5675930469632a500038010be1a191dc8252522001546200079e565b62000701565b5051612d9a9081620007e482396080518181816108270152818161096b01528181610c9301528181610f1f015281816112d601528181611b09015261262d015260a05181611462015260c0518181816102950152818161079b01528181611cfe0152611d9f0152f35b8281813d831162000369575b620003478183620003fd565b81010312620003655751801515036200036157386200027f565b8480fd5b8580fd5b503d6200033b565b85513d88823e3d90fd5b845163e2517d3f60e01b815233600482015260248101879052604490fd5b809293508b809250969594963d8311620003e6575b620003ba8183620003fd565b81010312620003e257519360ff85168503620003e2579293919291879190620001db620001b4565b8c80fd5b503d620003ae565b8d513d85823e3d90fd5b600080fd5b601f909101601f19168101906001600160401b038211908210176200042157604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620003f857565b6001600160a01b031660008181527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205490919060ff16620004c857818052816020526040822081835260205260408220600160ff1982541617905533916000805160206200357e8339815191528180a4600190565b5090565b6001600160a01b031660008181527fb56095460044281636dd3a77e227972b73971c7d766b38feb76e4e7f12e8c60260205260408120549091906000805160206200359e8339815191529060ff166200055857808352826020526040832082845260205260408320600160ff198254161790556000805160206200357e833981519152339380a4600190565b505090565b6001600160a01b031660008181527fc191fc48a308d795605d8380942284aa535eb60ab59dded9d8c4eff77cf3c8726020526040812054909190600080516020620035be8339815191529060ff166200055857808352826020526040832082845260205260408320600160ff198254161790556000805160206200357e833981519152339380a4600190565b6001600160a01b031660008181527f94449471841a7f40a7871d590913647b573afdadc8e3b552848558acac45c28a6020526040812054909190600080516020620035fe8339815191529060ff166200055857808352826020526040832082845260205260408320600160ff198254161790556000805160206200357e833981519152339380a4600190565b6001600160a01b031660008181527fa921dec465a2db617c1283eb3fd0c7be03ef4a04bbcfeba0659a6baa62f900016020526040812054909190600080516020620035de8339815191529060ff166200055857808352826020526040832082845260205260408320600160ff198254161790556000805160206200357e833981519152339380a4600190565b6001600160a01b031660008181527f35981844e2f7014ed33b54358b929d550bdab17164e935e079ba150ceda75ff860205260408120549091907fc9d160ddf8721202a3f3e2bd663b1fdb5675930469632a500038010be1a191dc9060ff166200055857808352826020526040832082845260205260408320600160ff198254161790556000805160206200357e833981519152339380a4600190565b80600052600060205260406000203360005260205260ff6040600020541615620007c55750565b6044906040519063e2517d3f60e01b82523360048301526024820152fdfe6080604052600436101561001257600080fd5b60003560e01c8062f714ce14611e3f57806301fd155d14611ca857806301ffc9a714611c525780630ee209c114611b915780631171bda914611ac2578063132c29b214611a875780631ee903b6146119c757806321fccb48146117ac578063248a9ca31461177d5780632a6739a2146116af5780632e718ab7146116745780632f2ff15d1461163557806336568abe146115ee5780633e0c0629146115745780633f8634811461155d57806345daa27b146115225780634a0bbabb146115015780634f5f32991461148657806352816b68146114485780635433a7661461140c5780635c23ef6e1461131c5780635c5088e4146112855780636e553f65146112605780637adbf9731461117e5780637dc0d1d01461115557806391d14854146111085780639377e63a146110025780639d4cc8d314610ee9578063a217fddf14610ecd578063a4e2a31e14610dbd578063acf1c94814610d82578063bea151fb14610d52578063c4e2c1e614610d28578063c7bee63d14610c6d578063c83abb8514610a2a578063cf07456f146109fe578063d4c3eea014610897578063d547741f14610856578063de1409ce14610811578063de5eaee614610767578063de777a7f146105f2578063e00cb4a5146104dc578063e1121672146104a1578063eb0bc1ec14610483578063f20c333c1461030b578063f3bddde1146102ed578063f817f7d0146102c4578063f887ea401461027f5763fa6bd2ee1461023657600080fd5b3461027a57602036600319011261027a5760206102706001600160a01b0361025c611e8d565b166000526004602052604060002054151590565b6040519015158152f35b600080fd5b3461027a57600036600319011261027a576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461027a57600036600319011261027a576006546040516001600160a01b039091168152602090f35b3461027a57600036600319011261027a576020600254604051908152f35b3461027a5760208060031936011261027a5760043567ffffffffffffffff811161027a5761033d903690600401611eb7565b90610346612267565b60005b82811061035257005b610365610360828585612021565b611fd3565b61036d612267565b6001600160a01b0390811660008181526004602052604090205490919061046a578590600154166040519182809263b3596f0760e01b825285600483015260249485915afa90811561045e57600091610431575b501561041b576103d082612c58565b156104055750906001917f500f8acd525a3d9f96ab641587f59e34ef9d02f9397fdd46bb7786273bad1607600080a201610349565b906040519063cdb5999560e01b82526004820152fd5b9060405190631066c96360e31b82526004820152fd5b90508681813d8311610457575b6104488183611fe7565b8101031261027a5751876103c1565b503d61043e565b6040513d6000823e3d90fd5b60405163098f893f60e21b815260048101839052602490fd5b3461027a57600036600319011261027a576020600754604051908152f35b3461027a57600036600319011261027a5760206040517fc9d160ddf8721202a3f3e2bd663b1fdb5675930469632a500038010be1a191dc8152f35b3461027a57604036600319011261027a576104f5611e77565b60015460405163b3596f0760e01b81526001600160a01b039283166004820181905260209391929091849184916024918391165afa90811561045e5783906000926105c2575b60405163313ce56760e01b81529350839060049082905afa90811561045e576105839260009261058b575b5061057761057d91600435906120eb565b916120da565b906120fe565b604051908152f35b61057d9192506105b361057791863d88116105bb575b6105ab8183611fe7565b8101906120c1565b929150610566565b503d6105a1565b915082813d83116105eb575b6105d88183611fe7565b8101031261027a5782600492519161053b565b503d6105ce565b3461027a5761060036611ee8565b80939261060b61220b565b60405163095ea7b360e01b81526001600160a01b03949094166004850181905260248501959095526020949285856044816000885af192831561045e5761067795879461073a575b506040519586938493637706db7560e01b8552604060048601526044850191612066565b90602483015203816000855af191821561045e5760009261070b575b506040516370a0823160e01b81523060048201528381602481855afa90811561045e576000916106de575b50156106ce575b50604051908152f35b6106d790612b0d565b50826106c5565b90508381813d8311610704575b6106f58183611fe7565b8101031261027a5751846106be565b503d6106eb565b9091508281813d8311610733575b6107238183611fe7565b8101031261027a57519083610693565b503d610719565b61075990853d8711610760575b6107518183611fe7565b810190612009565b5087610653565b503d610747565b3461027a57602061077736611fa9565b929161079760405194859384936308f109c360e11b85526004850161211e565b03817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa801561045e576000906107de575b602090604051908152f35b506020813d602011610809575b816107f860209383611fe7565b8101031261027a57602090516107d3565b3d91506107eb565b3461027a57600036600319011261027a576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b3461027a57604036600319011261027a57610895600435610875611e77565b90806000526000602052610890600160406000200154612359565b6127f5565b005b3461027a57600036600319011261027a5760006108b2612ca5565b6000915b8151831015610956576001600160a01b036108d1848461208a565b5116604051906370a0823160e01b82523060048301526020918281602481855afa92831561045e57600093610921575b505091610913610919926001946123c2565b9061209e565b9201916108b6565b908094929350813d831161094f575b61093a8183611fe7565b8101031261027a579151906109136001610901565b503d610930565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602082602481845afa91821561045e576000926109c9575b60206105836109be8661091387876123c2565b6109136109136124a1565b91506020823d6020116109f6575b816109e460209383611fe7565b8101031261027a5790519060206109ab565b3d91506109d7565b3461027a57600036600319011261027a57610a26610a1a612ca5565b60405191829182611f64565b0390f35b3461027a57606036600319011261027a57610a43611e8d565b6024359067ffffffffffffffff60443581811161027a57610a68903690600401611eb7565b919092610a7361220b565b60405163095ea7b360e01b81526001600160a01b0391909116600482018190526024820186905260209590939086836044816000895af192831561045e57600093610c50575b50610ae5604051968793849363350376e360e21b85526004850152604060248501526044840191612066565b038183865af192831561045e57600093610bae575b50506040516370a0823160e01b81523060048201528381602481855afa90811561045e57600091610b81575b5015610b71575b50906040519181839283018184528251809152816040850193019160005b828110610b5a57505050500390f35b835185528695509381019392810192600101610b4b565b610b7a90612b0d565b5082610b2d565b90508381813d8311610ba7575b610b988183611fe7565b8101031261027a575184610b26565b503d610b8e565b909192503d806000833e610bc28183611fe7565b810190848183031261027a5780519083821161027a570181601f8201121561027a578051928311610c3a578260051b60405193610c0187830186611fe7565b8452858085019183010192831161027a5785809201905b838210610c2b5750505050908380610afa565b81518152908201908201610c18565b634e487b7160e01b600052604160045260246000fd5b610c6690883d8a11610760576107518183611fe7565b5087610ab9565b3461027a57600036600319011261027a576040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316602082602481845afa91821561045e57600092610cf2575b6020610583610ce285856123c2565b610cea6124a1565b91905061209e565b91506020823d602011610d20575b81610d0d60209383611fe7565b8101031261027a57905190610ce2610cd3565b3d9150610d00565b3461027a57610895610d3936611f30565b610d44929192612191565b6001600160a01b031661237f565b3461027a576020610d6236611fa9565b92916107976040519485938493637b5e2c7b60e01b85526004850161211e565b3461027a57600036600319011261027a5760206040517fb3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc8152f35b3461027a57602036600319011261027a57610dd6611e8d565b610dde612267565b6001600160a01b0390811660008181526004602052604090205490919061046a576020602491600154166040519283809263b3596f0760e01b82528660048301525afa90811561045e57600091610e9b575b5015610e8357610e3f81612c58565b15610e6b577f500f8acd525a3d9f96ab641587f59e34ef9d02f9397fdd46bb7786273bad1607600080a2005b6024906040519063cdb5999560e01b82526004820152fd5b60249060405190631066c96360e31b82526004820152fd5b90506020813d602011610ec5575b81610eb660209383611fe7565b8101031261027a575182610e30565b3d9150610ea9565b3461027a57600036600319011261027a57602060405160008152f35b3461027a5760208060031936011261027a57610f03611e8d565b610f0b61231f565b6001600160a01b039081168015610ff057817f0000000000000000000000000000000000000000000000000000000000000000169160065416906040519163095ea7b360e01b9081845260048401526000602484015284836044816000885af190811561045e578593604492610fd3575b50600680546001600160a01b03191684179055610f9761231f565b6000604051958694859384526004840152811960248401525af1801561045e57610fbd57005b8161089592903d10610760576107518183611fe7565b610fe990853d8711610760576107518183611fe7565b5086610f7c565b6040516314e632d960e11b8152600490fd5b3461027a57608036600319011261027a5761101b611e8d565b602435906044359081600f0b80920361027a57829161103861220b565b60405163095ea7b360e01b81526001600160a01b03929092166004830181905260248301949094526020939184816044816000875af1801561045e576110eb575b5060405192630d2680e960e11b845260048401526024830152606435604483015282826064816000855af191821561045e5760009261070b57506040516370a0823160e01b81523060048201528381602481855afa90811561045e576000916106de5750156106ce5750604051908152f35b61110190853d8711610760576107518183611fe7565b5084611079565b3461027a57604036600319011261027a57611121611e77565b600435600052600060205260406000209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b3461027a57600036600319011261027a576001546040516001600160a01b039091168152602090f35b3461027a57602036600319011261027a576004356001600160a01b0381169081900361027a576111ac61231f565b604051638c89b64f60e01b8152602081600481855afa90811561045e5760009161122e575b506002548091036112165750600180546001600160a01b031916821790557f3f32684a32a11dabdbb8c0177de80aa3ae36a004d75210335b49e544e48cd0aa600080a2005b60249060405190639b6812b960e01b82526004820152fd5b90506020813d602011611258575b8161124960209383611fe7565b8101031261027a5751826111d1565b3d915061123c565b3461027a57604036600319011261027a5761089561127c611e77565b600435336128aa565b3461027a57600036600319011261027a5761129e61231f565b60065460405163095ea7b360e01b81526001600160a01b039182166004820152600019602482015290602090829060449082906000907f0000000000000000000000000000000000000000000000000000000000000000165af1801561045e5761130457005b6108959060203d602011610760576107518183611fe7565b3461027a57604036600319011261027a57611335611e77565b60015460405163b3596f0760e01b81526001600160a01b039283166004820181905260209391929091849184916024918391165afa90811561045e5783906000926113dc575b60405163313ce56760e01b81529350839060049082905afa801561045e576113b26113ba91610583946000916113bf575b506120da565b6004356120eb565b6120fe565b6113d69150863d88116105bb576105ab8183611fe7565b866113ac565b915082813d8311611405575b6113f28183611fe7565b8101031261027a5782600492519161137b565b503d6113e8565b3461027a57602036600319011261027a576001600160a01b0361142d611e8d565b16600052600860205260206040600020541515604051908152f35b3461027a57600036600319011261027a57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461027a57600036600319011261027a57604051806007549182815260208091019260076000527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688916000905b8282106114ea57610a2685610a1a81890382611fe7565b8354865294850194600193840193909101906114d3565b3461027a57602036600319011261027a5761151a61231f565b600435600255005b3461027a57600036600319011261027a5760206040517f1f29e81ed8f7ae439f042f6b6767d105e87c4eef908508d5d9e550aef35af6448152f35b3461027a5761089561156e36611f30565b916128aa565b3461027a57604036600319011261027a5761158d611e8d565b602435906115996122c3565b8147106115d6576000918291829182916001600160a01b03165af16115bc61286a565b50156115c457005b604051630a12f52160e11b8152600490fd5b60405163cd78605960e01b8152306004820152602490fd5b3461027a57604036600319011261027a57611607611e77565b336001600160a01b0382160361162357610895906004356127f5565b60405163334bd91960e11b8152600490fd5b3461027a57604036600319011261027a57610895600435611654611e77565b9080600052600060205261166f600160406000200154612359565b612777565b3461027a57600036600319011261027a5760206040517f85e8f2d6819d6b24108062d87ea08f54651bcb8960d98062d3faf96e7873b8b98152f35b3461027a57600036600319011261027a5760006116ca612ca5565b6000915b8151831015611768576001600160a01b036116e9848461208a565b5116604051906370a0823160e01b82523060048301526020918281602481855afa92831561045e57600093611733575b50509161091361172b926001946123c2565b9201916116ce565b908094929350813d8311611761575b61174c8183611fe7565b8101031261027a579151906109136001611719565b503d611742565b6105836020916117766124a1565b509061209e565b3461027a57602036600319011261027a5760043560005260006020526020600160406000200154604051908152f35b3461027a576117ba36611ee8565b91906117c461220b565b604051630293577560e41b81526020946001600160a01b038181169592909187826004818a5afa91821561045e57600092611998575b5095949290918794926000975b8189106118905750505061183995506040519586938493635b96faef60e11b8552604060048601526044850191612066565b90602483015203816000855af191821561045e5760009261185f575b506106c590612bf2565b9091508281813d8311611889575b6118778183611fe7565b8101031261027a5751906106c5611855565b503d61186d565b91939550919395966040519063c661065760e01b825280600483015289826024818c5afa91821561045e5785928b9160009161196b575b506118d3838a8c612021565b356118ed575b505060019150019695939188959391611807565b611930906118fc848b8d612021565b60405163095ea7b360e01b81526001600160a01b038816600482015290356024820152948592839160009183906044820190565b0393165af191821561045e5760019261194e575b85928b91506118d9565b611964908b3d8d11610760576107518183611fe7565b508a611944565b61198b9150823d8411611991575b6119838183611fe7565b810190612047565b8c6118c7565b503d611979565b9091508781813d83116119c0575b6119b08183611fe7565b8101031261027a575190886117fa565b503d6119a6565b3461027a57602036600319011261027a576119e0611e8d565b6119e8612267565b6001600160a01b031660008181526004602052604090205415611a6f5760016003541115611a5d57611a1981612a0d565b15611a45577fcebbf63022189259f517d89d98c7c527b44c211d25e443dad13aab2479c7e7b3600080a2005b6024906040519063644e3dd760e11b82526004820152fd5b6040516305bc742560e11b8152600490fd5b60249060405190632108722b60e01b82526004820152fd5b3461027a57600036600319011261027a5760206040517f1a52e20da533a06a1f80a73dba6e5d09cb788f108eae685b8ce6644834e67abe8152f35b3461027a57606036600319011261027a57611adb611e8d565b611ae3611e77565b90611aec6122c3565b600260055414611b7f5760026005556001600160a01b03908116907f00000000000000000000000000000000000000000000000000000000000000001681148015611b62575b611b4a57611b43916044359161237f565b6001600555005b60249060405190638415be8960e01b82526004820152fd5b50611b7a816000526004602052604060002054151590565b611b32565b604051633ee5aeb560e01b8152600490fd5b3461027a57602036600319011261027a5760043567ffffffffffffffff811161027a57611bc2903690600401611eb7565b90611bcb612267565b60005b828110611bd757005b611be5610360828585612021565b611bed612267565b6001600160a01b031660008181526004602052604090205415611a6f5760016003541115611a5d57611c1e81612a0d565b15611a4557906001917fcebbf63022189259f517d89d98c7c527b44c211d25e443dad13aab2479c7e7b3600080a201611bce565b3461027a57602036600319011261027a5760043563ffffffff60e01b811680910361027a57602090637965db0b60e01b8114908115611c97575b506040519015158152f35b6301ffc9a760e01b14905082611c8c565b3461027a5761042036600319011261027a57366101641161027a57366103e41161027a57611cd461220b565b6004356001600160a01b038116810361027a5760405163095ea7b360e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301526103e4356024830152909160209183916044918391600091165af1801561045e57611e20575b50604051633f51256760e21b8152600460008183015b600b8210611df8578360006101648083015b60058310611de1576103e480359085015261040480359085015230610424850152836020816104448160007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af1801561045e576000906107de57602090604051908152f35b600190608081818581943701920192019190611d72565b60019060209081906001600160a01b03611e1187611ea3565b16815201930191019091611d60565b611e389060203d602011610760576107518183611fe7565b5080611d4a565b3461027a57604036600319011261027a57610895611e5b611e77565b611e63612191565b6004359033906001600160a01b031661237f565b602435906001600160a01b038216820361027a57565b600435906001600160a01b038216820361027a57565b35906001600160a01b038216820361027a57565b9181601f8401121561027a5782359167ffffffffffffffff831161027a576020808501948460051b01011161027a57565b90606060031983011261027a576004356001600160a01b038116810361027a57916024359067ffffffffffffffff821161027a57611f2891600401611eb7565b909160443590565b606090600319011261027a576001600160a01b03600435818116810361027a579160243591604435908116810361027a5790565b602090602060408183019282815285518094520193019160005b828110611f8c575050505090565b83516001600160a01b031685529381019392810192600101611f7e565b61040060031982011261027a576101649181831161027a576004926103e492831161027a57913590565b356001600160a01b038116810361027a5790565b90601f8019910116810190811067ffffffffffffffff821117610c3a57604052565b9081602091031261027a5751801515810361027a5790565b91908110156120315760051b0190565b634e487b7160e01b600052603260045260246000fd5b9081602091031261027a57516001600160a01b038116810361027a5790565b81835290916001600160fb1b03831161027a5760209260051b809284830137010190565b80518210156120315760209160051b010190565b919082018092116120ab57565b634e487b7160e01b600052601160045260246000fd5b9081602091031261027a575160ff8116810361027a5790565b60ff16604d81116120ab57600a0a90565b818102929181159184041417156120ab57565b8115612108570490565b634e487b7160e01b600052601260045260246000fd5b61040081019493929091600090835b600b831061216b575050506101608201906000915b60058310612154575050506103e00152565b600190608081818581943701920192019190612142565b6001906001600160a01b0361217f84611ea3565b1681529201916020918201910161212d565b3360009081527fc191fc48a308d795605d8380942284aa535eb60ab59dded9d8c4eff77cf3c87260205260409020547f1f29e81ed8f7ae439f042f6b6767d105e87c4eef908508d5d9e550aef35af6449060ff16156121ed5750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b3360009081527f35981844e2f7014ed33b54358b929d550bdab17164e935e079ba150ceda75ff860205260409020547fc9d160ddf8721202a3f3e2bd663b1fdb5675930469632a500038010be1a191dc9060ff16156121ed5750565b3360009081527fb56095460044281636dd3a77e227972b73971c7d766b38feb76e4e7f12e8c60260205260409020547f85e8f2d6819d6b24108062d87ea08f54651bcb8960d98062d3faf96e7873b8b99060ff16156121ed5750565b3360009081527fa921dec465a2db617c1283eb3fd0c7be03ef4a04bbcfeba0659a6baa62f9000160205260409020547fb3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc9060ff16156121ed5750565b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff16156121ed5750565b80600052600060205260406000203360005260205260ff60406000205416156121ed5750565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526123c0916123bb606483611fe7565b61292d565b565b60015460405163b3596f0760e01b81526001600160a01b0392831660048201819052929091602091829184916024918391165afa91821561045e57600092612470575b50806004936040519485809263313ce56760e01b82525afa801561045e5761243b9360ff92600092612453575b505016926120eb565b604d82116120ab5761245091600a0a906120fe565b90565b6124699250803d106105bb576105ab8183611fe7565b3880612432565b9291508083813d831161249a575b6124888183611fe7565b8101031261027a579151909180612405565b503d61247e565b6000906000916007546000905b8082106124bb5750509190565b600760009081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6888301546040516370a0823160e01b815230600482015293969492939192839290916001600160a01b0316602082602481845afa91821561045e57600092612743575b506040516318160ddd60e01b815290602082600481845afa91821561045e5760009261270f575b50604051630293577560e41b815290602082600481845afa91821561045e576000926126db575b509291906000935b8185106125a55750505050509161259761259d9260019461209e565b9461209e565b9401906124ae565b90919293966040519063c661065760e01b8252886004830152602082602481865afa91821561045e576000926126ba575b50604051634903b0d160e01b8152600481018a9052602081602481875afa90811561045e5786918891600091612680575b50916113ba6126199261261f946120eb565b836123c2565b916001600160a01b039081167f00000000000000000000000000000000000000000000000000000000000000009091160361266c576001916126609161209e565b975b019392919061257b565b979660019161267a9161209e565b96612662565b925050506020813d6020116126b2575b8161269d60209383611fe7565b8101031261027a5751859087906113ba612607565b3d9150612690565b6126d491925060203d602011611991576119838183611fe7565b90386125d6565b9091506020813d602011612707575b816126f760209383611fe7565b8101031261027a57519038612573565b3d91506126ea565b9091506020813d60201161273b575b8161272b60209383611fe7565b8101031261027a5751903861254c565b3d915061271e565b9091506020813d60201161276f575b8161275f60209383611fe7565b8101031261027a57519038612525565b3d9150612752565b9060009180835282602052604083209160018060a01b03169182845260205260ff604084205416156000146127f057808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054166000146127f05780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b3d156128a5573d9067ffffffffffffffff8211610c3a5760405191612899601f8201601f191660200184611fe7565b82523d6000602084013e565b606090565b9060018060a01b03809316928360005260046020526040600020541561291457604051926323b872dd60e01b602085015216602483015230604483015260648201526064815260a081019181831067ffffffffffffffff841117610c3a576123c09260405261292d565b604051632762993f60e11b815260048101859052602490fd5b6000806129569260018060a01b03169360208151910182865af161294f61286a565b9083612d13565b8051908115159182612984575b505061296c5750565b60249060405190635274afe760e01b82526004820152fd5b6129979250602080918301019101612009565b153880612963565b6003548110156120315760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b6007548110156120315760076000527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880190600090565b600090808252600490816020526040832054801515600014612b075760001990808201818111612af45760035490838201918211612ae157818103612a97575b5050506003548015612a8457810190612a658261299f565b909182549160031b1b1916905560035582526020526040812055600190565b634e487b7160e01b855260318452602485fd5b612acc612aa6612ab59361299f565b90549060031b1c92839261299f565b819391549060031b91821b91600019901b19161790565b90558552836020526040852055388080612a4d565b634e487b7160e01b875260118652602487fd5b634e487b7160e01b865260118552602486fd5b50505090565b60008181526008602052604081205490919080156127f05760001990808201818111612bde5760075490838201918211612bca57818103612b96575b5050506007548015612b8257810190612b61826129d6565b909182549160031b1b19169055600755815260086020526040812055600190565b634e487b7160e01b84526031600452602484fd5b612bb4612ba5612ab5936129d6565b90549060031b1c9283926129d6565b9055845260086020526040842055388080612b49565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b85526011600452602485fd5b600081815260086020526040812054612c5357600754600160401b811015612c3f579082612c2b612ab5846001604096016007556129d6565b905560075492815260086020522055600190565b634e487b7160e01b82526041600452602482fd5b905090565b600081815260046020526040812054612c5357600354600160401b811015612c3f579082612c91612ab58460016040960160035561299f565b905560035492815260046020522055600190565b6040519060035480835282602091602082019060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b936000905b828210612cf9575050506123c092500383611fe7565b855484526001958601958895509381019390910190612ce3565b90612d2857508051156115c457805190602001fd5b81511580612d5b575b612d39575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15612d3156fea2646970667358221220d1cc63bdbff580c47b2cd30033e73fdd4c5b031e4074b0fb6720674db015d07b64736f6c634300081800332f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d85e8f2d6819d6b24108062d87ea08f54651bcb8960d98062d3faf96e7873b8b91f29e81ed8f7ae439f042f6b6767d105e87c4eef908508d5d9e550aef35af644b3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc1a52e20da533a06a1f80a73dba6e5d09cb788f108eae685b8ce6644834e67abe000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a000000000000000000000000d9ba545656cba566c042f96634749242a3bf76c7000000000000000000000000fa7560956807d95dcef22990ddd92e38dbaf5cdd0000000000000000000000009f2fa7709b30c75047980a0d70a106728f0ef2db0000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b30220000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b30220000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b30220000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b3022
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c8062f714ce14611e3f57806301fd155d14611ca857806301ffc9a714611c525780630ee209c114611b915780631171bda914611ac2578063132c29b214611a875780631ee903b6146119c757806321fccb48146117ac578063248a9ca31461177d5780632a6739a2146116af5780632e718ab7146116745780632f2ff15d1461163557806336568abe146115ee5780633e0c0629146115745780633f8634811461155d57806345daa27b146115225780634a0bbabb146115015780634f5f32991461148657806352816b68146114485780635433a7661461140c5780635c23ef6e1461131c5780635c5088e4146112855780636e553f65146112605780637adbf9731461117e5780637dc0d1d01461115557806391d14854146111085780639377e63a146110025780639d4cc8d314610ee9578063a217fddf14610ecd578063a4e2a31e14610dbd578063acf1c94814610d82578063bea151fb14610d52578063c4e2c1e614610d28578063c7bee63d14610c6d578063c83abb8514610a2a578063cf07456f146109fe578063d4c3eea014610897578063d547741f14610856578063de1409ce14610811578063de5eaee614610767578063de777a7f146105f2578063e00cb4a5146104dc578063e1121672146104a1578063eb0bc1ec14610483578063f20c333c1461030b578063f3bddde1146102ed578063f817f7d0146102c4578063f887ea401461027f5763fa6bd2ee1461023657600080fd5b3461027a57602036600319011261027a5760206102706001600160a01b0361025c611e8d565b166000526004602052604060002054151590565b6040519015158152f35b600080fd5b3461027a57600036600319011261027a576040517f0000000000000000000000009f2fa7709b30c75047980a0d70a106728f0ef2db6001600160a01b03168152602090f35b3461027a57600036600319011261027a576006546040516001600160a01b039091168152602090f35b3461027a57600036600319011261027a576020600254604051908152f35b3461027a5760208060031936011261027a5760043567ffffffffffffffff811161027a5761033d903690600401611eb7565b90610346612267565b60005b82811061035257005b610365610360828585612021565b611fd3565b61036d612267565b6001600160a01b0390811660008181526004602052604090205490919061046a578590600154166040519182809263b3596f0760e01b825285600483015260249485915afa90811561045e57600091610431575b501561041b576103d082612c58565b156104055750906001917f500f8acd525a3d9f96ab641587f59e34ef9d02f9397fdd46bb7786273bad1607600080a201610349565b906040519063cdb5999560e01b82526004820152fd5b9060405190631066c96360e31b82526004820152fd5b90508681813d8311610457575b6104488183611fe7565b8101031261027a5751876103c1565b503d61043e565b6040513d6000823e3d90fd5b60405163098f893f60e21b815260048101839052602490fd5b3461027a57600036600319011261027a576020600754604051908152f35b3461027a57600036600319011261027a5760206040517fc9d160ddf8721202a3f3e2bd663b1fdb5675930469632a500038010be1a191dc8152f35b3461027a57604036600319011261027a576104f5611e77565b60015460405163b3596f0760e01b81526001600160a01b039283166004820181905260209391929091849184916024918391165afa90811561045e5783906000926105c2575b60405163313ce56760e01b81529350839060049082905afa90811561045e576105839260009261058b575b5061057761057d91600435906120eb565b916120da565b906120fe565b604051908152f35b61057d9192506105b361057791863d88116105bb575b6105ab8183611fe7565b8101906120c1565b929150610566565b503d6105a1565b915082813d83116105eb575b6105d88183611fe7565b8101031261027a5782600492519161053b565b503d6105ce565b3461027a5761060036611ee8565b80939261060b61220b565b60405163095ea7b360e01b81526001600160a01b03949094166004850181905260248501959095526020949285856044816000885af192831561045e5761067795879461073a575b506040519586938493637706db7560e01b8552604060048601526044850191612066565b90602483015203816000855af191821561045e5760009261070b575b506040516370a0823160e01b81523060048201528381602481855afa90811561045e576000916106de575b50156106ce575b50604051908152f35b6106d790612b0d565b50826106c5565b90508381813d8311610704575b6106f58183611fe7565b8101031261027a5751846106be565b503d6106eb565b9091508281813d8311610733575b6107238183611fe7565b8101031261027a57519083610693565b503d610719565b61075990853d8711610760575b6107518183611fe7565b810190612009565b5087610653565b503d610747565b3461027a57602061077736611fa9565b929161079760405194859384936308f109c360e11b85526004850161211e565b03817f0000000000000000000000009f2fa7709b30c75047980a0d70a106728f0ef2db6001600160a01b03165afa801561045e576000906107de575b602090604051908152f35b506020813d602011610809575b816107f860209383611fe7565b8101031261027a57602090516107d3565b3d91506107eb565b3461027a57600036600319011261027a576040517f000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a6001600160a01b03168152602090f35b3461027a57604036600319011261027a57610895600435610875611e77565b90806000526000602052610890600160406000200154612359565b6127f5565b005b3461027a57600036600319011261027a5760006108b2612ca5565b6000915b8151831015610956576001600160a01b036108d1848461208a565b5116604051906370a0823160e01b82523060048301526020918281602481855afa92831561045e57600093610921575b505091610913610919926001946123c2565b9061209e565b9201916108b6565b908094929350813d831161094f575b61093a8183611fe7565b8101031261027a579151906109136001610901565b503d610930565b6040516370a0823160e01b81523060048201527f000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a6001600160a01b0316602082602481845afa91821561045e576000926109c9575b60206105836109be8661091387876123c2565b6109136109136124a1565b91506020823d6020116109f6575b816109e460209383611fe7565b8101031261027a5790519060206109ab565b3d91506109d7565b3461027a57600036600319011261027a57610a26610a1a612ca5565b60405191829182611f64565b0390f35b3461027a57606036600319011261027a57610a43611e8d565b6024359067ffffffffffffffff60443581811161027a57610a68903690600401611eb7565b919092610a7361220b565b60405163095ea7b360e01b81526001600160a01b0391909116600482018190526024820186905260209590939086836044816000895af192831561045e57600093610c50575b50610ae5604051968793849363350376e360e21b85526004850152604060248501526044840191612066565b038183865af192831561045e57600093610bae575b50506040516370a0823160e01b81523060048201528381602481855afa90811561045e57600091610b81575b5015610b71575b50906040519181839283018184528251809152816040850193019160005b828110610b5a57505050500390f35b835185528695509381019392810192600101610b4b565b610b7a90612b0d565b5082610b2d565b90508381813d8311610ba7575b610b988183611fe7565b8101031261027a575184610b26565b503d610b8e565b909192503d806000833e610bc28183611fe7565b810190848183031261027a5780519083821161027a570181601f8201121561027a578051928311610c3a578260051b60405193610c0187830186611fe7565b8452858085019183010192831161027a5785809201905b838210610c2b5750505050908380610afa565b81518152908201908201610c18565b634e487b7160e01b600052604160045260246000fd5b610c6690883d8a11610760576107518183611fe7565b5087610ab9565b3461027a57600036600319011261027a576040516370a0823160e01b81523060048201527f000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a6001600160a01b0316602082602481845afa91821561045e57600092610cf2575b6020610583610ce285856123c2565b610cea6124a1565b91905061209e565b91506020823d602011610d20575b81610d0d60209383611fe7565b8101031261027a57905190610ce2610cd3565b3d9150610d00565b3461027a57610895610d3936611f30565b610d44929192612191565b6001600160a01b031661237f565b3461027a576020610d6236611fa9565b92916107976040519485938493637b5e2c7b60e01b85526004850161211e565b3461027a57600036600319011261027a5760206040517fb3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc8152f35b3461027a57602036600319011261027a57610dd6611e8d565b610dde612267565b6001600160a01b0390811660008181526004602052604090205490919061046a576020602491600154166040519283809263b3596f0760e01b82528660048301525afa90811561045e57600091610e9b575b5015610e8357610e3f81612c58565b15610e6b577f500f8acd525a3d9f96ab641587f59e34ef9d02f9397fdd46bb7786273bad1607600080a2005b6024906040519063cdb5999560e01b82526004820152fd5b60249060405190631066c96360e31b82526004820152fd5b90506020813d602011610ec5575b81610eb660209383611fe7565b8101031261027a575182610e30565b3d9150610ea9565b3461027a57600036600319011261027a57602060405160008152f35b3461027a5760208060031936011261027a57610f03611e8d565b610f0b61231f565b6001600160a01b039081168015610ff057817f000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a169160065416906040519163095ea7b360e01b9081845260048401526000602484015284836044816000885af190811561045e578593604492610fd3575b50600680546001600160a01b03191684179055610f9761231f565b6000604051958694859384526004840152811960248401525af1801561045e57610fbd57005b8161089592903d10610760576107518183611fe7565b610fe990853d8711610760576107518183611fe7565b5086610f7c565b6040516314e632d960e11b8152600490fd5b3461027a57608036600319011261027a5761101b611e8d565b602435906044359081600f0b80920361027a57829161103861220b565b60405163095ea7b360e01b81526001600160a01b03929092166004830181905260248301949094526020939184816044816000875af1801561045e576110eb575b5060405192630d2680e960e11b845260048401526024830152606435604483015282826064816000855af191821561045e5760009261070b57506040516370a0823160e01b81523060048201528381602481855afa90811561045e576000916106de5750156106ce5750604051908152f35b61110190853d8711610760576107518183611fe7565b5084611079565b3461027a57604036600319011261027a57611121611e77565b600435600052600060205260406000209060018060a01b0316600052602052602060ff604060002054166040519015158152f35b3461027a57600036600319011261027a576001546040516001600160a01b039091168152602090f35b3461027a57602036600319011261027a576004356001600160a01b0381169081900361027a576111ac61231f565b604051638c89b64f60e01b8152602081600481855afa90811561045e5760009161122e575b506002548091036112165750600180546001600160a01b031916821790557f3f32684a32a11dabdbb8c0177de80aa3ae36a004d75210335b49e544e48cd0aa600080a2005b60249060405190639b6812b960e01b82526004820152fd5b90506020813d602011611258575b8161124960209383611fe7565b8101031261027a5751826111d1565b3d915061123c565b3461027a57604036600319011261027a5761089561127c611e77565b600435336128aa565b3461027a57600036600319011261027a5761129e61231f565b60065460405163095ea7b360e01b81526001600160a01b039182166004820152600019602482015290602090829060449082906000907f000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a165af1801561045e5761130457005b6108959060203d602011610760576107518183611fe7565b3461027a57604036600319011261027a57611335611e77565b60015460405163b3596f0760e01b81526001600160a01b039283166004820181905260209391929091849184916024918391165afa90811561045e5783906000926113dc575b60405163313ce56760e01b81529350839060049082905afa801561045e576113b26113ba91610583946000916113bf575b506120da565b6004356120eb565b6120fe565b6113d69150863d88116105bb576105ab8183611fe7565b866113ac565b915082813d8311611405575b6113f28183611fe7565b8101031261027a5782600492519161137b565b503d6113e8565b3461027a57602036600319011261027a576001600160a01b0361142d611e8d565b16600052600860205260206040600020541515604051908152f35b3461027a57600036600319011261027a57602060405160ff7f0000000000000000000000000000000000000000000000000000000000000006168152f35b3461027a57600036600319011261027a57604051806007549182815260208091019260076000527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688916000905b8282106114ea57610a2685610a1a81890382611fe7565b8354865294850194600193840193909101906114d3565b3461027a57602036600319011261027a5761151a61231f565b600435600255005b3461027a57600036600319011261027a5760206040517f1f29e81ed8f7ae439f042f6b6767d105e87c4eef908508d5d9e550aef35af6448152f35b3461027a5761089561156e36611f30565b916128aa565b3461027a57604036600319011261027a5761158d611e8d565b602435906115996122c3565b8147106115d6576000918291829182916001600160a01b03165af16115bc61286a565b50156115c457005b604051630a12f52160e11b8152600490fd5b60405163cd78605960e01b8152306004820152602490fd5b3461027a57604036600319011261027a57611607611e77565b336001600160a01b0382160361162357610895906004356127f5565b60405163334bd91960e11b8152600490fd5b3461027a57604036600319011261027a57610895600435611654611e77565b9080600052600060205261166f600160406000200154612359565b612777565b3461027a57600036600319011261027a5760206040517f85e8f2d6819d6b24108062d87ea08f54651bcb8960d98062d3faf96e7873b8b98152f35b3461027a57600036600319011261027a5760006116ca612ca5565b6000915b8151831015611768576001600160a01b036116e9848461208a565b5116604051906370a0823160e01b82523060048301526020918281602481855afa92831561045e57600093611733575b50509161091361172b926001946123c2565b9201916116ce565b908094929350813d8311611761575b61174c8183611fe7565b8101031261027a579151906109136001611719565b503d611742565b6105836020916117766124a1565b509061209e565b3461027a57602036600319011261027a5760043560005260006020526020600160406000200154604051908152f35b3461027a576117ba36611ee8565b91906117c461220b565b604051630293577560e41b81526020946001600160a01b038181169592909187826004818a5afa91821561045e57600092611998575b5095949290918794926000975b8189106118905750505061183995506040519586938493635b96faef60e11b8552604060048601526044850191612066565b90602483015203816000855af191821561045e5760009261185f575b506106c590612bf2565b9091508281813d8311611889575b6118778183611fe7565b8101031261027a5751906106c5611855565b503d61186d565b91939550919395966040519063c661065760e01b825280600483015289826024818c5afa91821561045e5785928b9160009161196b575b506118d3838a8c612021565b356118ed575b505060019150019695939188959391611807565b611930906118fc848b8d612021565b60405163095ea7b360e01b81526001600160a01b038816600482015290356024820152948592839160009183906044820190565b0393165af191821561045e5760019261194e575b85928b91506118d9565b611964908b3d8d11610760576107518183611fe7565b508a611944565b61198b9150823d8411611991575b6119838183611fe7565b810190612047565b8c6118c7565b503d611979565b9091508781813d83116119c0575b6119b08183611fe7565b8101031261027a575190886117fa565b503d6119a6565b3461027a57602036600319011261027a576119e0611e8d565b6119e8612267565b6001600160a01b031660008181526004602052604090205415611a6f5760016003541115611a5d57611a1981612a0d565b15611a45577fcebbf63022189259f517d89d98c7c527b44c211d25e443dad13aab2479c7e7b3600080a2005b6024906040519063644e3dd760e11b82526004820152fd5b6040516305bc742560e11b8152600490fd5b60249060405190632108722b60e01b82526004820152fd5b3461027a57600036600319011261027a5760206040517f1a52e20da533a06a1f80a73dba6e5d09cb788f108eae685b8ce6644834e67abe8152f35b3461027a57606036600319011261027a57611adb611e8d565b611ae3611e77565b90611aec6122c3565b600260055414611b7f5760026005556001600160a01b03908116907f000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a1681148015611b62575b611b4a57611b43916044359161237f565b6001600555005b60249060405190638415be8960e01b82526004820152fd5b50611b7a816000526004602052604060002054151590565b611b32565b604051633ee5aeb560e01b8152600490fd5b3461027a57602036600319011261027a5760043567ffffffffffffffff811161027a57611bc2903690600401611eb7565b90611bcb612267565b60005b828110611bd757005b611be5610360828585612021565b611bed612267565b6001600160a01b031660008181526004602052604090205415611a6f5760016003541115611a5d57611c1e81612a0d565b15611a4557906001917fcebbf63022189259f517d89d98c7c527b44c211d25e443dad13aab2479c7e7b3600080a201611bce565b3461027a57602036600319011261027a5760043563ffffffff60e01b811680910361027a57602090637965db0b60e01b8114908115611c97575b506040519015158152f35b6301ffc9a760e01b14905082611c8c565b3461027a5761042036600319011261027a57366101641161027a57366103e41161027a57611cd461220b565b6004356001600160a01b038116810361027a5760405163095ea7b360e01b81526001600160a01b037f0000000000000000000000009f2fa7709b30c75047980a0d70a106728f0ef2db811660048301526103e4356024830152909160209183916044918391600091165af1801561045e57611e20575b50604051633f51256760e21b8152600460008183015b600b8210611df8578360006101648083015b60058310611de1576103e480359085015261040480359085015230610424850152836020816104448160007f0000000000000000000000009f2fa7709b30c75047980a0d70a106728f0ef2db6001600160a01b03165af1801561045e576000906107de57602090604051908152f35b600190608081818581943701920192019190611d72565b60019060209081906001600160a01b03611e1187611ea3565b16815201930191019091611d60565b611e389060203d602011610760576107518183611fe7565b5080611d4a565b3461027a57604036600319011261027a57610895611e5b611e77565b611e63612191565b6004359033906001600160a01b031661237f565b602435906001600160a01b038216820361027a57565b600435906001600160a01b038216820361027a57565b35906001600160a01b038216820361027a57565b9181601f8401121561027a5782359167ffffffffffffffff831161027a576020808501948460051b01011161027a57565b90606060031983011261027a576004356001600160a01b038116810361027a57916024359067ffffffffffffffff821161027a57611f2891600401611eb7565b909160443590565b606090600319011261027a576001600160a01b03600435818116810361027a579160243591604435908116810361027a5790565b602090602060408183019282815285518094520193019160005b828110611f8c575050505090565b83516001600160a01b031685529381019392810192600101611f7e565b61040060031982011261027a576101649181831161027a576004926103e492831161027a57913590565b356001600160a01b038116810361027a5790565b90601f8019910116810190811067ffffffffffffffff821117610c3a57604052565b9081602091031261027a5751801515810361027a5790565b91908110156120315760051b0190565b634e487b7160e01b600052603260045260246000fd5b9081602091031261027a57516001600160a01b038116810361027a5790565b81835290916001600160fb1b03831161027a5760209260051b809284830137010190565b80518210156120315760209160051b010190565b919082018092116120ab57565b634e487b7160e01b600052601160045260246000fd5b9081602091031261027a575160ff8116810361027a5790565b60ff16604d81116120ab57600a0a90565b818102929181159184041417156120ab57565b8115612108570490565b634e487b7160e01b600052601260045260246000fd5b61040081019493929091600090835b600b831061216b575050506101608201906000915b60058310612154575050506103e00152565b600190608081818581943701920192019190612142565b6001906001600160a01b0361217f84611ea3565b1681529201916020918201910161212d565b3360009081527fc191fc48a308d795605d8380942284aa535eb60ab59dded9d8c4eff77cf3c87260205260409020547f1f29e81ed8f7ae439f042f6b6767d105e87c4eef908508d5d9e550aef35af6449060ff16156121ed5750565b6044906040519063e2517d3f60e01b82523360048301526024820152fd5b3360009081527f35981844e2f7014ed33b54358b929d550bdab17164e935e079ba150ceda75ff860205260409020547fc9d160ddf8721202a3f3e2bd663b1fdb5675930469632a500038010be1a191dc9060ff16156121ed5750565b3360009081527fb56095460044281636dd3a77e227972b73971c7d766b38feb76e4e7f12e8c60260205260409020547f85e8f2d6819d6b24108062d87ea08f54651bcb8960d98062d3faf96e7873b8b99060ff16156121ed5750565b3360009081527fa921dec465a2db617c1283eb3fd0c7be03ef4a04bbcfeba0659a6baa62f9000160205260409020547fb3e25b5404b87e5a838579cb5d7481d61ad96ee284d38ec1e97c07ba64e7f6fc9060ff16156121ed5750565b3360009081527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5602052604081205460ff16156121ed5750565b80600052600060205260406000203360005260205260ff60406000205416156121ed5750565b60405163a9059cbb60e01b60208201526001600160a01b039290921660248301526044808301939093529181526123c0916123bb606483611fe7565b61292d565b565b60015460405163b3596f0760e01b81526001600160a01b0392831660048201819052929091602091829184916024918391165afa91821561045e57600092612470575b50806004936040519485809263313ce56760e01b82525afa801561045e5761243b9360ff92600092612453575b505016926120eb565b604d82116120ab5761245091600a0a906120fe565b90565b6124699250803d106105bb576105ab8183611fe7565b3880612432565b9291508083813d831161249a575b6124888183611fe7565b8101031261027a579151909180612405565b503d61247e565b6000906000916007546000905b8082106124bb5750509190565b600760009081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6888301546040516370a0823160e01b815230600482015293969492939192839290916001600160a01b0316602082602481845afa91821561045e57600092612743575b506040516318160ddd60e01b815290602082600481845afa91821561045e5760009261270f575b50604051630293577560e41b815290602082600481845afa91821561045e576000926126db575b509291906000935b8185106125a55750505050509161259761259d9260019461209e565b9461209e565b9401906124ae565b90919293966040519063c661065760e01b8252886004830152602082602481865afa91821561045e576000926126ba575b50604051634903b0d160e01b8152600481018a9052602081602481875afa90811561045e5786918891600091612680575b50916113ba6126199261261f946120eb565b836123c2565b916001600160a01b039081167f000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a9091160361266c576001916126609161209e565b975b019392919061257b565b979660019161267a9161209e565b96612662565b925050506020813d6020116126b2575b8161269d60209383611fe7565b8101031261027a5751859087906113ba612607565b3d9150612690565b6126d491925060203d602011611991576119838183611fe7565b90386125d6565b9091506020813d602011612707575b816126f760209383611fe7565b8101031261027a57519038612573565b3d91506126ea565b9091506020813d60201161273b575b8161272b60209383611fe7565b8101031261027a5751903861254c565b3d915061271e565b9091506020813d60201161276f575b8161275f60209383611fe7565b8101031261027a57519038612525565b3d9150612752565b9060009180835282602052604083209160018060a01b03169182845260205260ff604084205416156000146127f057808352826020526040832082845260205260408320600160ff198254161790557f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d339380a4600190565b505090565b9060009180835282602052604083209160018060a01b03169182845260205260ff6040842054166000146127f05780835282602052604083208284526020526040832060ff1981541690557ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b339380a4600190565b3d156128a5573d9067ffffffffffffffff8211610c3a5760405191612899601f8201601f191660200184611fe7565b82523d6000602084013e565b606090565b9060018060a01b03809316928360005260046020526040600020541561291457604051926323b872dd60e01b602085015216602483015230604483015260648201526064815260a081019181831067ffffffffffffffff841117610c3a576123c09260405261292d565b604051632762993f60e11b815260048101859052602490fd5b6000806129569260018060a01b03169360208151910182865af161294f61286a565b9083612d13565b8051908115159182612984575b505061296c5750565b60249060405190635274afe760e01b82526004820152fd5b6129979250602080918301019101612009565b153880612963565b6003548110156120315760036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b0190600090565b6007548110156120315760076000527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880190600090565b600090808252600490816020526040832054801515600014612b075760001990808201818111612af45760035490838201918211612ae157818103612a97575b5050506003548015612a8457810190612a658261299f565b909182549160031b1b1916905560035582526020526040812055600190565b634e487b7160e01b855260318452602485fd5b612acc612aa6612ab59361299f565b90549060031b1c92839261299f565b819391549060031b91821b91600019901b19161790565b90558552836020526040852055388080612a4d565b634e487b7160e01b875260118652602487fd5b634e487b7160e01b865260118552602486fd5b50505090565b60008181526008602052604081205490919080156127f05760001990808201818111612bde5760075490838201918211612bca57818103612b96575b5050506007548015612b8257810190612b61826129d6565b909182549160031b1b19169055600755815260086020526040812055600190565b634e487b7160e01b84526031600452602484fd5b612bb4612ba5612ab5936129d6565b90549060031b1c9283926129d6565b9055845260086020526040842055388080612b49565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b85526011600452602485fd5b600081815260086020526040812054612c5357600754600160401b811015612c3f579082612c2b612ab5846001604096016007556129d6565b905560075492815260086020522055600190565b634e487b7160e01b82526041600452602482fd5b905090565b600081815260046020526040812054612c5357600354600160401b811015612c3f579082612c91612ab58460016040960160035561299f565b905560035492815260046020522055600190565b6040519060035480835282602091602082019060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b936000905b828210612cf9575050506123c092500383611fe7565b855484526001958601958895509381019390910190612ce3565b90612d2857508051156115c457805190602001fd5b81511580612d5b575b612d39575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15612d3156fea2646970667358221220d1cc63bdbff580c47b2cd30033e73fdd4c5b031e4074b0fb6720674db015d07b64736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a000000000000000000000000d9ba545656cba566c042f96634749242a3bf76c7000000000000000000000000fa7560956807d95dcef22990ddd92e38dbaf5cdd0000000000000000000000009f2fa7709b30c75047980a0d70a106728f0ef2db0000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b30220000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b30220000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b30220000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b3022
-----Decoded View---------------
Arg [0] : _dusd (address): 0x788D96f655735f52c676A133f4dFC53cEC614d4A
Arg [1] : _amoManager (address): 0xd9Ba545656Cba566C042F96634749242a3bF76c7
Arg [2] : _oracle (address): 0xFA7560956807d95DCeF22990DdD92e38DbAf5cDd
Arg [3] : _router (address): 0x9f2Fa7709B30c75047980a0d70A106728f0Ef2db
Arg [4] : _admin (address): 0x0f5e3D9AEe7Ab5fDa909Af1ef147D98a7f4B3022
Arg [5] : _collateralWithdrawer (address): 0x0f5e3D9AEe7Ab5fDa909Af1ef147D98a7f4B3022
Arg [6] : _recoverer (address): 0x0f5e3D9AEe7Ab5fDa909Af1ef147D98a7f4B3022
Arg [7] : _amoTrader (address): 0x0f5e3D9AEe7Ab5fDa909Af1ef147D98a7f4B3022
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000788d96f655735f52c676a133f4dfc53cec614d4a
Arg [1] : 000000000000000000000000d9ba545656cba566c042f96634749242a3bf76c7
Arg [2] : 000000000000000000000000fa7560956807d95dcef22990ddd92e38dbaf5cdd
Arg [3] : 0000000000000000000000009f2fa7709b30c75047980a0d70a106728f0ef2db
Arg [4] : 0000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b3022
Arg [5] : 0000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b3022
Arg [6] : 0000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b3022
Arg [7] : 0000000000000000000000000f5e3d9aee7ab5fda909af1ef147d98a7f4b3022
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.