Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 20 from a total of 20 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Pause | 21766229 | 7 hrs ago | IN | 0 ETH | 0.00024334 | ||||
Deposit | 21765550 | 9 hrs ago | IN | 0 ETH | 0.00111018 | ||||
Deposit | 21764017 | 14 hrs ago | IN | 0 ETH | 0.00287774 | ||||
Deposit | 21762374 | 20 hrs ago | IN | 0 ETH | 0.00168087 | ||||
Deposit | 21762224 | 20 hrs ago | IN | 0 ETH | 0.00280144 | ||||
Propose Safe | 21761008 | 25 hrs ago | IN | 0 ETH | 0.00249501 | ||||
Deposit | 21760119 | 27 hrs ago | IN | 0 ETH | 0.00138345 | ||||
Deposit | 21759595 | 29 hrs ago | IN | 0 ETH | 0.00082447 | ||||
Deposit | 21751970 | 2 days ago | IN | 0 ETH | 0.00028167 | ||||
Deposit | 21751076 | 2 days ago | IN | 0 ETH | 0.00062776 | ||||
Deposit | 21750919 | 2 days ago | IN | 0 ETH | 0.00046511 | ||||
Deposit | 21749427 | 2 days ago | IN | 0 ETH | 0.00023057 | ||||
Approve | 21748250 | 2 days ago | IN | 0 ETH | 0.00009948 | ||||
Set Operator | 21748246 | 2 days ago | IN | 0 ETH | 0.00009858 | ||||
Deposit | 21743404 | 3 days ago | IN | 0 ETH | 0.0003574 | ||||
Deposit | 21743144 | 3 days ago | IN | 0 ETH | 0.00022337 | ||||
Deposit | 21737428 | 4 days ago | IN | 0 ETH | 0.00070834 | ||||
Deposit | 21732464 | 5 days ago | IN | 0 ETH | 0.00175155 | ||||
Deposit | 21700493 | 9 days ago | IN | 0 ETH | 0.00082779 | ||||
Deposit | 21700475 | 9 days ago | IN | 0 ETH | 0.00096472 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0xB64D29F6...E6ACB8814 The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
OracleVault
Compiler Version
v0.8.25+commit.b61c2a91
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0 // Docgen-SOLC: 0.8.25 pragma solidity ^0.8.25; import {AsyncVault, InitializeParams} from "./AsyncVault.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {IPriceOracle} from "src/interfaces/IPriceOracle.sol"; struct ChangeProposal { address addr; uint256 timestamp; } /** * @title OracleVault * @author RedVeil * @notice ERC-7540 (https://eips.ethereum.org/EIPS/eip-7540) compliant async redeem vault using a PushOracle for pricing and a Safe for managing assets * @dev Oracle and safe security is handled in other contracts. We simply assume they are secure and don't implement any further checks in this contract */ contract OracleVault is AsyncVault { address public safe; /** * @notice Constructor for the OracleVault * @param params The parameters to initialize the vault with * @param oracle_ The oracle to use for pricing * @param safe_ The safe which will manage the assets */ constructor( InitializeParams memory params, address oracle_, address safe_ ) AsyncVault(params) { if (safe_ == address(0) || oracle_ == address(0)) revert Misconfigured(); safe = safe_; oracle = IPriceOracle(oracle_); } /*////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ IPriceOracle public oracle; /// @notice Total amount of underlying `asset` token managed by the safe. function totalAssets() public view override returns (uint256) { return oracle.getQuote(totalSupply, share, address(asset)); } /*////////////////////////////////////////////////////////////// ERC-4626 OVERRIDES //////////////////////////////////////////////////////////////*/ /// @dev Internal function to handle the deposit and mint function afterDeposit(uint256 assets, uint256) internal override { // Deposit and mint already have the `whenNotPaused` modifier so we don't need to check it here _takeFees(); // Transfer assets to the safe SafeTransferLib.safeTransfer(asset, safe, assets); } /*////////////////////////////////////////////////////////////// BaseControlledAsyncRedeem OVERRIDES //////////////////////////////////////////////////////////////*/ /// @dev Internal function to transfer assets from the safe to the vault before fulfilling a redeem function beforeFulfillRedeem(uint256 assets, uint256) internal override { SafeTransferLib.safeTransferFrom(asset, safe, address(this), assets); } /*////////////////////////////////////////////////////////////// AsyncVault OVERRIDES //////////////////////////////////////////////////////////////*/ /// @dev Internal function to handle the withdrawal incentive function handleWithdrawalIncentive( uint256 fee, address feeRecipient ) internal override { if (fee > 0) // Transfer the fee from the safe to the fee recipient SafeTransferLib.safeTransferFrom(asset, safe, feeRecipient, fee); } /*////////////////////////////////////////////////////////////// SWITCH SAFE LOGIC //////////////////////////////////////////////////////////////*/ ChangeProposal public proposedSafe; event SafeProposed(address indexed proposedSafe); event SafeChanged(address indexed oldSafe, address indexed newSafe); /** * @notice Proposes a new safe that can be accepted by the owner after a delay * @param newSafe The new safe to propose * @dev !!! This is a dangerous operation and should be used with extreme caution !!! */ function proposeSafe(address newSafe) external onlyOwner { require(newSafe != address(0), "SafeVault/invalid-safe"); proposedSafe = ChangeProposal({ addr: newSafe, timestamp: block.timestamp }); emit SafeProposed(newSafe); } /** * @notice Accepts the proposed safe * @dev !!! This is a dangerous operation and should be used with extreme caution !!! * @dev This will pause the vault to ensure the oracle is set up correctly and no one sends deposits with faulty prices * @dev Its important to ensure that the oracle will be switched before unpausing the vault again */ function acceptSafe() external onlyOwner { ChangeProposal memory proposal = proposedSafe; require(proposal.addr != address(0), "SafeVault/no-safe-proposed"); require( proposal.timestamp + 3 days <= block.timestamp, "SafeVault/safe-not-yet-acceptable" ); emit SafeChanged(safe, proposal.addr); safe = proposal.addr; delete proposedSafe; // Pause to ensure that no deposits get through with faulty prices _pause(); } /*////////////////////////////////////////////////////////////// SWITCH ORACLE LOGIC //////////////////////////////////////////////////////////////*/ ChangeProposal public proposedOracle; event OracleProposed(address indexed proposedOracle); event OracleChanged(address indexed oldOracle, address indexed newOracle); /** * @notice Proposes a new oracle that can be accepted by the owner after a delay * @param newOracle The new oracle to propose * @dev !!! This is a dangerous operation and should be used with extreme caution !!! */ function proposeOracle(address newOracle) external onlyOwner { require(newOracle != address(0), "SafeVault/invalid-oracle"); proposedOracle = ChangeProposal({ addr: newOracle, timestamp: block.timestamp }); emit OracleProposed(newOracle); } /** * @notice Accepts the proposed oracle * @dev !!! This is a dangerous operation and should be used with extreme caution !!! * @dev This will pause the vault to ensure the oracle is set up correctly and no one sends deposits with faulty prices * @dev Its important to ensure that the oracle will be switched before unpausing the vault again */ function acceptOracle() external onlyOwner { ChangeProposal memory proposal = proposedOracle; require(proposal.addr != address(0), "SafeVault/no-oracle-proposed"); require( proposal.timestamp + 3 days <= block.timestamp, "SafeVault/oracle-not-yet-acceptable" ); emit OracleChanged(address(oracle), proposal.addr); oracle = IPriceOracle(proposal.addr); delete proposedOracle; // Pause to ensure that no deposits get through with faulty prices _pause(); } }
// SPDX-License-Identifier: GPL-3.0 // Docgen-SOLC: 0.8.25 pragma solidity ^0.8.25; import {BaseControlledAsyncRedeem} from "./BaseControlledAsyncRedeem.sol"; import {BaseERC7540, ERC20} from "./BaseERC7540.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; /// @notice Handles the initialize parameters of the vault struct InitializeParams { /// @notice The address of the asset that the vault will manage address asset; /// @notice The name of the vault string name; /// @notice The symbol of the vault string symbol; /// @notice The trusted manager of the vault (handles all sensitive management logic) address owner; /// @notice The limits of the vault Limits limits; /// @notice The fees of the vault Fees fees; } /// @notice Stores the bounds of the vault struct Bounds { /// @notice Upper bound of the vault (will be used for future profit calculations of the manager) uint256 upper; /// @notice Lower bound of the vault (used on withdrawals to ensure an additional asset buffer between the reported totalAssets and the actual totalAssets) uint256 lower; } /// @notice Stores the deposit limit and minAmounts of the vault struct Limits { /// @notice Maximum amount of assets that can be deposited into the vault uint256 depositLimit; /// @notice Minimum amount of shares that can be minted / redeemed from the vault uint256 minAmount; } /// @notice Stores all fee related variables struct Fees { /// @notice Performance fee rate in 1e18 (100% = 1e18) uint64 performanceFee; /// @notice Management fee rate in 1e18 (100% = 1e18) uint64 managementFee; /// @notice Withdrawal incentive fee rate in 1e18 (100% = 1e18) uint64 withdrawalIncentive; /// @notice Timestamp of the last time the fees were updated (used for management fee calculations) uint64 feesUpdatedAt; /// @notice High water mark of the vault (used for performance fee calculations) uint256 highWaterMark; /// @notice Address of the fee recipient address feeRecipient; } /** * @title AsyncVault * @author RedVeil * @notice Abstract contract containing reusable logic that are the basis of ERC-7540 compliant async redeem vauls * @notice Besides the basic logic for ERC-7540 this contract contains most other logic to manage a modern DeFi vault * @dev Logic to account and manage assets must be implemented by inheriting contracts */ abstract contract AsyncVault is BaseControlledAsyncRedeem { using FixedPointMathLib for uint256; error ZeroAmount(); error Misconfigured(); /** * @notice Constructor for AsyncVault * @param params The initialization parameters */ constructor( InitializeParams memory params ) BaseERC7540(params.owner, params.asset, params.name, params.symbol) { _setLimits(params.limits); _setFees(params.fees); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Deposit assets into the vault * @param assets The amount of assets to deposit * @return shares The amount of shares required */ function deposit(uint256 assets) external returns (uint256) { return deposit(assets, msg.sender); } /** * @notice Mint shares into the vault * @param shares The amount of shares to mint * @return assets The amount of assets received */ function mint(uint256 shares) external returns (uint256) { return mint(shares, msg.sender); } /** * @notice Withdraw assets from the vault * @param assets The amount of assets to withdraw * @return shares The amount of shares required */ function withdraw(uint256 assets) external returns (uint256) { return withdraw(assets, msg.sender, msg.sender); } /** * @notice Redeem shares from the vault * @param shares The amount of shares to redeem * @return assets The amount of assets received */ function redeem(uint256 shares) external returns (uint256) { return redeem(shares, msg.sender, msg.sender); } /*////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Simulates a deposit into the vault and returns the amount of shares that would be received by the user * @param assets The amount of assets to deposit * @return shares The amount of shares that would be received by the user * @dev This function will return 0 if the vault is paused or if the deposit doesnt meet the limits */ function previewDeposit( uint256 assets ) public view override returns (uint256) { Limits memory limits_ = limits; uint256 shares = convertToShares(assets); if ( paused || totalAssets() + assets > limits_.depositLimit || shares < limits_.minAmount ) return 0; return super.previewDeposit(assets); } /** * @notice Simulates a mint into the vault and returns the amount of assets required to mint the given amount of shares * @param shares The amount of shares to mint * @return assets The amount of assets required to mint the given amount of shares * @dev This function will return 0 if the vault is paused or if the mint doesnt meet the limits */ function previewMint( uint256 shares ) public view override returns (uint256) { Limits memory limits_ = limits; uint256 assets = convertToAssets(shares); if ( paused || totalAssets() + assets > limits_.depositLimit || shares < limits_.minAmount ) return 0; return super.previewMint(shares); } /** * @notice Converts shares to assets based on a lower bound of totalAssets * @param shares The amount of shares to convert * @return lowerTotalAssets The lower bound value of assets that correspond to the given amount of shares * @dev This function is used on redeem fulfillment to ensure an additional asset buffer between the reported totalAssets and the actual totalAssets. * In most cases this will be the same as `convertToAssets` but same vaults might need to add a small buffer if they use volatile strategies or assets that are hard to sell. */ function convertToLowBoundAssets( uint256 shares ) public view returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. uint256 assets = totalAssets().mulDivDown(1e18 - bounds.lower, 1e18); return supply == 0 ? shares : shares.mulDivDown(assets, supply); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Returns the maximum amount of assets that can be deposited into the vault * @return assetsThe maxDeposit of the controller * @dev Will return 0 if the vault is paused or if the deposit limit is reached */ function maxDeposit(address) public view override returns (uint256) { uint256 assets = totalAssets(); uint256 depositLimit_ = limits.depositLimit; if (paused) return 0; if (depositLimit_ == type(uint256).max) return depositLimit_; return assets >= depositLimit_ ? 0 : depositLimit_ - assets; } /** * @notice Returns the maximum amount of shares that can be minted into the vault * @return shares The maxMint of the controller * @dev Will return 0 if the vault is paused or if the deposit limit is reached * @dev Overflows if depositLimit is close to maxUint (convertToShares multiplies depositLimit with totalSupply) */ function maxMint(address) public view override returns (uint256) { uint256 assets = totalAssets(); uint256 depositLimit_ = limits.depositLimit; if (paused) return 0; if (depositLimit_ == type(uint256).max) return depositLimit_; return assets >= depositLimit_ ? 0 : convertToShares(depositLimit_ - assets); } /*////////////////////////////////////////////////////////////// REQUEST REDEEM LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Requests a redeem for the caller * @param shares The amount of shares to redeem * @return requestId The requestId of the redeem request */ function requestRedeem(uint256 shares) external returns (uint256) { return requestRedeem(shares, msg.sender, msg.sender); } /** * @notice Requests a redeem of shares from the vault * @param shares The amount of shares to redeem * @param controller The user that will be receiving pending shares * @param owner The owner of the shares to redeem * @return requestId The requestId of the redeem request * @dev This redeem request is added to any pending redeem request of the controller * @dev This function will revert if the shares are less than the minAmount */ function requestRedeem( uint256 shares, address controller, address owner ) public override returns (uint256 requestId) { require(shares >= limits.minAmount, "ERC7540Vault/min-amount"); return _requestRedeem(shares, controller, owner); } /*////////////////////////////////////////////////////////////// FULFILL REDEEM LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Fulfills a redeem request of the controller to allow the controller to withdraw their assets * @param shares The amount of shares to redeem * @param controller The controller to redeem for * @return assets The amount of assets received * @dev This function will revert if the shares are less than the minAmount * @dev This function will also take the withdrawal incentive fee from the assets to incentivse the manager to fulfill the request */ function fulfillRedeem( uint256 shares, address controller ) external override returns (uint256 assets) { // Take fees before fulfilling the redeem _takeFees(); // Using the lower bound totalAssets ensures that even with volatile strategies and market conditions we will have sufficient assets to cover the redeem assets = convertToLowBoundAssets(shares); // Calculate the withdrawal incentive fee from the assets Fees memory fees_ = fees; uint256 fees = assets.mulDivDown( uint256(fees_.withdrawalIncentive), 1e18 ); // Burn controller's shares _burn(address(this), shares); // Fulfill the redeem request _fulfillRedeem(assets - fees, shares, controller); // Send the withdrawal incentive fee to the fee recipient handleWithdrawalIncentive(fees, fees_.feeRecipient); } /** * @notice Fulfills multiple redeem requests of the controller to allow the controller to withdraw their assets * @param shares The amount of shares to redeem * @param controllers The controllers to redeem for * @return total The total amount of assets received * @dev This function will revert if the shares and controllers arrays are not the same length * @dev This function will also take the withdrawal incentive fee from the assets to incentivse the manager to fulfill the requests */ function fulfillMultipleRedeems( uint256[] memory shares, address[] memory controllers ) external returns (uint256 total) { if (shares.length != controllers.length) revert Misconfigured(); // Take fees before fulfilling the redeem _takeFees(); // cache the fees Fees memory fees_ = fees; uint256 totalShares; uint256 totalFees; for (uint256 i; i < shares.length; i++) { // Using the lower bound totalAssets ensures that even with volatile strategies and market conditions we will have sufficient assets to cover the redeem uint256 assets = convertToLowBoundAssets(shares[i]); // Calculate the withdrawal incentive fee from the assets uint256 fees = assets.mulDivDown( uint256(fees_.withdrawalIncentive), 1e18 ); // Fulfill the redeem request _fulfillRedeem(assets - fees, shares[i], controllers[i]); // Add to the total assets and fees total += assets; totalFees += fees; totalShares += shares[i]; } // Burn controller's shares _burn(address(this), totalShares); // Send the withdrawal incentive fee to the fee recipient handleWithdrawalIncentive(totalFees, fees_.feeRecipient); return total; } /** * @notice Handles the withdrawal incentive fee by sending it to the fee recipient * @param fee The amount of fee to send * @param feeRecipient The address to send the fee to * @dev This function is expected to be overriden in inheriting contracts */ function handleWithdrawalIncentive( uint256 fee, address feeRecipient ) internal virtual { if (fee > 0) SafeTransferLib.safeTransfer(asset, feeRecipient, fee); } /*////////////////////////////////////////////////////////////// ERC-4626 OVERRIDES //////////////////////////////////////////////////////////////*/ /** * @notice Takes fees before a withdraw (if the contract is not paused) * @dev This function is expected to be overriden in inheriting contracts */ function beforeWithdraw(uint256 assets, uint256) internal virtual override { if (!paused) _takeFees(); } /** * @notice Takes fees before a deposit * @dev This function is expected to be overriden in inheriting contracts */ function beforeDeposit(uint256 assets, uint256) internal virtual override { // deposit and mint already have the `whenNotPaused` modifier so we don't need to check it here _takeFees(); } /*////////////////////////////////////////////////////////////// BOUND LOGIC //////////////////////////////////////////////////////////////*/ Bounds public bounds; event BoundsUpdated(Bounds prev, Bounds next); /// @notice Returns the bounds of the vault function getBounds() public view returns (Bounds memory) { return bounds; } /** * @notice Sets the bounds of the vault to ensure that even with volatile strategies and market conditions we will have sufficient assets to cover the redeem * @param bounds_ The bounds to set * @dev This function will revert if the bounds are greater than or equal to 1e18 */ function setBounds(Bounds memory bounds_) external onlyOwner { // Bounds shouldnt be larger than 20% if (bounds_.lower > 2e17 || bounds_.upper > 2e17) revert Misconfigured(); emit BoundsUpdated(bounds, bounds_); bounds = bounds_; } /*////////////////////////////////////////////////////////////// FEE LOGIC //////////////////////////////////////////////////////////////*/ Fees public fees; event FeesUpdated(Fees prev, Fees next); error InvalidFee(uint256 fee); /// @notice Returns the fees parameters of the vault function getFees() public view returns (Fees memory) { return fees; } /// @notice Returns the accrued fees of the vault function accruedFees() public view returns (uint256) { Fees memory fees_ = fees; return _accruedFees(fees_); } /// @dev Internal function to calculate the accrued fees function _accruedFees(Fees memory fees_) internal view returns (uint256) { return _accruedPerformanceFee(fees_) + _accruedManagementFee(fees_); } /** * @notice Performance fee that has accrued since last fee harvest. * @return accruedPerformanceFee In underlying `asset` token. * @dev Performance fee is based on a high water mark value. If vault share value has increased above the * HWM in a fee period, issue fee shares to the vault equal to the performance fee. */ function _accruedPerformanceFee( Fees memory fees_ ) internal view returns (uint256) { uint256 shareValue = convertToAssets(10 ** decimals); uint256 performanceFee = uint256(fees_.performanceFee); return performanceFee > 0 && shareValue > fees_.highWaterMark ? performanceFee.mulDivUp( (shareValue - fees_.highWaterMark) * totalSupply, (10 ** (18 + decimals)) ) : 0; } /** * @notice Management fee that has accrued since last fee harvest. * @return accruedManagementFee In underlying `asset` token. * @dev Management fee is annualized per minute, based on 525,600 minutes per year. Total assets are calculated using * the average of their current value and the value at the previous fee harvest checkpoint. This method is similar to * calculating a definite integral using the trapezoid rule. */ function _accruedManagementFee( Fees memory fees_ ) internal view returns (uint256) { uint256 managementFee = uint256(fees_.managementFee); return managementFee > 0 ? managementFee.mulDivDown( totalAssets() * (block.timestamp - fees_.feesUpdatedAt), 31536000 // seconds per year ) / 1e18 : 0; } /** * @notice Sets the fees of the vault * @param fees_ The fees to set * @dev This function will revert if the fees are greater than 20% performanceFee, 5% managementFee, or 5% withdrawalIncentive * @dev This function will also take the fees before setting them to ensure the new fees rates arent applied to any pending fees */ function setFees(Fees memory fees_) public onlyOwner whenNotPaused { _takeFees(); _setFees(fees_); } /// @dev Internal function to set the fees function _setFees(Fees memory fees_) internal { // Dont take more than 20% performanceFee, 5% managementFee, 5% withdrawalIncentive if ( fees_.performanceFee > 2e17 || fees_.managementFee > 5e16 || fees_.withdrawalIncentive > 5e16 ) revert Misconfigured(); if (fees_.feeRecipient == address(0)) revert Misconfigured(); // Dont rely on user input here fees_.feesUpdatedAt = uint64(block.timestamp); // initialise or copy current HWM if (fees.highWaterMark == 0) { fees_.highWaterMark = convertToAssets(10 ** decimals); // from constructor } else { fees_.highWaterMark = fees.highWaterMark; // from setFees } emit FeesUpdated(fees, fees_); fees = fees_; } /** * @notice Mints fees as shares of the vault to the fee recipient * @dev It will also update the all other fee related variables */ function takeFees() external whenNotPaused { _takeFees(); } /// @dev Internal function to take the fees function _takeFees() internal { Fees memory fees_ = fees; uint256 perfFee = _accruedPerformanceFee(fees_); uint256 mgmtFee = _accruedManagementFee(fees_); uint256 shareValue = convertToAssets(10 ** decimals); // Mint fees to the fee recipient if (perfFee + mgmtFee > 0) _mint(fees_.feeRecipient, convertToShares(perfFee + mgmtFee)); // Update the high water mark (used by performance fee) if (shareValue > fees_.highWaterMark) fees.highWaterMark = shareValue; // Update the fees updated at timestamp (used by management fee) if (mgmtFee > 0) fees.feesUpdatedAt = uint64(block.timestamp); } /*////////////////////////////////////////////////////////////// LIMIT LOGIC //////////////////////////////////////////////////////////////*/ Limits public limits; event LimitsUpdated(Limits prev, Limits next); /** * @notice Sets the deposit limit and minAmounts of the vault to limit user exposure to strategy risks * @param limits_ The limits to set */ function setLimits(Limits memory limits_) external onlyOwner { _setLimits(limits_); } /// @dev Internal function to set the limits function _setLimits(Limits memory limits_) internal { // cache uint256 totalSupply_ = totalSupply; if (totalSupply_ > 0 && limits_.depositLimit < totalAssets()) revert Misconfigured(); if (limits_.minAmount > (10 ** decimals)) revert Misconfigured(); emit LimitsUpdated(limits, limits_); limits = limits_; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument. mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant MAX_UINT256 = 2**256 - 1; uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // Divide x * y by the denominator. z := div(mul(x, y), denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) } // If x * y modulo the denominator is strictly greater than 0, // 1 is added to round up the division of x * y by the denominator. z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let y := x // We start y at x, which will help us make our initial estimate. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // We check y >= 2^(k + 8) but shift right by k bits // each branch to ensure that if x >= 256, then y >= 256. if iszero(lt(y, 0x10000000000000000000000000000000000)) { y := shr(128, y) z := shl(64, z) } if iszero(lt(y, 0x1000000000000000000)) { y := shr(64, y) z := shl(32, z) } if iszero(lt(y, 0x10000000000)) { y := shr(32, y) z := shl(16, z) } if iszero(lt(y, 0x1000000)) { y := shr(16, y) z := shl(8, z) } // Goal was to get z*z*y within a small factor of x. More iterations could // get y in a tighter range. Currently, we will have y in [256, 256*2^16). // We ensured y >= 256 so that the relative difference between y and y+1 is small. // That's not possible if x < 256 but we can just verify those cases exhaustively. // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256. // Correctness can be checked exhaustively for x < 256, so we assume y >= 256. // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps. // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256. // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18. // There is no overflow risk here since y < 2^136 after the first branch above. z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If x+1 is a perfect square, the Babylonian method cycles between // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Mod x by y. Note this will return // 0 instead of reverting if y is zero. z := mod(x, y) } } function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { // Divide x by y. Note this will return // 0 instead of reverting if y is zero. r := div(x, y) } } function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Add 1 to x * y if x % y > 0. Note this will // return 0 instead of reverting if y is zero. z := add(gt(mod(x, y), 0), div(x, y)) } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.8.0; /// @title IPriceOracle /// @custom:security-contact [email protected] /// @author Euler Labs (https://www.eulerlabs.com/) /// @notice Common PriceOracle interface. interface IPriceOracle { /// @notice Get the name of the oracle. /// @return The name of the oracle. function name() external view returns (string memory); /// @notice One-sided price: How much quote token you would get for inAmount of base token, assuming no price spread. /// @param inAmount The amount of `base` to convert. /// @param base The token that is being priced. /// @param quote The token that is the unit of account. /// @return outAmount The amount of `quote` that is equivalent to `inAmount` of `base`. function getQuote( uint256 inAmount, address base, address quote ) external view returns (uint256 outAmount); /// @notice Two-sided price: How much quote token you would get/spend for selling/buying inAmount of base token. /// @param inAmount The amount of `base` to convert. /// @param base The token that is being priced. /// @param quote The token that is the unit of account. /// @return bidOutAmount The amount of `quote` you would get for selling `inAmount` of `base`. /// @return askOutAmount The amount of `quote` you would spend for buying `inAmount` of `base`. function getQuotes( uint256 inAmount, address base, address quote ) external view returns (uint256 bidOutAmount, uint256 askOutAmount); }
// SPDX-License-Identifier: GPL-3.0 // Docgen-SOLC: 0.8.25 pragma solidity ^0.8.25; import {BaseERC7540} from "./BaseERC7540.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; import {IERC7540Redeem} from "ERC-7540/interfaces/IERC7540.sol"; /// @notice Stores the requestBalance of a controller struct RequestBalance { /// @notice The amount of shares that have been requested to be redeemed uint256 pendingShares; /// @notice The timestamp of the last redeem request (will be used to ensure timely fulfillment of redeem requests) uint256 requestTime; /// @notice The amount of shares that have been freed up by a fulfilled redeem request uint256 claimableShares; /// @notice The amount of assets that have been freed up by a fulfilled redeem request uint256 claimableAssets; } /** * @title BaseControlledAsyncRedeem * @author RedVeil * @notice Abstract contract containing reusable logic for controlled async redeem flows * @dev Based on https://github.com/ERC4626-Alliance/ERC-7540-Reference/blob/main/src/BaseControlledAsyncRedeem.sol */ abstract contract BaseControlledAsyncRedeem is BaseERC7540, IERC7540Redeem { using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// ERC4626 OVERRIDDEN LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Deposit assets into the vault * @param assets The amount of assets to deposit * @param receiver The address to receive the shares * @return shares The amount of shares received * @dev This function is synchronous and will revert if the vault is paused * @dev It will first use claimable balances of previous redeem requests before transferring assets from the sender */ function deposit( uint256 assets, address receiver ) public override whenNotPaused returns (uint256 shares) { // Additional logic for inheriting contracts beforeDeposit(assets, shares); // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // Need to transfer before minting or ERC777s could reenter. SafeTransferLib.safeTransferFrom( asset, msg.sender, address(this), assets ); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); // Additional logic for inheriting contracts afterDeposit(assets, shares); } /** * @notice Mints shares from the vault * @param shares The amount of shares to mint * @param receiver The address to receive the shares * @return assets The amount of assets deposited * @dev This function is synchronous and will revert if the vault is paused * @dev It will first use claimable balances of previous redeem requests before minting shares */ function mint( uint256 shares, address receiver ) public override whenNotPaused returns (uint256 assets) { // Additional logic for inheriting contracts beforeDeposit(assets, shares); require(shares != 0, "ZERO_SHARES"); assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. // Need to transfer before minting or ERC777s could reenter. SafeTransferLib.safeTransferFrom( asset, msg.sender, address(this), assets ); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); // Additional logic for inheriting contracts afterDeposit(assets, shares); } /// @dev Additional logic for inheriting contracts before depositing function beforeDeposit(uint256 assets, uint256 shares) internal virtual {} /** * @notice Withdraws assets from the vault which have beenpreviously freed up by a fulfilled redeem request * @param assets The amount of assets to withdraw * @param receiver The address to receive the assets * @param controller The controller to withdraw from * @return shares The amount of shares burned * @dev This function is asynchronous and will not revert if the vault is paused * @dev msg.sender must be the controller or an operator for the controller * @dev Requires sufficient claimableAssets in the controller's requestBalance */ function withdraw( uint256 assets, address receiver, address controller ) public virtual override returns (uint256 shares) { require( controller == msg.sender || isOperator[controller][msg.sender], "ERC7540Vault/invalid-caller" ); require(assets != 0, "ZERO_ASSETS"); RequestBalance storage currentBalance = requestBalances[controller]; shares = assets.mulDivUp( currentBalance.claimableShares, currentBalance.claimableAssets ); // Modify the currentBalance state accordingly _withdrawClaimableBalance(assets, currentBalance); // Additional logic for inheriting contracts beforeWithdraw(assets, shares); // Transfer assets to the receiver SafeTransferLib.safeTransfer(asset, receiver, assets); emit Withdraw(msg.sender, receiver, controller, assets, shares); // Additional logic for inheriting contracts afterWithdraw(assets, shares); } /** * @notice Modifies the currentBalance state to reflect a withdrawal of claimableAssets * @param assets The amount of assets to withdraw * @param currentBalance The requestBalance of the controller * @dev Claiming partially introduces precision loss. The user therefore receives a rounded down amount, * while the claimable balance is reduced by a rounded up amount. */ function _withdrawClaimableBalance( uint256 assets, RequestBalance storage currentBalance ) internal { uint256 sharesUp = assets.mulDivUp( currentBalance.claimableShares, currentBalance.claimableAssets ); currentBalance.claimableAssets -= assets; currentBalance.claimableShares = currentBalance.claimableShares > sharesUp ? currentBalance.claimableShares - sharesUp : 0; } /** * @notice Redeems shares from the vault which have beenpreviously freed up by a fulfilled redeem request * @param shares The amount of shares to redeem * @param receiver The address to receive the assets * @param controller The controller to redeem from * @return assets The amount of assets received * @dev This function is asynchronous and will not revert if the vault is paused * @dev msg.sender must be the controller or an operator for the controller * @dev Requires sufficient claimableShares in the controller's requestBalance */ function redeem( uint256 shares, address receiver, address controller ) public virtual override returns (uint256 assets) { require( controller == msg.sender || isOperator[controller][msg.sender], "ERC7540Vault/invalid-caller" ); require(shares != 0, "ZERO_SHARES"); RequestBalance storage currentBalance = requestBalances[controller]; assets = shares.mulDivDown( currentBalance.claimableAssets, currentBalance.claimableShares ); // Modify the currentBalance state accordingly _redeemClaimableBalance(shares, currentBalance); // Additional logic for inheriting contracts beforeWithdraw(assets, shares); // Transfer assets to the receiver SafeTransferLib.safeTransfer(asset, receiver, assets); emit Withdraw(msg.sender, receiver, controller, assets, shares); // Additional logic for inheriting contracts afterWithdraw(assets, shares); } /** * @notice Modifies the currentBalance state to reflect a withdrawal of claimableAssets * @param shares The amount of shares to redeem * @param currentBalance The requestBalance of the controller * @dev Claiming partially introduces precision loss. The user therefore receives a rounded down amount, * while the claimable balance is reduced by a rounded up amount. */ function _redeemClaimableBalance( uint256 shares, RequestBalance storage currentBalance ) internal { uint256 assetsUp = shares.mulDivUp( currentBalance.claimableAssets, currentBalance.claimableShares ); currentBalance.claimableAssets = currentBalance.claimableAssets > assetsUp ? currentBalance.claimableAssets - assetsUp : 0; currentBalance.claimableShares -= shares; } /// @dev Additional logic for inheriting contracts after withdrawing function afterWithdraw(uint256 assets, uint256 shares) internal virtual {} /*////////////////////////////////////////////////////////////// ACCOUNTNG LOGIC //////////////////////////////////////////////////////////////*/ /// @dev controller => requestBalance mapping(address => RequestBalance) public requestBalances; /** * @notice Returns the requestBalance of a controller * @param controller The controller to get the requestBalance of * @return requestBalance The requestBalance of the controller */ function getRequestBalance( address controller ) public view returns (RequestBalance memory) { return requestBalances[controller]; } /** * @notice Returns the requested shares for redeem that have not yet been fulfilled of a controller * @param controller The controller to get the pendingShares of * @return pendingShares The pendingShares of the controller */ function pendingRedeemRequest( uint256, address controller ) public view returns (uint256) { return requestBalances[controller].pendingShares; } /** * @notice Returns the shares that have been freed up by a fulfilled redeem request of a controller * @param controller The controller to get the claimableShares of * @return claimableShares The claimableShares of the controller */ function claimableRedeemRequest( uint256, address controller ) public view returns (uint256) { return requestBalances[controller].claimableShares; } /** * @notice Simulates a deposit into the vault and returns the amount of shares that would be received by the user * @param assets The amount of assets to deposit * @return shares The amount of shares that would be received by the user * @dev This function will return 0 if the vault is paused */ function previewDeposit( uint256 assets ) public view virtual override returns (uint256) { return paused ? 0 : super.previewDeposit(assets); } /** * @notice Simulates a mint into the vault and returns the amount of assets required to mint the given amount of shares * @param shares The amount of shares to mint * @return assets The amount of assets required to mint the given amount of shares * @dev This function will return 0 if the vault is paused */ function previewMint( uint256 shares ) public view virtual override returns (uint256) { return paused ? 0 : super.previewMint(shares); } /// @dev Previewing withdraw is not supported for async flows (we would require the controller to be known which we do not have in ERC4626) function previewWithdraw( uint256 ) public pure virtual override returns (uint256) { revert("ERC7540Vault/async-flow"); } /// @dev Previewing redeem is not supported for async flows (we would require the controller to be known which we do not have in ERC4626) function previewRedeem( uint256 ) public pure virtual override returns (uint256 assets) { revert("ERC7540Vault/async-flow"); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Returns the maximum amount of assets that can be deposited into the vault * @return assets The maxDeposit of the controller * @dev Will return 0 if the vault is paused */ function maxDeposit( address ) public view virtual override returns (uint256) { return paused ? 0 : type(uint256).max; } /** * @notice Returns the maximum amount of shares that can be minted into the vault * @return shares The maxMint of the controller * @dev Will return 0 if the vault is paused */ function maxMint(address) public view virtual override returns (uint256) { return paused ? 0 : type(uint256).max; } /** * @notice Returns the maximum amount of assets that can be withdrawn from the vault * @param controller The controller to get the maxWithdraw of * @return assets The maxWithdraw of the controller * @dev This is simply the claimableAssets of the controller (i.e. the assets that have been freed up by a fulfilled redeem request) */ function maxWithdraw( address controller ) public view virtual override returns (uint256) { return requestBalances[controller].claimableAssets; } /** * @notice Returns the maximum amount of shares that can be redeemed from the vault * @param controller The controller to get the maxRedeem of * @return shares The maxRedeem of the controller * @dev This is simply the claimableShares of the controller (i.e. the shares that have been freed up by a fulfilled redeem request) */ function maxRedeem( address controller ) public view virtual override returns (uint256) { return requestBalances[controller].claimableShares; } /*////////////////////////////////////////////////////////////// REQUEST REDEEM LOGIC //////////////////////////////////////////////////////////////*/ event RedeemRequested( address indexed controller, address indexed owner, uint256 requestId, address sender, uint256 shares ); /** * @notice Requests a redeem of shares from the vault * @param shares The amount of shares to redeem * @param controller The user that will be receiving pending shares * @param owner The owner of the shares to redeem * @return requestId The requestId of the redeem request * @dev This redeem request is added to any pending redeem request of the controller */ function requestRedeem( uint256 shares, address controller, address owner ) external virtual returns (uint256 requestId) { return _requestRedeem(shares, controller, owner); } /// @dev Internal function to request a redeem function _requestRedeem( uint256 shares, address controller, address owner ) internal returns (uint256 requestId) { require( owner == msg.sender || isOperator[owner][msg.sender], "ERC7540Vault/invalid-owner" ); require( ERC20(address(this)).balanceOf(owner) >= shares, "ERC7540Vault/insufficient-balance" ); require(shares != 0, "ZERO_SHARES"); // Transfer shares from owner to vault (these will be burned on withdrawal) SafeTransferLib.safeTransferFrom(this, owner, address(this), shares); // Update the controller's requestBalance RequestBalance storage currentBalance = requestBalances[controller]; currentBalance.pendingShares += shares; currentBalance.requestTime = block.timestamp; emit RedeemRequested(controller, owner, REQUEST_ID, msg.sender, shares); return REQUEST_ID; } /*////////////////////////////////////////////////////////////// CANCEL REDEEM REQUEST LOGIC //////////////////////////////////////////////////////////////*/ event RedeemRequestCanceled( address indexed controller, address indexed receiver, uint256 shares ); /** * @notice Cancels a redeem request of the controller * @param controller The controller to cancel the redeem request of * @dev This will transfer the pending shares back to the msg.sender */ function cancelRedeemRequest(address controller) external virtual { return _cancelRedeemRequest(controller, msg.sender); } /** * @notice Cancels a redeem request of the controller * @param controller The controller to cancel the redeem request of * @param receiver The receiver of the pending shares * @dev This will transfer the pending shares back to the receiver */ function cancelRedeemRequest( address controller, address receiver ) public virtual { return _cancelRedeemRequest(controller, receiver); } /// @dev Internal function to cancel a redeem request function _cancelRedeemRequest( address controller, address receiver ) internal virtual { require( controller == msg.sender || isOperator[controller][msg.sender], "ERC7540Vault/invalid-caller" ); // Get the pending shares RequestBalance storage currentBalance = requestBalances[controller]; uint256 shares = currentBalance.pendingShares; require(shares > 0, "ERC7540Vault/no-pending-request"); // Transfer the pending shares back to the receiver SafeTransferLib.safeTransfer(ERC20(address(this)), receiver, shares); // Update the controller's requestBalance currentBalance.pendingShares = 0; currentBalance.requestTime = 0; emit RedeemRequestCanceled(controller, receiver, shares); } /*////////////////////////////////////////////////////////////// DEPOSIT FULFILLMENT LOGIC //////////////////////////////////////////////////////////////*/ event RedeemRequestFulfilled( address indexed controller, address indexed fulfiller, uint256 shares, uint256 assets ); /** * @notice Fulfills a redeem request of the controller to allow the controller to withdraw their assets * @param shares The amount of shares to redeem * @param controller The controller to redeem for * @return assets The amount of assets claimable by the controller */ function fulfillRedeem( uint256 shares, address controller ) external virtual returns (uint256) { uint256 assets = convertToAssets(shares); // Burn controller's shares _burn(address(this), shares); return _fulfillRedeem(assets, shares, controller); } /// @dev Internal function to fulfill a redeem request function _fulfillRedeem( uint256 assets, uint256 shares, address controller ) internal virtual returns (uint256) { if (assets == 0 || shares == 0) revert("ZERO_SHARES"); RequestBalance storage currentBalance = requestBalances[controller]; // Check that there are pending shares to fulfill require( currentBalance.pendingShares != 0 && shares <= currentBalance.pendingShares, "ZERO_SHARES" ); // Additional logic for inheriting contracts beforeFulfillRedeem(assets, shares); // Update the controller's requestBalance currentBalance.claimableShares += shares; currentBalance.claimableAssets += assets; currentBalance.pendingShares -= shares; // Reset the requestTime if there are no more pending shares if (currentBalance.pendingShares == 0) currentBalance.requestTime = 0; emit RedeemRequestFulfilled(controller, msg.sender, shares, assets); // Additional logic for inheriting contracts afterFulfillRedeem(assets, shares); return assets; } /// @dev Additional logic for inheriting contracts before fulfilling a redeem request function beforeFulfillRedeem( uint256 assets, uint256 shares ) internal virtual {} /// @dev Additional logic for inheriting contracts after fulfilling a redeem request function afterFulfillRedeem( uint256 assets, uint256 shares ) internal virtual {} /*////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Check if the contract supports an interface * @param interfaceId The interface ID to check * @return exists True if the contract supports the interface, false otherwise */ function supportsInterface( bytes4 interfaceId ) public pure virtual override returns (bool) { return interfaceId == type(IERC7540Redeem).interfaceId || super.supportsInterface(interfaceId); } }
// SPDX-License-Identifier: GPL-3.0 // Docgen-SOLC: 0.8.25 pragma solidity ^0.8.25; import {ERC4626} from "solmate/tokens/ERC4626.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol"; import {Owned} from "src/utils/Owned.sol"; import {Pausable} from "src/utils/Pausable.sol"; import {IERC7540Operator} from "ERC-7540/interfaces/IERC7540.sol"; import {IERC7575} from "ERC-7540/interfaces/IERC7575.sol"; import {IERC165} from "ERC-7540/interfaces/IERC7575.sol"; /** * @title BaseERC7540 * @author RedVeil * @notice Abstract contract containing reusable logic for ERC-7540 - https://eips.ethereum.org/EIPS/eip-7540 * @notice Based on https://github.com/ERC4626-Alliance/ERC-7540-Reference/blob/main/src/BaseERC7540.sol */ abstract contract BaseERC7540 is ERC4626, Owned, ReentrancyGuard, Pausable, IERC7540Operator { /// @dev Assume requests are non-fungible and all have ID = 0 uint256 internal constant REQUEST_ID = 0; /// @dev Required for IERC7575 address public share = address(this); /** * @notice Constructor for BaseERC7540 * @param _owner The permissioned owner of the vault (controls all management functions) * @param _asset The address of the underlying asset * @param _name The name of the vault * @param _symbol The symbol of the vault */ constructor( address _owner, address _asset, string memory _name, string memory _symbol ) Owned(_owner) ERC4626(ERC20(_asset), _name, _symbol) {} /*////////////////////////////////////////////////////////////// ROLE LOGIC //////////////////////////////////////////////////////////////*/ /// @dev role => account => approved mapping(bytes32 => mapping(address => bool)) public hasRole; event RoleUpdated(bytes32 role, address account, bool approved); /** * @notice Update the role for an account * @param role The role to update * @param account The account to update * @param approved The approval status to set */ function updateRole( bytes32 role, address account, bool approved ) public onlyOwner { hasRole[role][account] = approved; emit RoleUpdated(role, account, approved); } /** * @notice Modifier to check if the caller has the specified role or is the owner * @param role The role to check */ modifier onlyRoleOrOwner(bytes32 role) { require( hasRole[role][msg.sender] || msg.sender == owner, "BaseERC7540/not-authorized" ); _; } /*////////////////////////////////////////////////////////////// PAUSING LOGIC //////////////////////////////////////////////////////////////*/ bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE"); /// @notice Pause Deposits. Caller must be owner or have the PAUSER_ROLE function pause() external override onlyRoleOrOwner(PAUSER_ROLE) { _pause(); } /// @notice Unpause Deposits. Caller must be owner function unpause() external override onlyRoleOrOwner(PAUSER_ROLE) { _unpause(); } /*////////////////////////////////////////////////////////////// ERC7540 LOGIC //////////////////////////////////////////////////////////////*/ /// @dev controller => operator => approved mapping(address => mapping(address => bool)) public isOperator; /** * @notice Set the approval status for an operator * @param operator The operator to set * @param approved The approval status to set * @dev Operators are approved to requestRedeem,withdraw and redeem for the msg.sender using the balance of msg.sender */ function setOperator( address operator, bool approved ) public virtual returns (bool success) { require( msg.sender != operator, "ERC7540Vault/cannot-set-self-as-operator" ); isOperator[msg.sender][operator] = approved; emit OperatorSet(msg.sender, operator, approved); success = true; } /*////////////////////////////////////////////////////////////// EIP-7441 LOGIC //////////////////////////////////////////////////////////////*/ mapping(address controller => mapping(bytes32 nonce => bool used)) public authorizations; /** * @notice Authorize an operator for a controller * @param controller The controller to authorize the operator for * @param operator The operator to authorize * @param approved The approval status to set * @param nonce The nonce to use for the authorization * @param deadline The deadline for the authorization * @param signature The signature to verify the authorization * @dev Operators are approved to requestRedeem,withdraw and redeem for the msg.sender using the balance of msg.sender */ function authorizeOperator( address controller, address operator, bool approved, bytes32 nonce, uint256 deadline, bytes memory signature ) public virtual returns (bool success) { require( controller != operator, "ERC7540Vault/cannot-set-self-as-operator" ); require(block.timestamp <= deadline, "ERC7540Vault/expired"); require( !authorizations[controller][nonce], "ERC7540Vault/authorization-used" ); authorizations[controller][nonce] = true; bytes32 r; bytes32 s; uint8 v; assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } require( uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ERC7540Vault/invalid-signature-s" ); address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "AuthorizeOperator(address controller,address operator,bool approved,bytes32 nonce,uint256 deadline)" ), controller, operator, approved, nonce, deadline ) ) ) ), v, r, s ); require( recoveredAddress != address(0) && recoveredAddress == controller, "INVALID_SIGNER" ); isOperator[controller][operator] = approved; emit OperatorSet(controller, operator, approved); success = true; } /*////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ /** * @notice Check if the contract supports an interface * @param interfaceId The interface ID to check * @return True if the contract supports the interface, false otherwise */ function supportsInterface( bytes4 interfaceId ) public pure virtual returns (bool) { return interfaceId == type(IERC7575).interfaceId || interfaceId == type(IERC7540Operator).interfaceId || interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; interface IERC7540Operator { /** * @dev The event emitted when an operator is set. * * @param controller The address of the controller. * @param operator The address of the operator. * @param approved The approval status. */ event OperatorSet(address indexed controller, address indexed operator, bool approved); /** * @dev Sets or removes an operator for the caller. * * @param operator The address of the operator. * @param approved The approval status. * @return Whether the call was executed successfully or not */ function setOperator(address operator, bool approved) external returns (bool); /** * @dev Returns `true` if the `operator` is approved as an operator for an `controller`. * * @param controller The address of the controller. * @param operator The address of the operator. * @return status The approval status */ function isOperator(address controller, address operator) external view returns (bool status); } interface IERC7540Deposit { event DepositRequest( address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets ); /** * @dev Transfers assets from sender into the Vault and submits a Request for asynchronous deposit. * * - MUST support ERC-20 approve / transferFrom on asset as a deposit Request flow. * - MUST revert if all of assets cannot be requested for deposit. * - owner MUST be msg.sender unless some unspecified explicit approval is given by the caller, * approval of ERC-20 tokens from owner to sender is NOT enough. * * @param assets the amount of deposit assets to transfer from owner * @param controller the controller of the request who will be able to operate the request * @param owner the source of the deposit assets * * NOTE: most implementations will require pre-approval of the Vault with the Vault's underlying asset token. */ function requestDeposit(uint256 assets, address controller, address owner) external returns (uint256 requestId); /** * @dev Returns the amount of requested assets in Pending state. * * - MUST NOT include any assets in Claimable state for deposit or mint. * - MUST NOT show any variations depending on the caller. * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. */ function pendingDepositRequest(uint256 requestId, address controller) external view returns (uint256 pendingAssets); /** * @dev Returns the amount of requested assets in Claimable state for the controller to deposit or mint. * * - MUST NOT include any assets in Pending state. * - MUST NOT show any variations depending on the caller. * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. */ function claimableDepositRequest(uint256 requestId, address controller) external view returns (uint256 claimableAssets); /** * @dev Mints shares Vault shares to receiver by claiming the Request of the controller. * * - MUST emit the Deposit event. * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator. */ function deposit(uint256 assets, address receiver, address controller) external returns (uint256 shares); /** * @dev Mints exactly shares Vault shares to receiver by claiming the Request of the controller. * * - MUST emit the Deposit event. * - controller MUST equal msg.sender unless the controller has approved the msg.sender as an operator. */ function mint(uint256 shares, address receiver, address controller) external returns (uint256 assets); } interface IERC7540Redeem { event RedeemRequest( address indexed controller, address indexed owner, uint256 indexed requestId, address sender, uint256 assets ); /** * @dev Assumes control of shares from sender into the Vault and submits a Request for asynchronous redeem. * * - MUST support a redeem Request flow where the control of shares is taken from sender directly * where msg.sender has ERC-20 approval over the shares of owner. * - MUST revert if all of shares cannot be requested for redeem. * * @param shares the amount of shares to be redeemed to transfer from owner * @param controller the controller of the request who will be able to operate the request * @param owner the source of the shares to be redeemed * * NOTE: most implementations will require pre-approval of the Vault with the Vault's share token. */ function requestRedeem(uint256 shares, address controller, address owner) external returns (uint256 requestId); /** * @dev Returns the amount of requested shares in Pending state. * * - MUST NOT include any shares in Claimable state for redeem or withdraw. * - MUST NOT show any variations depending on the caller. * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. */ function pendingRedeemRequest(uint256 requestId, address controller) external view returns (uint256 pendingShares); /** * @dev Returns the amount of requested shares in Claimable state for the controller to redeem or withdraw. * * - MUST NOT include any shares in Pending state for redeem or withdraw. * - MUST NOT show any variations depending on the caller. * - MUST NOT revert unless due to integer overflow caused by an unreasonably large input. */ function claimableRedeemRequest(uint256 requestId, address controller) external view returns (uint256 claimableShares); }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; /// @notice Minimal ERC4626 tokenized Vault implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC4626.sol) abstract contract ERC4626 is ERC20 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /*////////////////////////////////////////////////////////////// IMMUTABLES //////////////////////////////////////////////////////////////*/ ERC20 public immutable asset; constructor( ERC20 _asset, string memory _name, string memory _symbol ) ERC20(_name, _symbol, _asset.decimals()) { asset = _asset; } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LOGIC //////////////////////////////////////////////////////////////*/ function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function withdraw( uint256 assets, address receiver, address owner ) public virtual returns (uint256 shares) { shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } function redeem( uint256 shares, address receiver, address owner ) public virtual returns (uint256 assets) { if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } // Check for rounding error since we round down in previewRedeem. require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } /*////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ function totalAssets() public view virtual returns (uint256); function convertToShares(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); } function convertToAssets(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); } function previewDeposit(uint256 assets) public view virtual returns (uint256) { return convertToShares(assets); } function previewMint(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); } function previewWithdraw(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); } function previewRedeem(uint256 shares) public view virtual returns (uint256) { return convertToAssets(shares); } /*////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } function maxWithdraw(address owner) public view virtual returns (uint256) { return convertToAssets(balanceOf[owner]); } function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf[owner]; } /*////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/ function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} function afterDeposit(uint256 assets, uint256 shares) internal virtual {} }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() virtual { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } }
// SPDX-License-Identifier: GPL-3.0 // Docgen-SOLC: 0.8.25 pragma solidity ^0.8.25; // https://docs.synthetix.io/contracts/source/contracts/owned contract Owned { address public owner; address public nominatedOwner; event OwnerNominated(address newOwner); event OwnerChanged(address oldOwner, address newOwner); constructor(address _owner) { require(_owner != address(0), "Owned/owner-zero"); owner = _owner; emit OwnerChanged(address(0), _owner); } function nominateNewOwner(address _owner) external virtual onlyOwner { nominatedOwner = _owner; emit OwnerNominated(_owner); } function acceptOwnership() external virtual { require( msg.sender == nominatedOwner, "Owned/not-nominated" ); emit OwnerChanged(owner, nominatedOwner); owner = nominatedOwner; nominatedOwner = address(0); } modifier onlyOwner() { _onlyOwner(); _; } function _onlyOwner() private view { require( msg.sender == owner, "Owned/not-owner" ); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Gas optimized Pausable for smart contracts. /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Pausable.sol) abstract contract Pausable { bool public paused; event Paused(address account); event Unpaused(address account); modifier whenNotPaused() { _requireNotPaused(); _; } modifier whenPaused() { _requirePaused(); _; } function _requireNotPaused() internal view virtual { if (paused) { revert("Pausable/paused"); } } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { if (!paused) { revert("Pausable/not-paused"); } } function _pause() internal virtual whenNotPaused { paused = true; emit Paused(msg.sender); } function _unpause() internal virtual whenPaused { paused = false; emit Unpaused(msg.sender); } function pause() external virtual whenNotPaused { _pause(); } function unpause() external virtual whenPaused { _unpause(); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.5.0; /** * @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. */ 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); } interface IERC7575 is IERC165 { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the address of the share token * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function share() external view returns (address shareTokenAddress); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/", "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "solmate/=lib/solmate/src/", "safe-smart-account/=lib/safe-smart-account/contracts/", "weiroll/=lib/weiroll/contracts/", "solady/=lib/solady/src/", "bitlib/=lib/solidity-bytes-utils/contracts/", "ERC-7540/=lib/ERC-7540-Reference/src/", "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/", "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", "ERC-7540-Reference/=lib/ERC-7540-Reference/src/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "solidity-bytes-utils/=lib/solidity-bytes-utils/contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "shanghai", "viaIR": false, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"components":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"uint256","name":"depositLimit","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"internalType":"struct Limits","name":"limits","type":"tuple"},{"components":[{"internalType":"uint64","name":"performanceFee","type":"uint64"},{"internalType":"uint64","name":"managementFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalIncentive","type":"uint64"},{"internalType":"uint64","name":"feesUpdatedAt","type":"uint64"},{"internalType":"uint256","name":"highWaterMark","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct Fees","name":"fees","type":"tuple"}],"internalType":"struct InitializeParams","name":"params","type":"tuple"},{"internalType":"address","name":"oracle_","type":"address"},{"internalType":"address","name":"safe_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"InvalidFee","type":"error"},{"inputs":[],"name":"Misconfigured","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"upper","type":"uint256"},{"internalType":"uint256","name":"lower","type":"uint256"}],"indexed":false,"internalType":"struct Bounds","name":"prev","type":"tuple"},{"components":[{"internalType":"uint256","name":"upper","type":"uint256"},{"internalType":"uint256","name":"lower","type":"uint256"}],"indexed":false,"internalType":"struct Bounds","name":"next","type":"tuple"}],"name":"BoundsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint64","name":"performanceFee","type":"uint64"},{"internalType":"uint64","name":"managementFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalIncentive","type":"uint64"},{"internalType":"uint64","name":"feesUpdatedAt","type":"uint64"},{"internalType":"uint256","name":"highWaterMark","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"indexed":false,"internalType":"struct Fees","name":"prev","type":"tuple"},{"components":[{"internalType":"uint64","name":"performanceFee","type":"uint64"},{"internalType":"uint64","name":"managementFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalIncentive","type":"uint64"},{"internalType":"uint64","name":"feesUpdatedAt","type":"uint64"},{"internalType":"uint256","name":"highWaterMark","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"indexed":false,"internalType":"struct Fees","name":"next","type":"tuple"}],"name":"FeesUpdated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"depositLimit","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"indexed":false,"internalType":"struct Limits","name":"prev","type":"tuple"},{"components":[{"internalType":"uint256","name":"depositLimit","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"indexed":false,"internalType":"struct Limits","name":"next","type":"tuple"}],"name":"LimitsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"OperatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOracle","type":"address"},{"indexed":true,"internalType":"address","name":"newOracle","type":"address"}],"name":"OracleChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proposedOracle","type":"address"}],"name":"OracleProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"RedeemRequest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"RedeemRequestCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"address","name":"fulfiller","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"}],"name":"RedeemRequestFulfilled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"controller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"requestId","type":"uint256"},{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"RedeemRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":false,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"RoleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldSafe","type":"address"},{"indexed":true,"internalType":"address","name":"newSafe","type":"address"}],"name":"SafeChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"proposedSafe","type":"address"}],"name":"SafeProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accruedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"authorizations","outputs":[{"internalType":"bool","name":"used","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"},{"internalType":"bytes32","name":"nonce","type":"bytes32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"authorizeOperator","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bounds","outputs":[{"internalType":"uint256","name":"upper","type":"uint256"},{"internalType":"uint256","name":"lower","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"},{"internalType":"address","name":"receiver","type":"address"}],"name":"cancelRedeemRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"cancelRedeemRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"controller","type":"address"}],"name":"claimableRedeemRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"convertToLowBoundAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fees","outputs":[{"internalType":"uint64","name":"performanceFee","type":"uint64"},{"internalType":"uint64","name":"managementFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalIncentive","type":"uint64"},{"internalType":"uint64","name":"feesUpdatedAt","type":"uint64"},{"internalType":"uint256","name":"highWaterMark","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"shares","type":"uint256[]"},{"internalType":"address[]","name":"controllers","type":"address[]"}],"name":"fulfillMultipleRedeems","outputs":[{"internalType":"uint256","name":"total","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"controller","type":"address"}],"name":"fulfillRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBounds","outputs":[{"components":[{"internalType":"uint256","name":"upper","type":"uint256"},{"internalType":"uint256","name":"lower","type":"uint256"}],"internalType":"struct Bounds","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFees","outputs":[{"components":[{"internalType":"uint64","name":"performanceFee","type":"uint64"},{"internalType":"uint64","name":"managementFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalIncentive","type":"uint64"},{"internalType":"uint64","name":"feesUpdatedAt","type":"uint64"},{"internalType":"uint256","name":"highWaterMark","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct Fees","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"getRequestBalance","outputs":[{"components":[{"internalType":"uint256","name":"pendingShares","type":"uint256"},{"internalType":"uint256","name":"requestTime","type":"uint256"},{"internalType":"uint256","name":"claimableShares","type":"uint256"},{"internalType":"uint256","name":"claimableAssets","type":"uint256"}],"internalType":"struct RequestBalance","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"address","name":"","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"limits","outputs":[{"internalType":"uint256","name":"depositLimit","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"nominateNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominatedOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IPriceOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"controller","type":"address"}],"name":"pendingRedeemRequest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOracle","type":"address"}],"name":"proposeOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newSafe","type":"address"}],"name":"proposeSafe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proposedOracle","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"proposedSafe","outputs":[{"internalType":"address","name":"addr","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"requestBalances","outputs":[{"internalType":"uint256","name":"pendingShares","type":"uint256"},{"internalType":"uint256","name":"requestTime","type":"uint256"},{"internalType":"uint256","name":"claimableShares","type":"uint256"},{"internalType":"uint256","name":"claimableAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"controller","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"requestRedeem","outputs":[{"internalType":"uint256","name":"requestId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"requestRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"safe","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"upper","type":"uint256"},{"internalType":"uint256","name":"lower","type":"uint256"}],"internalType":"struct Bounds","name":"bounds_","type":"tuple"}],"name":"setBounds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"performanceFee","type":"uint64"},{"internalType":"uint64","name":"managementFee","type":"uint64"},{"internalType":"uint64","name":"withdrawalIncentive","type":"uint64"},{"internalType":"uint64","name":"feesUpdatedAt","type":"uint64"},{"internalType":"uint256","name":"highWaterMark","type":"uint256"},{"internalType":"address","name":"feeRecipient","type":"address"}],"internalType":"struct Fees","name":"fees_","type":"tuple"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"depositLimit","type":"uint256"},{"internalType":"uint256","name":"minAmount","type":"uint256"}],"internalType":"struct Limits","name":"limits_","type":"tuple"}],"name":"setLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setOperator","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"share","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"takeFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"updateRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"controller","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]
Deployed Bytecode
0x608060405234801561000f575f80fd5b506004361061044e575f3560e01c80637dc0d1d011610242578063b460af9411610140578063d905777e116100bf578063e63ab1e911610084578063e63ab1e914610c18578063e90baf1814610c3f578063eaed1d0714610c52578063ef8b30f714610c7f578063f5a23d8d14610c92575f80fd5b8063d905777e14610af8578063db006a7514610b23578063db8d55f114610b36578063db901a2a14610bdb578063dd62ed3e14610bee575f80fd5b8063c6e6f59211610105578063c6e6f59214610a67578063cdf5bba314610a7a578063ce95596014610aa7578063ce96cb7714610aba578063d505accf14610ae5575f80fd5b8063b460af94146109ee578063b6363cf214610a01578063b6b55f2514610a2e578063ba08765214610a41578063c63d75b614610a54575f80fd5b806395d89b41116101cc578063a8d5fd6511610191578063a8d5fd651461098a578063a9059cbb146109a2578063aa2f892d146109b5578063adc565e1146109c8578063b3d7f6b9146109db575f80fd5b806395d89b41146108b757806399998406146108bf5780639a6d1866146108d25780639af1d35a146108e9578063a0712d6814610977575f80fd5b80638456cb59116102125780638456cb591461084e578063860aefcf146108565780638da5cb5b1461086457806391d148541461087757806394bf804d146108a4575f80fd5b80637dc0d1d01461080c5780637ecebe001461081f5780637ed51b771461083e5780637f2d957514610846575f80fd5b80633644e5151161034f5780635c975abb116102d9578063711b58ff1161029e578063711b58ff1461076257806378d873281461077557806379ba5097146107ab5780637d41c86e146107b35780637d7b81b7146107c6575f80fd5b80635c975abb146107135780635dd912f514610720578063682c2058146107285780636e553f651461073057806370a0823114610743575f80fd5b8063402d267d1161031f578063402d267d146106b75780634cdad506146104e057806353a47bb7146106ca578063558a7297146106dd5780635843a5ad146106f0575f80fd5b80633644e5151461064c57806338d52e0f146106545780633cc87a201461067b5780633f4ba83a146106af575f80fd5b80631627540c116103db5780632aa38127116103a05780632aa38127146105c75780632d0777d8146105da5780632e1a7d4d146105ed578063313ce56714610600578063346080af14610639575f80fd5b80631627540c1461055a57806318160ddd1461056d578063186f03541461057657806323b872dd146105a157806326537b57146105b4575f80fd5b806307a2d13a1161042157806307a2d13a146104ba578063095ea7b3146104cd5780630a28a477146104e0578063126bd46c146104f3578063138e027c14610506575f80fd5b806301e1d1141461045257806301ffc9a71461046d578063025d7e331461049057806306fdde03146104a5575b5f80fd5b61045a610cbc565b6040519081526020015b60405180910390f35b61048061047b366004613881565b610d68565b6040519015158152602001610464565b6104a361049e366004613938565b610d92565b005b6104ad610da6565b6040516104649190613952565b61045a6104c836600461399e565b610e31565b6104806104db3660046139d0565b610e5d565b61045a6104ee36600461399e565b610ec8565b61045a6105013660046139f8565b610f17565b61053a610514366004613a22565b600d6020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610464565b6104a3610568366004613a22565b610fd7565b61045a60025481565b601554610589906001600160a01b031681565b6040516001600160a01b039091168152602001610464565b6104806105af366004613a3b565b611033565b61045a6105c236600461399e565b61110d565b6104a36105d5366004613a22565b611168565b6104a36105e8366004613a74565b611220565b61045a6105fb36600461399e565b61122e565b6106277f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610464565b6104a3610647366004613aab565b61123a565b61045a6112b9565b6105897f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a781565b6040805180820182525f8082526020918201528151808301909252600e548252600f54908201526040516104649190613ae4565b6104a361130e565b61045a6106c5366004613a22565b6113ca565b600754610589906001600160a01b031681565b6104806106eb366004613afb565b611419565b600e54600f546106fe919082565b60408051928352602083019190915201610464565b6009546104809060ff1681565b6104a36114a6565b61045a6114b8565b61045a61073e3660046139f8565b611529565b61045a610751366004613a22565b60036020525f908152604090205481565b610480610770366004613b23565b6115ec565b601954601a5461078c916001600160a01b03169082565b604080516001600160a01b039093168352602083019190915201610464565b6104a3611982565b61045a6107c1366004613bf3565b611a44565b6107d96107d4366004613a22565b611aa3565b60405161046491908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b601654610589906001600160a01b031681565b61045a61082d366004613a22565b60056020525f908152604090205481565b6104a3611b17565b6104a3611c6a565b6104a3611dbf565b6013546014546106fe919082565b600654610589906001600160a01b031681565b6104806108853660046139f8565b600a60209081525f928352604080842090915290825290205460ff1681565b61045a6108b23660046139f8565b611e7b565b6104ad611f3e565b6104a36108cd366004613a22565b611f4b565b60175460185461078c916001600160a01b03169082565b60105460115460125461092d926001600160401b0380821693600160401b8304821693600160801b8404831693600160c01b9004909216916001600160a01b031686565b604080516001600160401b0397881681529587166020870152938616938501939093529316606083015260808201929092526001600160a01b0390911660a082015260c001610464565b61045a61098536600461399e565b611f55565b6009546105899061010090046001600160a01b031681565b6104806109b03660046139d0565b611f60565b61045a6109c336600461399e565b611fc3565b6104a36109d6366004613a22565b611fcf565b61045a6109e936600461399e565b61208e565b61045a6109fc366004613bf3565b6120fc565b610480610a0f366004613a74565b600b60209081525f928352604080842090915290825290205460ff1681565b61045a610a3c36600461399e565b612250565b61045a610a4f366004613bf3565b61225b565b61045a610a62366004613a22565b61238d565b61045a610a7536600461399e565b6123d6565b610480610a883660046139d0565b600c60209081525f928352604080842090915290825290205460ff1681565b61045a610ab5366004613cb8565b6123f5565b61045a610ac8366004613a22565b6001600160a01b03165f908152600d602052604090206003015490565b6104a3610af3366004613d6a565b612591565b61045a610b06366004613a22565b6001600160a01b03165f908152600d602052604090206002015490565b61045a610b3136600461399e565b6127cf565b610bce6040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b9004909116606082015260115460808201526012546001600160a01b031660a082015290565b6040516104649190613e29565b6104a3610be9366004613e4d565b6127db565b61045a610bfc366004613a74565b600460209081525f928352604080842090915290825290205481565b61045a7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b6104a3610c4d366004613938565b6127fc565b61045a610c603660046139f8565b6001600160a01b03165f908152600d6020526040902060020154919050565b61045a610c8d36600461399e565b61288c565b61045a610ca03660046139f8565b6001600160a01b03165f908152600d6020526040902054919050565b601654600254600954604051632b9a19db60e21b8152600481019290925261010090046001600160a01b0390811660248301527f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a7811660448301525f92169063ae68676c90606401602060405180830381865afa158015610d3f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d639190613ee2565b905090565b5f6001600160e01b03198216631883ba3960e21b1480610d8c5750610d8c826128fa565b92915050565b610d9a61294a565b610da381612996565b50565b5f8054610db290613ef9565b80601f0160208091040260200160405190810160405280929190818152602001828054610dde90613ef9565b8015610e295780601f10610e0057610100808354040283529160200191610e29565b820191905f5260205f20905b815481529060010190602001808311610e0c57829003601f168201915b505050505081565b6002545f908015610e5457610e4f610e47610cbc565b849083612a65565b610e56565b825b9392505050565b335f8181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610eb79086815260200190565b60405180910390a350600192915050565b60405162461bcd60e51b815260206004820152601760248201527f455243373534305661756c742f6173796e632d666c6f7700000000000000000060448201525f906064015b60405180910390fd5b5f610f20612a80565b610f298361110d565b6040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b82048116938301849052600160c01b90910416606082015260115460808201526012546001600160a01b031660a08201529192505f90610fa0908490670de0b6b3a7640000612a65565b9050610fac3086612b94565b610fc0610fb98285613f45565b8686612bfb565b50610fcf818360a00151612d1a565b505092915050565b610fdf61294a565b600780546001600160a01b0319166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b6001600160a01b0383165f9081526004602090815260408083203384529091528120545f19811461108c576110688382613f45565b6001600160a01b0386165f9081526004602090815260408083203384529091529020555b6001600160a01b0385165f90815260036020526040812080548592906110b3908490613f45565b90915550506001600160a01b038085165f81815260036020526040908190208054870190555190918716905f80516020614286833981519152906110fa9087815260200190565b60405180910390a3506001949350505050565b600254600f545f919082906111469061112e90670de0b6b3a7640000613f45565b670de0b6b3a764000061113f610cbc565b9190612a65565b9050811561115e57611159848284612a65565b611160565b835b949350505050565b61117061294a565b6001600160a01b0381166111bf5760405162461bcd60e51b8152602060048201526016602482015275536166655661756c742f696e76616c69642d7361666560501b6044820152606401610f0e565b6040805180820182526001600160a01b038316808252426020909201829052601780546001600160a01b0319168217905560189190915590517f500119a4be3e95be870ef423be9332d25076b52ba5ca53b00f8fa823b61f6e2b905f90a250565b61122a8282612d59565b5050565b5f610d8c8233336120fc565b61124261294a565b5f838152600a602090815260408083206001600160a01b03861680855290835292819020805460ff19168515159081179091558151878152928301939093528101919091527f5f0ecfd1ea5555d5b4b6140b49c92365beaf40d0a057dc34a9746990cd4ce8d49060600160405180910390a1505050565b5f7f000000000000000000000000000000000000000000000000000000000000000146146112e957610d63612e71565b507f9934d0b5d1d560955ab23717544f90c4463a8955be6caf5da5253f39372af06090565b335f9081527f6c4ab3a3cc4fea3ac566afdaa38e0e471d1bdfd1aa59eb20affdabfa5e893fed60205260409020547f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a9060ff168061137657506006546001600160a01b031633145b6113c25760405162461bcd60e51b815260206004820152601a60248201527f42617365455243373534302f6e6f742d617574686f72697a65640000000000006044820152606401610f0e565b610da3612f09565b5f806113d4610cbc565b6013546009549192509060ff16156113ef57505f9392505050565b5f1981036113fe579392505050565b80821015611410576111598282613f45565b5f949350505050565b5f6001600160a01b03831633036114425760405162461bcd60e51b8152600401610f0e90613f58565b335f818152600b602090815260408083206001600160a01b03881680855290835292819020805460ff191687151590811790915590519081529192917fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa2679101610eb7565b6114ae612f51565b6114b6612a80565b565b6040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b9004909116606082015260115460808201526012546001600160a01b031660a08201525f9061152381612f96565b91505090565b5f611532612f51565b61153c8382612fb3565b6115458361288c565b9050805f036115665760405162461bcd60e51b8152600401610f0e90613fa0565b6115927f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a7333086612fbb565b61159c8282613053565b60408051848152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610d8c83826130a2565b5f856001600160a01b0316876001600160a01b03160361161e5760405162461bcd60e51b8152600401610f0e90613f58565b824211156116655760405162461bcd60e51b8152602060048201526014602482015273115490cdcd4d0c15985d5b1d0bd95e1c1a5c995960621b6044820152606401610f0e565b6001600160a01b0387165f908152600c6020908152604080832087845290915290205460ff16156116d85760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f617574686f72697a6174696f6e2d75736564006044820152606401610f0e565b6001600160a01b0387165f908152600c602090815260408083208784528252808320805460ff191660011790559084015190840151606085015191929091901a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156117885760405162461bcd60e51b815260206004820181905260248201527f455243373534305661756c742f696e76616c69642d7369676e61747572652d736044820152606401610f0e565b5f60016117936112b9565b7fa3efcf8cb518126a85cdfd1c1102ee539e0700189f80926e1ac37144450473fa8d8d8d8d8d6040516020016117ff969594939291909586526001600160a01b03948516602087015292909316604085015215156060840152608083019190915260a082015260c00190565b6040516020818303038152906040528051906020012060405160200161183c92919061190160f01b81526002810192909252602282015260420190565b60408051601f1981840301815282825280516020918201205f84529083018083525260ff851690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015611897573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116158015906118cd57508a6001600160a01b0316816001600160a01b0316145b61190a5760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610f0e565b6001600160a01b038b81165f818152600b60209081526040808320948f1680845294825291829020805460ff19168e151590811790915591519182527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a35060019a9950505050505050505050565b6007546001600160a01b031633146119d25760405162461bcd60e51b815260206004820152601360248201527213dddb99590bdb9bdd0b5b9bdb5a5b985d1959606a1b6044820152606401610f0e565b600654600754604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160078054600680546001600160a01b03199081166001600160a01b03841617909155169055565b6014545f90841015611a985760405162461bcd60e51b815260206004820152601760248201527f455243373534305661756c742f6d696e2d616d6f756e740000000000000000006044820152606401610f0e565b6111608484846130e2565b611aca60405180608001604052805f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152600d6020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b611b1f61294a565b604080518082019091526017546001600160a01b03168082526018546020830152611b8c5760405162461bcd60e51b815260206004820152601a60248201527f536166655661756c742f6e6f2d736166652d70726f706f7365640000000000006044820152606401610f0e565b4281602001516203f480611ba09190613fc5565b1115611bf85760405162461bcd60e51b815260206004820152602160248201527f536166655661756c742f736166652d6e6f742d7965742d61636365707461626c6044820152606560f81b6064820152608401610f0e565b80516015546040516001600160a01b0392831692909116907f260a9a29aa1c98abc86285a91c26a04cce10cb086152a137bf2f444ca24a45ca905f90a38051601580546001600160a01b039092166001600160a01b03199283161790556017805490911690555f601855610da36132e3565b611c7261294a565b604080518082019091526019546001600160a01b0316808252601a546020830152611cdf5760405162461bcd60e51b815260206004820152601c60248201527f536166655661756c742f6e6f2d6f7261636c652d70726f706f736564000000006044820152606401610f0e565b4281602001516203f480611cf39190613fc5565b1115611d4d5760405162461bcd60e51b815260206004820152602360248201527f536166655661756c742f6f7261636c652d6e6f742d7965742d61636365707461604482015262626c6560e81b6064820152608401610f0e565b80516016546040516001600160a01b0392831692909116907f05cd89403c6bdeac21c2ff33de395121a31fa1bc2bf3adf4825f1f86e79969dd905f90a38051601680546001600160a01b039092166001600160a01b03199283161790556019805490911690555f601a55610da36132e3565b335f9081527f6c4ab3a3cc4fea3ac566afdaa38e0e471d1bdfd1aa59eb20affdabfa5e893fed60205260409020547f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a9060ff1680611e2757506006546001600160a01b031633145b611e735760405162461bcd60e51b815260206004820152601a60248201527f42617365455243373534302f6e6f742d617574686f72697a65640000000000006044820152606401610f0e565b610da36132e3565b5f611e84612f51565b611e8e8184612fb3565b825f03611ead5760405162461bcd60e51b8152600401610f0e90613fa0565b611eb68361208e565b9050611ee47f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a7333084612fbb565b611eee8284613053565b60408051828152602081018590526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610d8c81846130a2565b60018054610db290613ef9565b610da38133612d59565b5f610d8c8233611e7b565b335f90815260036020526040812080548391908390611f80908490613f45565b90915550506001600160a01b0383165f81815260036020526040908190208054850190555133905f8051602061428683398151915290610eb79086815260200190565b5f610d8c823333611a44565b611fd761294a565b6001600160a01b03811661202d5760405162461bcd60e51b815260206004820152601860248201527f536166655661756c742f696e76616c69642d6f7261636c6500000000000000006044820152606401610f0e565b6040805180820182526001600160a01b038316808252426020909201829052601980546001600160a01b03191682179055601a9190915590517f0138b9794085eddff5afe429af2255342c44615c1c1f81ce93a1e38a476cee58905f90a250565b60408051808201909152601354815260145460208201525f90816120b184610e31565b60095490915060ff16806120d757508151816120cb610cbc565b6120d59190613fc5565b115b806120e55750816020015184105b156120f357505f9392505050565b61116084613328565b5f6001600160a01b03821633148061213657506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6121525760405162461bcd60e51b8152600401610f0e90613fd8565b835f0361218f5760405162461bcd60e51b815260206004820152600b60248201526a5a45524f5f41535345545360a81b6044820152606401610f0e565b6001600160a01b0382165f908152600d60205260409020600281015460038201546121bb918791613349565b91506121c7858261336c565b6121d185836133d2565b6121fc7f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a785876133e4565b60408051868152602081018490526001600160a01b03808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a45b509392505050565b5f610d8c8233611529565b5f6001600160a01b03821633148061229557506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6122b15760405162461bcd60e51b8152600401610f0e90613fd8565b835f036122d05760405162461bcd60e51b8152600401610f0e90613fa0565b6001600160a01b0382165f908152600d60205260409020600381015460028201546122fc918791612a65565b91506123088582613461565b61231282866133d2565b61233d7f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a785846133e4565b60408051838152602081018790526001600160a01b03808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4612248565b5f80612397610cbc565b6013546009549192509060ff16156123b257505f9392505050565b5f1981036123c1579392505050565b8082101561141057611159610a758383613f45565b6002545f908015610e5457610e4f816123ed610cbc565b859190612a65565b5f8151835114612418576040516321f9f13f60e11b815260040160405180910390fd5b612420612a80565b6040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b9004909116606082015260115460808201526012546001600160a01b031660a08201525f80805b865181101561256f575f6124b08883815181106124a3576124a361400f565b602002602001015161110d565b90505f6124dd86604001516001600160401b0316670de0b6b3a764000084612a659092919063ffffffff16565b90506125256124ec8284613f45565b8a85815181106124fe576124fe61400f565b60200260200101518a86815181106125185761251861400f565b6020026020010151612bfb565b506125308288613fc5565b965061253c8185613fc5565b93508883815181106125505761255061400f565b6020026020010151856125639190613fc5565b94505050600101612484565b5061257a3083612b94565b612588818460a00151612d1a565b50505092915050565b428410156125e15760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610f0e565b5f60016125ec6112b9565b6001600160a01b038a81165f8181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f1981840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156126f4573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b0381161580159061272a5750876001600160a01b0316816001600160a01b0316145b6127675760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610f0e565b6001600160a01b039081165f9081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b5f610d8c82333361225b565b6127e361294a565b6127eb612f51565b6127f3612a80565b610da3816134c7565b61280461294a565b6702c68af0bb14000081602001511180612826575080516702c68af0bb140000105b15612844576040516321f9f13f60e11b815260040160405180910390fd5b7f773cd47b12eabb5832b83bb94bc847b3e000c5b824fdf9da506cb9ecf9e1a53f600e82604051612876929190614023565b60405180910390a18051600e5560200151600f55565b60408051808201909152601354815260145460208201525f90816128af846123d6565b60095490915060ff16806128d557508151846128c9610cbc565b6128d39190613fc5565b115b806128e35750816020015181105b156128f157505f9392505050565b611160846136a3565b5f6001600160e01b03198216632f0a18c560e01b148061292a57506001600160e01b0319821663e3bc4e6560e01b145b80610d8c57506001600160e01b031982166301ffc9a760e01b1492915050565b6006546001600160a01b031633146114b65760405162461bcd60e51b815260206004820152600f60248201526e27bbb732b217b737ba16b7bbb732b960891b6044820152606401610f0e565b60025480158015906129af57506129ab610cbc565b8251105b156129cd576040516321f9f13f60e11b815260040160405180910390fd5b6129f87f0000000000000000000000000000000000000000000000000000000000000012600a61412b565b82602001511115612a1c576040516321f9f13f60e11b815260040160405180910390fd5b7ff455e80a7d5b3f148849bb4acc1e909fafb067275e80e335f966127410c9169f601383604051612a4e929190614023565b60405180910390a150805160135560200151601455565b5f825f190484118302158202612a79575f80fd5b5091020490565b6040805160c0810182526010546001600160401b038082168352600160401b820481166020840152600160801b8204811693830193909352600160c01b9004909116606082015260115460808201526012546001600160a01b031660a08201525f612aea826136b8565b90505f612af683613770565b90505f612b276104c87f0000000000000000000000000000000000000000000000000000000000000012600a61412b565b90505f612b348385613fc5565b1115612b555760a0840151612b5590612b50610a758587613fc5565b613053565b8360800151811115612b675760118190555b8115612b8e57601080546001600160c01b0316600160c01b426001600160401b0316021790555b50505050565b6001600160a01b0382165f9081526003602052604081208054839290612bbb908490613f45565b90915550506002805482900390556040518181525f906001600160a01b038416905f80516020614286833981519152906020015b60405180910390a35050565b5f831580612c07575082155b15612c245760405162461bcd60e51b8152600401610f0e90613fa0565b6001600160a01b0382165f908152600d60205260409020805415801590612c4c575080548411155b612c685760405162461bcd60e51b8152600401610f0e90613fa0565b612c7285856137d8565b83816002015f828254612c859190613fc5565b9250508190555084816003015f828254612c9f9190613fc5565b90915550508054849082905f90612cb7908490613f45565b909155505080545f03612ccb575f60018201555b604080518581526020810187905233916001600160a01b038616917f24111f527e6debb0efcfd4c847fc0ae4d8858cdfc72cc2fce0e757a3fce414f7910160405180910390a350929392505050565b811561122a5760155461122a907f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a7906001600160a01b03168385612fbb565b6001600160a01b038216331480612d9257506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b612dae5760405162461bcd60e51b8152600401610f0e90613fd8565b6001600160a01b0382165f908152600d60205260409020805480612e145760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f6e6f2d70656e64696e672d72657175657374006044820152606401610f0e565b612e1f3084836133e4565b5f80835560018301556040518181526001600160a01b0384811691908616907f756776540459f688c5059ee7547eed3bfaf8143dc14677d8fcef3007aceec9fc9060200160405180910390a350505050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f604051612ea19190614139565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b612f11613811565b6009805460ff191690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60095460ff16156114b65760405162461bcd60e51b815260206004820152600f60248201526e14185d5cd8589b194bdc185d5cd959608a1b6044820152606401610f0e565b5f612fa082613770565b612fa9836136b8565b610d8c9190613fc5565b61122a612a80565b5f6040516323b872dd60e01b81526001600160a01b03851660048201526001600160a01b038416602482015282604482015260205f6064835f8a5af13d15601f3d1160015f51141617169150508061304c5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610f0e565b5050505050565b8060025f8282546130649190613fc5565b90915550506001600160a01b0382165f818152600360209081526040808320805486019055518481525f805160206142868339815191529101612bef565b6130aa612a80565b60155461122a907f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a7906001600160a01b0316846133e4565b5f6001600160a01b03821633148061311c57506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6131685760405162461bcd60e51b815260206004820152601a60248201527f455243373534305661756c742f696e76616c69642d6f776e65720000000000006044820152606401610f0e565b6040516370a0823160e01b81526001600160a01b0383166004820152849030906370a0823190602401602060405180830381865afa1580156131ac573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906131d09190613ee2565b10156132285760405162461bcd60e51b815260206004820152602160248201527f455243373534305661756c742f696e73756666696369656e742d62616c616e636044820152606560f81b6064820152608401610f0e565b835f036132475760405162461bcd60e51b8152600401610f0e90613fa0565b61325330833087612fbb565b6001600160a01b0383165f908152600d6020526040812080549091869183919061327e908490613fc5565b9091555050426001820155604080515f81523360208201529081018690526001600160a01b0384811691908616907f1e7ddf69e1e9242eb5635deb06ffd4691a8962ec488a863a26721288c88985519060600160405180910390a3505f949350505050565b6132eb612f51565b6009805460ff191660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25890602001612f47565b6009545f9060ff166133425761333d82613859565b610d8c565b5f92915050565b5f825f19048411830215820261335d575f80fd5b50910281810615159190040190565b5f61338a82600201548360030154856133499092919063ffffffff16565b905082826003015f82825461339f9190613f45565b9091555050600282015481106133b5575f6133c5565b8082600201546133c59190613f45565b8260020181905550505050565b60095460ff1661122a5761122a612a80565b5f60405163a9059cbb60e01b81526001600160a01b038416600482015282602482015260205f6044835f895af13d15601f3d1160015f511416171691505080612b8e5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610f0e565b5f61347f82600301548360020154856133499092919063ffffffff16565b905080826003015411613492575f6134a2565b8082600301546134a29190613f45565b826003018190555082826002015f8282546134bd9190613f45565b9091555050505050565b6702c68af0bb140000815f01516001600160401b031611806134fc575066b1a2bc2ec5000081602001516001600160401b0316115b8061351a575066b1a2bc2ec5000081604001516001600160401b0316115b15613538576040516321f9f13f60e11b815260040160405180910390fd5b60a08101516001600160a01b0316613563576040516321f9f13f60e11b815260040160405180910390fd5b6001600160401b03421660608201526011545f036135b3576135a96104c87f0000000000000000000000000000000000000000000000000000000000000012600a61412b565b60808201526135bc565b60115460808201525b7faf0d4a9793f0cb3dd1b01241953e01dc6eb6515b9e5cd623f1b3cad0fc1fa4566010826040516135ee9291906141d7565b60405180910390a18051601080546020840151604085015160608601516001600160401b03908116600160c01b026001600160c01b03928216600160801b02929092166fffffffffffffffffffffffffffffffff938216600160401b026fffffffffffffffffffffffffffffffff199095169190961617929092171692909217919091179055608081015160115560a00151601280546001600160a01b039092166001600160a01b0319909216919091179055565b6009545f9060ff166133425761333d82613877565b5f806136e86104c87f0000000000000000000000000000000000000000000000000000000000000012600a61412b565b83519091506001600160401b031680158015906137085750836080015182115b613712575f611160565b6111606002548560800151846137289190613f45565b6137329190614236565b61375d7f0000000000000000000000000000000000000000000000000000000000000012601261424d565b61376890600a61412b565b839190613349565b60208101515f906001600160401b03168061378b575f610e56565b670de0b6b3a76400006137ce84606001516001600160401b0316426137b09190613f45565b6137b8610cbc565b6137c29190614236565b83906301e13380612a65565b610e569190614266565b60155461122a907f000000000000000000000000a1290d69c65a6fe4df752f95823fae25cb99e5a7906001600160a01b03163085612fbb565b60095460ff166114b65760405162461bcd60e51b815260206004820152601360248201527214185d5cd8589b194bdb9bdd0b5c185d5cd959606a1b6044820152606401610f0e565b6002545f908015610e5457610e4f61386f610cbc565b849083613349565b5f610d8c826123d6565b5f60208284031215613891575f80fd5b81356001600160e01b031981168114610e56575f80fd5b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b03811182821017156138e4576138e46138a8565b604052919050565b5f604082840312156138fc575f80fd5b604051604081018181106001600160401b038211171561391e5761391e6138a8565b604052823581526020928301359281019290925250919050565b5f60408284031215613948575f80fd5b610e5683836138ec565b5f602080835283518060208501525f5b8181101561397e57858101830151858201604001528201613962565b505f604082860101526040601f19601f8301168501019250505092915050565b5f602082840312156139ae575f80fd5b5035919050565b80356001600160a01b03811681146139cb575f80fd5b919050565b5f80604083850312156139e1575f80fd5b6139ea836139b5565b946020939093013593505050565b5f8060408385031215613a09575f80fd5b82359150613a19602084016139b5565b90509250929050565b5f60208284031215613a32575f80fd5b610e56826139b5565b5f805f60608486031215613a4d575f80fd5b613a56846139b5565b9250613a64602085016139b5565b9150604084013590509250925092565b5f8060408385031215613a85575f80fd5b613a8e836139b5565b9150613a19602084016139b5565b803580151581146139cb575f80fd5b5f805f60608486031215613abd575f80fd5b83359250613acd602085016139b5565b9150613adb60408501613a9c565b90509250925092565b815181526020808301519082015260408101610d8c565b5f8060408385031215613b0c575f80fd5b613b15836139b5565b9150613a1960208401613a9c565b5f805f805f8060c08789031215613b38575f80fd5b613b41876139b5565b95506020613b508189016139b5565b9550613b5e60408901613a9c565b9450606088013593506080880135925060a08801356001600160401b0380821115613b87575f80fd5b818a0191508a601f830112613b9a575f80fd5b813581811115613bac57613bac6138a8565b613bbe601f8201601f191685016138bc565b91508082528b84828501011115613bd3575f80fd5b80848401858401375f848284010152508093505050509295509295509295565b5f805f60608486031215613c05575f80fd5b83359250613c15602085016139b5565b9150613adb604085016139b5565b5f6001600160401b03821115613c3b57613c3b6138a8565b5060051b60200190565b5f82601f830112613c54575f80fd5b81356020613c69613c6483613c23565b6138bc565b8083825260208201915060208460051b870101935086841115613c8a575f80fd5b602086015b84811015613cad57613ca0816139b5565b8352918301918301613c8f565b509695505050505050565b5f8060408385031215613cc9575f80fd5b82356001600160401b0380821115613cdf575f80fd5b818501915085601f830112613cf2575f80fd5b81356020613d02613c6483613c23565b82815260059290921b84018101918181019089841115613d20575f80fd5b948201945b83861015613d3e57853582529482019490820190613d25565b96505086013592505080821115613d53575f80fd5b50613d6085828601613c45565b9150509250929050565b5f805f805f805f60e0888a031215613d80575f80fd5b613d89886139b5565b9650613d97602089016139b5565b95506040880135945060608801359350608088013560ff81168114613dba575f80fd5b9699959850939692959460a0840135945060c09093013592915050565b80516001600160401b039081168352602080830151821690840152604080830151821690840152606080830151909116908301526080808201519083015260a0908101516001600160a01b0316910152565b60c08101610d8c8284613dd7565b80356001600160401b03811681146139cb575f80fd5b5f60c08284031215613e5d575f80fd5b60405160c081018181106001600160401b0382111715613e7f57613e7f6138a8565b604052613e8b83613e37565b8152613e9960208401613e37565b6020820152613eaa60408401613e37565b6040820152613ebb60608401613e37565b606082015260808301356080820152613ed660a084016139b5565b60a08201529392505050565b5f60208284031215613ef2575f80fd5b5051919050565b600181811c90821680613f0d57607f821691505b602082108103613f2b57634e487b7160e01b5f52602260045260245ffd5b50919050565b634e487b7160e01b5f52601160045260245ffd5b81810381811115610d8c57610d8c613f31565b60208082526028908201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60408201526737b832b930ba37b960c11b606082015260800190565b6020808252600b908201526a5a45524f5f53484152455360a81b604082015260600190565b80820180821115610d8c57610d8c613f31565b6020808252601b908201527f455243373534305661756c742f696e76616c69642d63616c6c65720000000000604082015260600190565b634e487b7160e01b5f52603260045260245ffd5b8254815260018301546020820152608081018251604083015260208301516060830152610e56565b600181815b8085111561408557815f190482111561406b5761406b613f31565b8085161561407857918102915b93841c9390800290614050565b509250929050565b5f8261409b57506001610d8c565b816140a757505f610d8c565b81600181146140bd57600281146140c7576140e3565b6001915050610d8c565b60ff8411156140d8576140d8613f31565b50506001821b610d8c565b5060208310610133831016604e8410600b8410161715614106575081810a610d8c565b614110838361404b565b805f190482111561412357614123613f31565b029392505050565b5f610e5660ff84168361408d565b5f8083545f60018260011c9150600183168061415657607f831692505b6020808410820361417557634e487b7160e01b5f52602260045260245ffd5b818015614189576001811461419e576141c9565b60ff19861689528415158502890196506141c9565b5f8a8152602090205f5b868110156141c15781548b8201529085019083016141a8565b505084890196505b509498975050505050505050565b82546001600160401b038082168352604082811c82166020850152608083811c9092169084015260c091821c606084015260018501549083015260028401546001600160a01b031660a0830152610180820190610e5690830184613dd7565b8082028115828204841417610d8c57610d8c613f31565b60ff8181168382160190811115610d8c57610d8c613f31565b5f8261428057634e487b7160e01b5f52601260045260245ffd5b50049056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa26469706673582212209c14c0e8a86a3948dcf26b4d006615c614b8edeaab13a6a0e487ccbeeda0fe3764736f6c63430008190033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.