ERC-20
Overview
Max Total Supply
8 sbvUSD
Holders
3
Market
Price
$0.00 @ 0.000000 ETH
Onchain Market Cap
-
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Loading...
Loading
Loading...
Loading
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
OracleVault
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 20000 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: 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); /** * @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 returns (uint256); /** * @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 receiver */ function cancelRedeemRequest( address controller ) external; }
// 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); }
// 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.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 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: 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: 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: 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; // 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: 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: 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; } }
{ "evmVersion": "shanghai", "libraries": {}, "metadata": { "appendCBOR": true, "bytecodeHash": "ipfs", "useLiteralContent": false }, "optimizer": { "enabled": true, "runs": 20000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "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/", "@pyth/=lib/euler-price-oracle/lib/pyth-sdk-solidity/", "@redstone/evm-connector/=lib/euler-price-oracle/lib/redstone-oracles-monorepo/packages/evm-connector/contracts/", "@solady/=lib/euler-price-oracle/lib/solady/src/", "@uniswap/v3-core/=lib/euler-price-oracle/lib/v3-core/", "@uniswap/v3-periphery/=lib/euler-price-oracle/lib/v3-periphery/", "ERC-7540-Reference/=lib/ERC-7540-Reference/src/", "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/", "euler-price-oracle/=lib/euler-price-oracle/src/", "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/", "openzeppelin/=lib/euler-price-oracle/lib/openzeppelin-contracts/contracts/", "pyth-sdk-solidity/=lib/euler-price-oracle/lib/pyth-sdk-solidity/", "redstone-oracles-monorepo/=lib/euler-price-oracle/lib/", "solidity-bytes-utils/=lib/solidity-bytes-utils/contracts/", "v3-core/=lib/v3-core/", "v3-periphery/=lib/v3-periphery/contracts/" ], "viaIR": false }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"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"}]
Contract Creation Code
6101006040819052600160085560098054610100600160a81b03191630909202919091179055348015610030575f5ffd5b50604051615a68380380615a6883398101604081905261004f9161089c565b828060600151815f015182602001518360400151838383838181846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100a5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100c99190610998565b5f6100d48482610a3c565b5060016100e18382610a3c565b5060ff81166080524660a0526100f561023f565b60c0525050506001600160a01b0392831660e052505081166101505760405162461bcd60e51b815260206004820152601060248201526f4f776e65642f6f776e65722d7a65726f60801b604482015260640160405180910390fd5b600680546001600160a01b0319166001600160a01b038316908117909155604080515f815260208101929092527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a150505050506101c081608001516102d760201b60201c565b60a08101516101ce9061039e565b506001600160a01b03811615806101ec57506001600160a01b038216155b1561020a576040516321f9f13f60e11b815260040160405180910390fd5b601580546001600160a01b039283166001600160a01b0319918216179091556016805493909216921691909117905550610c85565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f60405161026f9190610af6565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60025480158015906102f057506102ec6105e5565b8251105b1561030e576040516321f9f13f60e11b815260040160405180910390fd5b60805161031c90600a610c60565b82602001511115610340576040516321f9f13f60e11b815260040160405180910390fd5b604080516013548152601454602080830191909152845182840152840151606082015290517ff455e80a7d5b3f148849bb4acc1e909fafb067275e80e335f966127410c9169f9181900360800190a150805160135560200151601455565b6702c68af0bb140000815f01516001600160401b031611806103d3575066b1a2bc2ec5000081602001516001600160401b0316115b806103f1575066b1a2bc2ec5000081604001516001600160401b0316115b1561040f576040516321f9f13f60e11b815260040160405180910390fd5b60a08101516001600160a01b031661043a576040516321f9f13f60e11b815260040160405180910390fd5b6001600160401b03421660608201526011545f0361047357610469608051600a6104649190610c60565b610673565b608082015261047c565b60115460808201525b604080516010546001600160401b03808216835281841c8116602080850191909152608083811c83168587015260c093841c606080870191909152601154828701526012546001600160a01b0390811660a080890191909152895186169688019690965292880151841660e087015287870151841661010087015287015190921661012085015290850151610140840152908401511661016082015290517faf0d4a9793f0cb3dd1b01241953e01dc6eb6515b9e5cd623f1b3cad0fc1fa456918190036101800190a18051601080546020840151604085015160608601516001600160401b03908116600160c01b026001600160c01b03928216600160801b02929092166001600160801b0393821668010000000000000000026001600160801b03199095169190961617929092171692909217919091179055608081015160115560a00151601280546001600160a01b039092166001600160a01b0319909216919091179055565b60165460025460095460e051604051632b9a19db60e21b815260048101939093526001600160a01b0361010090920482166024840152811660448301525f92169063ae68676c90606401602060405180830381865afa15801561064a573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061066e9190610c6e565b905090565b6002545f908015610696576106916106896105e5565b84908361069f565b610698565b825b9392505050565b5f825f1904841183021582026106b3575f5ffd5b5091020490565b634e487b7160e01b5f52604160045260245ffd5b60405160c081016001600160401b03811182821017156106f0576106f06106ba565b60405290565b604051601f8201601f191681016001600160401b038111828210171561071e5761071e6106ba565b604052919050565b80516001600160a01b038116811461073c575f5ffd5b919050565b5f82601f830112610750575f5ffd5b81516001600160401b03811115610769576107696106ba565b61077c601f8201601f19166020016106f6565b818152846020838601011115610790575f5ffd5b5f5b828110156107ae57602081860181015183830182015201610792565b505f918101602001919091529392505050565b5f604082840312156107d1575f5ffd5b604080519081016001600160401b03811182821017156107f3576107f36106ba565b604052825181526020928301519281019290925250919050565b80516001600160401b038116811461073c575f5ffd5b5f60c08284031215610833575f5ffd5b61083b6106ce565b90506108468261080d565b81526108546020830161080d565b60208201526108656040830161080d565b60408201526108766060830161080d565b60608201526080828101519082015261089160a08301610726565b60a082015292915050565b5f5f5f606084860312156108ae575f5ffd5b83516001600160401b038111156108c3575f5ffd5b840161018081870312156108d5575f5ffd5b6108dd6106ce565b6108e682610726565b815260208201516001600160401b03811115610900575f5ffd5b61090c88828501610741565b60208301525060408201516001600160401b0381111561092a575f5ffd5b61093688828501610741565b60408301525061094860608301610726565b606082015261095a87608084016107c1565b608082015261096c8760c08401610823565b60a08201529350610981905060208501610726565b915061098f60408501610726565b90509250925092565b5f602082840312156109a8575f5ffd5b815160ff81168114610698575f5ffd5b600181811c908216806109cc57607f821691505b6020821081036109ea57634e487b7160e01b5f52602260045260245ffd5b50919050565b601f821115610a3757805f5260205f20601f840160051c81016020851015610a155750805b601f840160051c820191505b81811015610a34575f8155600101610a21565b50505b505050565b81516001600160401b03811115610a5557610a556106ba565b610a6981610a6384546109b8565b846109f0565b6020601f821160018114610a9b575f8315610a845750848201515b5f19600385901b1c1916600184901b178455610a34565b5f84815260208120601f198516915b82811015610aca5787850151825560209485019460019092019101610aaa565b5084821015610ae757868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b5f5f8354610b03816109b8565b600182168015610b1a5760018114610b2f57610b5c565b60ff1983168652811515820286019350610b5c565b865f5260205f205f5b83811015610b5457815488820152600190910190602001610b38565b505081860193505b509195945050505050565b634e487b7160e01b5f52601160045260245ffd5b6001815b6001841115610bb657808504811115610b9a57610b9a610b67565b6001841615610ba857908102905b60019390931c928002610b7f565b935093915050565b5f82610bcc57506001610c5a565b81610bd857505f610c5a565b8160018114610bee5760028114610bf857610c14565b6001915050610c5a565b60ff841115610c0957610c09610b67565b50506001821b610c5a565b5060208310610133831016604e8410600b8410161715610c37575081810a610c5a565b610c435f198484610b7b565b805f1904821115610c5657610c56610b67565b0290505b92915050565b5f61069860ff841683610bbe565b5f60208284031215610c7e575f5ffd5b5051919050565b60805160a05160c05160e051614d55610d135f395f818161071c01528181610e1e0152818161182501528181612330015281816126b601528181612857015281816134bf015281816138da015261414b01525f6114da01525f6114aa01525f81816106c80152818161308d015281816131fd01528181613e8a01528181614029015261409f0152614d555ff3fe608060405234801561000f575f5ffd5b5060043610610511575f3560e01c80637dc0d1d0116102ab578063b460af941161017c578063d905777e116100dd578063e63ab1e911610093578063eaed1d0711610079578063eaed1d0714610d66578063ef8b30f714610d93578063f5a23d8d14610da6575f5ffd5b8063e63ab1e914610d2c578063e90baf1814610d53575f5ffd5b8063db8d55f1116100c3578063db8d55f114610c22578063db901a2a14610cef578063dd62ed3e14610d02575f5ffd5b8063d905777e14610be4578063db006a7514610c0f575f5ffd5b8063c6e6f59211610132578063ce95596011610118578063ce95596014610b93578063ce96cb7714610ba6578063d505accf14610bd1575f5ffd5b8063c6e6f59214610b53578063cdf5bba314610b66575f5ffd5b8063b6b55f2511610162578063b6b55f2514610b1a578063ba08765214610b2d578063c63d75b614610b40575f5ffd5b8063b460af9414610ada578063b6363cf214610aed575f5ffd5b806395d89b4111610226578063a8d5fd65116101dc578063aa2f892d116101c2578063aa2f892d14610aa1578063adc565e114610ab4578063b3d7f6b914610ac7575f5ffd5b8063a8d5fd6514610a76578063a9059cbb14610a8e575f5ffd5b80639a6d18661161020c5780639a6d1866146109955780639af1d35a146109ac578063a0712d6814610a63575f5ffd5b806395d89b411461097a5780639999840614610982575f5ffd5b80638456cb591161027b5780638da5cb5b116102615780638da5cb5b1461092757806391d148541461093a57806394bf804d14610967575f5ffd5b80638456cb5914610911578063860aefcf14610919575f5ffd5b80637dc0d1d0146108cf5780637ecebe00146108e25780637ed51b77146109015780637f2d957514610909575f5ffd5b80633644e515116103e55780635c975abb11610360578063711b58ff1161031657806379ba5097116102fc57806379ba50971461086e5780637d41c86e146108765780637d7b81b714610889575f5ffd5b8063711b58ff1461082557806378d8732814610838575f5ffd5b8063682c205811610346578063682c2058146107eb5780636e553f65146107f357806370a0823114610806575f5ffd5b80635c975abb146107d65780635dd912f5146107e3575f5ffd5b8063402d267d116103b557806353a47bb71161039b57806353a47bb71461078d578063558a7297146107a05780635843a5ad146107b3575f5ffd5b8063402d267d1461077a5780634cdad506146105a3575f5ffd5b80633644e5151461070f57806338d52e0f146107175780633cc87a201461073e5780633f4ba83a14610772575f5ffd5b80631627540c1161048f5780632aa38127116104455780632e1a7d4d1161042b5780632e1a7d4d146106b0578063313ce567146106c3578063346080af146106fc575f5ffd5b80632aa381271461068a5780632d0777d81461069d575f5ffd5b8063186f035411610475578063186f03541461063957806323b872dd1461066457806326537b5714610677575f5ffd5b80631627540c1461061d57806318160ddd14610630575f5ffd5b806307a2d13a116104e45780630a28a477116104ca5780630a28a477146105a3578063126bd46c146105b6578063138e027c146105c9575f5ffd5b806307a2d13a1461057d578063095ea7b314610590575f5ffd5b806301e1d1141461051557806301ffc9a714610530578063025d7e331461055357806306fdde0314610568575b5f5ffd5b61051d610dd0565b6040519081526020015b60405180910390f35b61054361053e3660046141f5565b610e95565b6040519015158152602001610527565b6105666105613660046142fd565b610ef0565b005b610570610f04565b6040516105279190614317565b61051d61058b366004614380565b610f8f565b61054361059e3660046143b2565b610fbb565b61051d6105b1366004614380565b611026565b61051d6105c43660046143da565b611075565b6105fd6105d7366004614404565b600d6020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610527565b61056661062b366004614404565b61115d565b61051d60025481565b60155461064c906001600160a01b031681565b6040516001600160a01b039091168152602001610527565b61054361067236600461441d565b6111d1565b61051d610685366004614380565b6112be565b610566610698366004614404565b611319565b6105666106ab366004614457565b6113f0565b61051d6106be366004614380565b6113fe565b6106ea7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610527565b61056661070a36600461448e565b61140a565b61051d6114a7565b61064c7f000000000000000000000000000000000000000000000000000000000000000081565b6040805180820182525f8082526020918201528151808301909252600e548252600f549082015260405161052791906144c7565b6105666114fc565b61051d610788366004614404565b6115b8565b60075461064c906001600160a01b031681565b6105436107ae3660046144de565b611607565b600e54600f546107c1919082565b60408051928352602083019190915201610527565b6009546105439060ff1681565b610566611708565b61051d61171a565b61051d6108013660046143da565b6117b3565b61051d610814366004614404565b60036020525f908152604090205481565b610543610833366004614506565b6118a6565b601954601a5461084f916001600160a01b03169082565b604080516001600160a01b039093168352602083019190915201610527565b610566611d3d565b61051d6108843660046145fa565b611e21565b61089c610897366004614404565b611e80565b60405161052791908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b60165461064c906001600160a01b031681565b61051d6108f0366004614404565b60056020525f908152604090205481565b610566611ef4565b61056661207b565b610566612202565b6013546014546107c1919082565b60065461064c906001600160a01b031681565b6105436109483660046143da565b600a60209081525f928352604080842090915290825290205460ff1681565b61051d6109753660046143da565b6122be565b6105706123b1565b610566610990366004614404565b6123be565b60175460185461084f916001600160a01b03169082565b601054601154601254610a189267ffffffffffffffff80821693680100000000000000008304821693700100000000000000000000000000000000840483169378010000000000000000000000000000000000000000000000009004909216916001600160a01b031686565b6040805167ffffffffffffffff97881681529587166020870152938616938501939093529316606083015260808201929092526001600160a01b0390911660a082015260c001610527565b61051d610a71366004614380565b6123c8565b60095461064c9061010090046001600160a01b031681565b610543610a9c3660046143b2565b6123d3565b61051d610aaf366004614380565b612449565b610566610ac2366004614404565b612455565b61051d610ad5366004614380565b61252c565b61051d610ae83660046145fa565b61259a565b610543610afb366004614457565b600b60209081525f928352604080842090915290825290205460ff1681565b61051d610b28366004614380565b612730565b61051d610b3b3660046145fa565b61273b565b61051d610b4e366004614404565b6128cd565b61051d610b61366004614380565b612916565b610543610b743660046143b2565b600c60209081525f928352604080842090915290825290205460ff1681565b61051d610ba13660046146be565b612935565b61051d610bb4366004614404565b6001600160a01b03165f908152600d602052604090206003015490565b610566610bdf36600461477a565b612b13565b61051d610bf2366004614404565b6001600160a01b03165f908152600d602052604090206002015490565b61051d610c1d366004614380565b612db7565b610ce26040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c08101825260105467ffffffffffffffff808216835268010000000000000000820481166020840152700100000000000000000000000000000000820481169383019390935278010000000000000000000000000000000000000000000000009004909116606082015260115460808201526012546001600160a01b031660a082015290565b60405161052791906147e7565b610566610cfd366004614872565b612dc3565b61051d610d10366004614457565b600460209081525f928352604080842090915290825290205481565b61051d7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610566610d613660046142fd565b612de4565b61051d610d743660046143da565b6001600160a01b03165f908152600d6020526040902060020154919050565b61051d610da1366004614380565b612e8d565b61051d610db43660046143da565b6001600160a01b03165f908152600d6020526040902054919050565b6016546002546009546040517fae68676c000000000000000000000000000000000000000000000000000000008152600481019290925261010090046001600160a01b0390811660248301527f0000000000000000000000000000000000000000000000000000000000000000811660448301525f92169063ae68676c90606401602060405180830381865afa158015610e6c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e90919061490a565b905090565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167fe9fcb88e000000000000000000000000000000000000000000000000000000001480610eea5750610eea82612efb565b92915050565b610ef8612fde565b610f0181613038565b50565b5f8054610f1090614921565b80601f0160208091040260200160405190810160405280929190818152602001828054610f3c90614921565b8015610f875780601f10610f5e57610100808354040283529160200191610f87565b820191905f5260205f20905b815481529060010190602001808311610f6a57829003601f168201915b505050505081565b6002545f908015610fb257610fad610fa5610dd0565b849083613139565b610fb4565b825b9392505050565b335f8181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906110159086815260200190565b60405180910390a350600192915050565b60405162461bcd60e51b815260206004820152601760248201527f455243373534305661756c742f6173796e632d666c6f7700000000000000000060448201525f906064015b60405180910390fd5b5f61107e613154565b611087836112be565b6040805160c08101825260105467ffffffffffffffff80821683526801000000000000000082048116602084015270010000000000000000000000000000000082048116938301849052780100000000000000000000000000000000000000000000000090910416606082015260115460808201526012546001600160a01b031660a08201529192505f90611126908490670de0b6b3a7640000613139565b905061113230866132b7565b61114661113f828561499f565b8686613331565b50611155818360a001516134b0565b505092915050565b611165612fde565b600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b6001600160a01b0383165f9081526004602090815260408083203384529091528120545f19811461122a57611206838261499f565b6001600160a01b0386165f9081526004602090815260408083203384529091529020555b6001600160a01b0385165f908152600360205260408120805485929061125190849061499f565b90915550506001600160a01b038085165f81815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906112ab9087815260200190565b60405180910390a3506001949350505050565b600254600f545f919082906112f7906112df90670de0b6b3a764000061499f565b670de0b6b3a76400006112f0610dd0565b9190613139565b9050811561130f5761130a848284613139565b611311565b835b949350505050565b611321612fde565b6001600160a01b0381166113775760405162461bcd60e51b815260206004820152601660248201527f536166655661756c742f696e76616c69642d7361666500000000000000000000604482015260640161106c565b6040805180820182526001600160a01b038316808252426020909201829052601780547fffffffffffffffffffffffff0000000000000000000000000000000000000000168217905560189190915590517f500119a4be3e95be870ef423be9332d25076b52ba5ca53b00f8fa823b61f6e2b905f90a250565b6113fa82826134ef565b5050565b5f610eea82333361259a565b611412612fde565b5f838152600a602090815260408083206001600160a01b0386168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558151878152928301939093528101919091527f5f0ecfd1ea5555d5b4b6140b49c92365beaf40d0a057dc34a9746990cd4ce8d49060600160405180910390a1505050565b5f7f000000000000000000000000000000000000000000000000000000000000000046146114d757610e90613637565b507f000000000000000000000000000000000000000000000000000000000000000090565b335f9081527f6c4ab3a3cc4fea3ac566afdaa38e0e471d1bdfd1aa59eb20affdabfa5e893fed60205260409020547f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a9060ff168061156457506006546001600160a01b031633145b6115b05760405162461bcd60e51b815260206004820152601a60248201527f42617365455243373534302f6e6f742d617574686f72697a6564000000000000604482015260640161106c565b610f016136cf565b5f5f6115c2610dd0565b6013546009549192509060ff16156115dd57505f9392505050565b5f1981036115ec579392505050565b808210156115fe5761130a828261499f565b5f949350505050565b5f6001600160a01b03831633036116865760405162461bcd60e51b815260206004820152602860248201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60448201527f6f70657261746f72000000000000000000000000000000000000000000000000606482015260840161106c565b335f818152600b602090815260408083206001600160a01b0388168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001687151590811790915590519081529192917fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa2679101611015565b611710613735565b611718613154565b565b6040805160c08101825260105467ffffffffffffffff808216835268010000000000000000820481166020840152700100000000000000000000000000000000820481169383019390935278010000000000000000000000000000000000000000000000009004909116606082015260115460808201526012546001600160a01b031660a08201525f906117ad81613788565b91505090565b5f6117bc613735565b6117c683826137a5565b6117cf83612e8d565b9050805f036118205760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b61184c7f00000000000000000000000000000000000000000000000000000000000000003330866137ad565b6118568282613867565b60408051848152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610eea83826138c9565b5f856001600160a01b0316876001600160a01b03160361192e5760405162461bcd60e51b815260206004820152602860248201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60448201527f6f70657261746f72000000000000000000000000000000000000000000000000606482015260840161106c565b8242111561197e5760405162461bcd60e51b815260206004820152601460248201527f455243373534305661756c742f65787069726564000000000000000000000000604482015260640161106c565b6001600160a01b0387165f908152600c6020908152604080832087845290915290205460ff16156119f15760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f617574686f72697a6174696f6e2d7573656400604482015260640161106c565b6001600160a01b0387165f908152600c60209081526040808320878452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559084015190840151606085015191929091901a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115611abf5760405162461bcd60e51b815260206004820181905260248201527f455243373534305661756c742f696e76616c69642d7369676e61747572652d73604482015260640161106c565b5f6001611aca6114a7565b7fa3efcf8cb518126a85cdfd1c1102ee539e0700189f80926e1ac37144450473fa8d8d8d8d8d604051602001611b36969594939291909586526001600160a01b03948516602087015292909316604085015215156060840152608083019190915260a082015260c00190565b60405160208183030381529060405280519060200120604051602001611b8e9291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201205f84529083018083525260ff851690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015611c07573d5f5f3e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615801590611c5b57508a6001600160a01b0316816001600160a01b0316145b611ca75760405162461bcd60e51b815260206004820152600e60248201527f494e56414c49445f5349474e4552000000000000000000000000000000000000604482015260640161106c565b6001600160a01b038b81165f818152600b60209081526040808320948f168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168e151590811790915591519182527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a35060019a9950505050505050505050565b6007546001600160a01b03163314611d975760405162461bcd60e51b815260206004820152601360248201527f4f776e65642f6e6f742d6e6f6d696e6174656400000000000000000000000000604482015260640161106c565b600654600754604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160078054600680547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b03841617909155169055565b6014545f90841015611e755760405162461bcd60e51b815260206004820152601760248201527f455243373534305661756c742f6d696e2d616d6f756e74000000000000000000604482015260640161106c565b611311848484613909565b611ea760405180608001604052805f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152600d6020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b611efc612fde565b604080518082019091526017546001600160a01b03168082526018546020830152611f695760405162461bcd60e51b815260206004820152601a60248201527f536166655661756c742f6e6f2d736166652d70726f706f736564000000000000604482015260640161106c565b4281602001516203f480611f7d91906149b2565b1115611ff15760405162461bcd60e51b815260206004820152602160248201527f536166655661756c742f736166652d6e6f742d7965742d61636365707461626c60448201527f6500000000000000000000000000000000000000000000000000000000000000606482015260840161106c565b80516015546040516001600160a01b0392831692909116907f260a9a29aa1c98abc86285a91c26a04cce10cb086152a137bf2f444ca24a45ca905f90a38051601580546001600160a01b039092167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790556017805490911690555f601855610f01613b6f565b612083612fde565b604080518082019091526019546001600160a01b0316808252601a5460208301526120f05760405162461bcd60e51b815260206004820152601c60248201527f536166655661756c742f6e6f2d6f7261636c652d70726f706f73656400000000604482015260640161106c565b4281602001516203f48061210491906149b2565b11156121785760405162461bcd60e51b815260206004820152602360248201527f536166655661756c742f6f7261636c652d6e6f742d7965742d6163636570746160448201527f626c650000000000000000000000000000000000000000000000000000000000606482015260840161106c565b80516016546040516001600160a01b0392831692909116907f05cd89403c6bdeac21c2ff33de395121a31fa1bc2bf3adf4825f1f86e79969dd905f90a38051601680546001600160a01b039092167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790556019805490911690555f601a55610f01613b6f565b335f9081527f6c4ab3a3cc4fea3ac566afdaa38e0e471d1bdfd1aa59eb20affdabfa5e893fed60205260409020547f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a9060ff168061226a57506006546001600160a01b031633145b6122b65760405162461bcd60e51b815260206004820152601a60248201527f42617365455243373534302f6e6f742d617574686f72697a6564000000000000604482015260640161106c565b610f01613b6f565b5f6122c7613735565b6122d181846137a5565b825f036123205760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b6123298361252c565b90506123577f00000000000000000000000000000000000000000000000000000000000000003330846137ad565b6123618284613867565b60408051828152602081018590526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610eea81846138c9565b60018054610f1090614921565b610f0181336134ef565b5f610eea82336122be565b335f908152600360205260408120805483919083906123f390849061499f565b90915550506001600160a01b0383165f81815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906110159086815260200190565b5f610eea823333611e21565b61245d612fde565b6001600160a01b0381166124b35760405162461bcd60e51b815260206004820152601860248201527f536166655661756c742f696e76616c69642d6f7261636c650000000000000000604482015260640161106c565b6040805180820182526001600160a01b038316808252426020909201829052601980547fffffffffffffffffffffffff00000000000000000000000000000000000000001682179055601a9190915590517f0138b9794085eddff5afe429af2255342c44615c1c1f81ce93a1e38a476cee58905f90a250565b60408051808201909152601354815260145460208201525f908161254f84610f8f565b60095490915060ff16806125755750815181612569610dd0565b61257391906149b2565b115b806125835750816020015184105b1561259157505f9392505050565b61131184613bd2565b5f6001600160a01b0382163314806125d457506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6126205760405162461bcd60e51b815260206004820152601b60248201527f455243373534305661756c742f696e76616c69642d63616c6c65720000000000604482015260640161106c565b835f0361266f5760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f415353455453000000000000000000000000000000000000000000604482015260640161106c565b6001600160a01b0382165f908152600d602052604090206002810154600382015461269b918791613bf3565b91506126a78582613c16565b6126b18583613c7c565b6126dc7f00000000000000000000000000000000000000000000000000000000000000008587613c8e565b60408051868152602081018490526001600160a01b03808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a45b509392505050565b5f610eea82336117b3565b5f6001600160a01b03821633148061277557506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6127c15760405162461bcd60e51b815260206004820152601b60248201527f455243373534305661756c742f696e76616c69642d63616c6c65720000000000604482015260640161106c565b835f036128105760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b6001600160a01b0382165f908152600d602052604090206003810154600282015461283c918791613139565b91506128488582613d32565b6128528286613c7c565b61287d7f00000000000000000000000000000000000000000000000000000000000000008584613c8e565b60408051838152602081018790526001600160a01b03808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4612728565b5f5f6128d7610dd0565b6013546009549192509060ff16156128f257505f9392505050565b5f198103612901579392505050565b808210156115fe5761130a610b61838361499f565b6002545f908015610fb257610fad8161292d610dd0565b859190613139565b5f8151835114612971576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612979613154565b6040805160c08101825260105467ffffffffffffffff808216835268010000000000000000820481166020840152700100000000000000000000000000000000820481169383019390935278010000000000000000000000000000000000000000000000009004909116606082015260115460808201526012546001600160a01b031660a08201525f80805b8651811015612af1575f612a31888381518110612a2457612a246149c5565b60200260200101516112be565b90505f612a5f866040015167ffffffffffffffff16670de0b6b3a7640000846131399092919063ffffffff16565b9050612aa7612a6e828461499f565b8a8581518110612a8057612a806149c5565b60200260200101518a8681518110612a9a57612a9a6149c5565b6020026020010151613331565b50612ab282886149b2565b9650612abe81856149b2565b9350888381518110612ad257612ad26149c5565b602002602001015185612ae591906149b2565b94505050600101612a05565b50612afc30836132b7565b612b0a818460a001516134b0565b50505092915050565b42841015612b635760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f45585049524544000000000000000000604482015260640161106c565b5f6001612b6e6114a7565b6001600160a01b038a81165f8181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612caf573d5f5f3e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615801590612d035750876001600160a01b0316816001600160a01b0316145b612d4f5760405162461bcd60e51b815260206004820152600e60248201527f494e56414c49445f5349474e4552000000000000000000000000000000000000604482015260640161106c565b6001600160a01b039081165f9081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b5f610eea82333361273b565b612dcb612fde565b612dd3613735565b612ddb613154565b610f0181613d98565b612dec612fde565b6702c68af0bb14000081602001511180612e0e575080516702c68af0bb140000105b15612e45576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f773cd47b12eabb5832b83bb94bc847b3e000c5b824fdf9da506cb9ecf9e1a53f600e82604051612e779291906149f2565b60405180910390a18051600e5560200151600f55565b60408051808201909152601354815260145460208201525f9081612eb084612916565b60095490915060ff1680612ed65750815184612eca610dd0565b612ed491906149b2565b115b80612ee45750816020015181105b15612ef257505f9392505050565b6113118461400a565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f2f0a18c5000000000000000000000000000000000000000000000000000000001480612f8d57507fffffffff0000000000000000000000000000000000000000000000000000000082167fe3bc4e6500000000000000000000000000000000000000000000000000000000145b80610eea57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001492915050565b6006546001600160a01b031633146117185760405162461bcd60e51b815260206004820152600f60248201527f4f776e65642f6e6f742d6f776e65720000000000000000000000000000000000604482015260640161106c565b6002548015801590613051575061304d610dd0565b8251105b15613088576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6130b37f0000000000000000000000000000000000000000000000000000000000000000600a614afd565b826020015111156130f0576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7ff455e80a7d5b3f148849bb4acc1e909fafb067275e80e335f966127410c9169f6013836040516131229291906149f2565b60405180910390a150805160135560200151601455565b5f825f19048411830215820261314d575f5ffd5b5091020490565b6040805160c08101825260105467ffffffffffffffff808216835268010000000000000000820481166020840152700100000000000000000000000000000000820481169383019390935278010000000000000000000000000000000000000000000000009004909116606082015260115460808201526012546001600160a01b031660a08201525f6131e68261401f565b90505f6131f2836140d8565b90505f61322361058b7f0000000000000000000000000000000000000000000000000000000000000000600a614afd565b90505f61323083856149b2565b11156132515760a08401516132519061324c610b6185876149b2565b613867565b83608001518111156132635760118190555b81156132b1576010805477ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000004267ffffffffffffffff16021790555b50505050565b6001600160a01b0382165f90815260036020526040812080548392906132de90849061499f565b90915550506002805482900390556040518181525f906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b5f83158061333d575082155b1561338a5760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b6001600160a01b0382165f908152600d602052604090208054158015906133b2575080548411155b6133fe5760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b6134088585614142565b83816002015f82825461341b91906149b2565b9250508190555084816003015f82825461343591906149b2565b90915550508054849082905f9061344d90849061499f565b909155505080545f03613461575f60018201555b604080518581526020810187905233916001600160a01b038616917f24111f527e6debb0efcfd4c847fc0ae4d8858cdfc72cc2fce0e757a3fce414f7910160405180910390a350929392505050565b81156113fa576015546113fa907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b031683856137ad565b6001600160a01b03821633148061352857506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6135745760405162461bcd60e51b815260206004820152601b60248201527f455243373534305661756c742f696e76616c69642d63616c6c65720000000000604482015260640161106c565b6001600160a01b0382165f908152600d602052604090208054806135da5760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f6e6f2d70656e64696e672d7265717565737400604482015260640161106c565b6135e5308483613c8e565b5f80835560018301556040518181526001600160a01b0384811691908616907f756776540459f688c5059ee7547eed3bfaf8143dc14677d8fcef3007aceec9fc9060200160405180910390a350505050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f6040516136679190614b0b565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6136d761417b565b600980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60095460ff16156117185760405162461bcd60e51b815260206004820152600f60248201527f5061757361626c652f7061757365640000000000000000000000000000000000604482015260640161106c565b5f613792826140d8565b61379b8361401f565b610eea91906149b2565b6113fa613154565b5f6040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526001600160a01b038416602482015282604482015260205f6064835f8a5af13d15601f3d1160015f5114161716915050806138605760405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000604482015260640161106c565b5050505050565b8060025f82825461387891906149b2565b90915550506001600160a01b0382165f818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101613325565b6138d1613154565b6015546113fa907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b031684613c8e565b5f6001600160a01b03821633148061394357506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b61398f5760405162461bcd60e51b815260206004820152601a60248201527f455243373534305661756c742f696e76616c69642d6f776e6572000000000000604482015260640161106c565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152849030906370a0823190602401602060405180830381865afa1580156139ec573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613a10919061490a565b1015613a845760405162461bcd60e51b815260206004820152602160248201527f455243373534305661756c742f696e73756666696369656e742d62616c616e6360448201527f6500000000000000000000000000000000000000000000000000000000000000606482015260840161106c565b835f03613ad35760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b613adf308330876137ad565b6001600160a01b0383165f908152600d60205260408120805490918691839190613b0a9084906149b2565b9091555050426001820155604080515f81523360208201529081018690526001600160a01b0384811691908616907f1e7ddf69e1e9242eb5635deb06ffd4691a8962ec488a863a26721288c88985519060600160405180910390a3505f949350505050565b613b77613735565b600980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2589060200161372b565b6009545f9060ff16613bec57613be7826141cd565b610eea565b5f92915050565b5f825f190484118302158202613c07575f5ffd5b50910281810615159190040190565b5f613c348260020154836003015485613bf39092919063ffffffff16565b905082826003015f828254613c49919061499f565b909155505060028201548110613c5f575f613c6f565b808260020154613c6f919061499f565b8260020181905550505050565b60095460ff166113fa576113fa613154565b5f6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015282602482015260205f6044835f895af13d15601f3d1160015f5114161716915050806132b15760405162461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c45440000000000000000000000000000000000604482015260640161106c565b5f613d508260030154836002015485613bf39092919063ffffffff16565b905080826003015411613d63575f613d73565b808260030154613d73919061499f565b826003018190555082826002015f828254613d8e919061499f565b9091555050505050565b6702c68af0bb140000815f015167ffffffffffffffff161180613dcf575066b1a2bc2ec50000816020015167ffffffffffffffff16115b80613dee575066b1a2bc2ec50000816040015167ffffffffffffffff16115b15613e25576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60a08101516001600160a01b0316613e69576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff421660608201526011545f03613eba57613eb061058b7f0000000000000000000000000000000000000000000000000000000000000000600a614afd565b6080820152613ec3565b60115460808201525b7faf0d4a9793f0cb3dd1b01241953e01dc6eb6515b9e5cd623f1b3cad0fc1fa456601082604051613ef5929190614bde565b60405180910390a180516010805460208401516040850151606086015167ffffffffffffffff90811678010000000000000000000000000000000000000000000000000277ffffffffffffffffffffffffffffffffffffffffffffffff92821670010000000000000000000000000000000002929092166fffffffffffffffffffffffffffffffff93821668010000000000000000027fffffffffffffffffffffffffffffffff000000000000000000000000000000009095169190961617929092171692909217919091179055608081015160115560a00151601280546001600160a01b039092167fffffffffffffffffffffffff0000000000000000000000000000000000000000909216919091179055565b6009545f9060ff16613bec57613be7826141eb565b5f8061404f61058b7f0000000000000000000000000000000000000000000000000000000000000000600a614afd565b835190915067ffffffffffffffff1680158015906140705750836080015182115b61407a575f611311565b611311600254856080015184614090919061499f565b61409a9190614cb7565b6140c57f00000000000000000000000000000000000000000000000000000000000000006012614cce565b6140d090600a614afd565b839190613bf3565b60208101515f9067ffffffffffffffff16806140f4575f610fb4565b670de0b6b3a7640000614138846060015167ffffffffffffffff164261411a919061499f565b614122610dd0565b61412c9190614cb7565b83906301e13380613139565b610fb49190614ce7565b6015546113fa907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b031630856137ad565b60095460ff166117185760405162461bcd60e51b815260206004820152601360248201527f5061757361626c652f6e6f742d70617573656400000000000000000000000000604482015260640161106c565b6002545f908015610fb257610fad6141e3610dd0565b849083613bf3565b5f610eea82612916565b5f60208284031215614205575f5ffd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610fb4575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156142a8576142a8614234565b604052919050565b5f604082840312156142c0575f5ffd5b6040805190810167ffffffffffffffff811182821017156142e3576142e3614234565b604052823581526020928301359281019290925250919050565b5f6040828403121561430d575f5ffd5b610fb483836142b0565b602081525f82518060208401525f5b818110156143435760208186018101516040868401015201614326565b505f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b5f60208284031215614390575f5ffd5b5035919050565b80356001600160a01b03811681146143ad575f5ffd5b919050565b5f5f604083850312156143c3575f5ffd5b6143cc83614397565b946020939093013593505050565b5f5f604083850312156143eb575f5ffd5b823591506143fb60208401614397565b90509250929050565b5f60208284031215614414575f5ffd5b610fb482614397565b5f5f5f6060848603121561442f575f5ffd5b61443884614397565b925061444660208501614397565b929592945050506040919091013590565b5f5f60408385031215614468575f5ffd5b61447183614397565b91506143fb60208401614397565b803580151581146143ad575f5ffd5b5f5f5f606084860312156144a0575f5ffd5b833592506144b060208501614397565b91506144be6040850161447f565b90509250925092565b815181526020808301519082015260408101610eea565b5f5f604083850312156144ef575f5ffd5b6144f883614397565b91506143fb6020840161447f565b5f5f5f5f5f5f60c0878903121561451b575f5ffd5b61452487614397565b955061453260208801614397565b94506145406040880161447f565b9350606087013592506080870135915060a087013567ffffffffffffffff811115614569575f5ffd5b8701601f81018913614579575f5ffd5b803567ffffffffffffffff81111561459357614593614234565b6145c460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614261565b8181528a60208385010111156145d8575f5ffd5b816020840160208301375f602083830101528093505050509295509295509295565b5f5f5f6060848603121561460c575f5ffd5b8335925061461c60208501614397565b91506144be60408501614397565b5f67ffffffffffffffff82111561464357614643614234565b5060051b60200190565b5f82601f83011261465c575f5ffd5b813561466f61466a8261462a565b614261565b8082825260208201915060208360051b860101925085831115614690575f5ffd5b602085015b838110156146b4576146a681614397565b835260209283019201614695565b5095945050505050565b5f5f604083850312156146cf575f5ffd5b823567ffffffffffffffff8111156146e5575f5ffd5b8301601f810185136146f5575f5ffd5b803561470361466a8261462a565b8082825260208201915060208360051b850101925087831115614724575f5ffd5b6020840193505b8284101561474657833582526020938401939091019061472b565b9450505050602083013567ffffffffffffffff811115614764575f5ffd5b6147708582860161464d565b9150509250929050565b5f5f5f5f5f5f5f60e0888a031215614790575f5ffd5b61479988614397565b96506147a760208901614397565b95506040880135945060608801359350608088013560ff811681146147ca575f5ffd5b9699959850939692959460a0840135945060c09093013592915050565b60c08101610eea828467ffffffffffffffff815116825267ffffffffffffffff602082015116602083015267ffffffffffffffff604082015116604083015267ffffffffffffffff6060820151166060830152608081015160808301526001600160a01b0360a08201511660a08301525050565b803567ffffffffffffffff811681146143ad575f5ffd5b5f60c0828403128015614883575f5ffd5b5060405160c0810167ffffffffffffffff811182821017156148a7576148a7614234565b6040526148b38361485b565b81526148c16020840161485b565b60208201526148d26040840161485b565b60408201526148e36060840161485b565b6060820152608083810135908201526148fe60a08401614397565b60a08201529392505050565b5f6020828403121561491a575f5ffd5b5051919050565b600181811c9082168061493557607f821691505b60208210810361496c577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610eea57610eea614972565b80820180821115610eea57610eea614972565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b8254815260018301546020820152608081018251604083015260208301516060830152610fb4565b6001815b6001841115614a5557808504811115614a3957614a39614972565b6001841615614a4757908102905b60019390931c928002614a1e565b935093915050565b5f82614a6b57506001610eea565b81614a7757505f610eea565b8160018114614a8d5760028114614a9757614ab3565b6001915050610eea565b60ff841115614aa857614aa8614972565b50506001821b610eea565b5060208310610133831016604e8410600b8410161715614ad6575081810a610eea565b614ae25f198484614a1a565b805f1904821115614af557614af5614972565b029392505050565b5f610fb460ff841683614a5d565b5f5f83545f8160011c90506001821680614b2657607f821691505b602082108103614b5d577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b808015614b715760018114614ba457614bd2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0084168752821515830287019450614bd2565b5f888152602090205f5b84811015614bca57815489820152600190910190602001614bae565b505082870194505b50929695505050505050565b5f61018082019050835467ffffffffffffffff8116835267ffffffffffffffff8160401c16602084015267ffffffffffffffff8160801c1660408401528060c01c606084015250600184015460808301526001600160a01b03806002860154161660a0830152610fb460c083018467ffffffffffffffff815116825267ffffffffffffffff602082015116602083015267ffffffffffffffff604082015116604083015267ffffffffffffffff6060820151166060830152608081015160808301526001600160a01b0360a08201511660a08301525050565b8082028115828204841417610eea57610eea614972565b60ff8181168382160190811115610eea57610eea614972565b5f82614d1a577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b50049056fea264697066735822122030cf38f2f7b836050afcf01fc1d0fa6936ff135a2e885041e6482d6df05107aa64736f6c634300081c00330000000000000000000000000000000000000000000000000000000000000060000000000000000000000000b2a4e3b4843adea2abdcc02aa9db8261fd9323ad000000000000000000000000452dc676b4e377a76b4b3048eb3b511a0f1ec057000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df1000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000919d5a6f2cbc0731380c26b4ac4f6183dd3a40c8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000919d5a6f2cbc0731380c26b4ac4f6183dd3a40c80000000000000000000000000000000000000000000000000000000000000006736276555344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067362765553440000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561000f575f5ffd5b5060043610610511575f3560e01c80637dc0d1d0116102ab578063b460af941161017c578063d905777e116100dd578063e63ab1e911610093578063eaed1d0711610079578063eaed1d0714610d66578063ef8b30f714610d93578063f5a23d8d14610da6575f5ffd5b8063e63ab1e914610d2c578063e90baf1814610d53575f5ffd5b8063db8d55f1116100c3578063db8d55f114610c22578063db901a2a14610cef578063dd62ed3e14610d02575f5ffd5b8063d905777e14610be4578063db006a7514610c0f575f5ffd5b8063c6e6f59211610132578063ce95596011610118578063ce95596014610b93578063ce96cb7714610ba6578063d505accf14610bd1575f5ffd5b8063c6e6f59214610b53578063cdf5bba314610b66575f5ffd5b8063b6b55f2511610162578063b6b55f2514610b1a578063ba08765214610b2d578063c63d75b614610b40575f5ffd5b8063b460af9414610ada578063b6363cf214610aed575f5ffd5b806395d89b4111610226578063a8d5fd65116101dc578063aa2f892d116101c2578063aa2f892d14610aa1578063adc565e114610ab4578063b3d7f6b914610ac7575f5ffd5b8063a8d5fd6514610a76578063a9059cbb14610a8e575f5ffd5b80639a6d18661161020c5780639a6d1866146109955780639af1d35a146109ac578063a0712d6814610a63575f5ffd5b806395d89b411461097a5780639999840614610982575f5ffd5b80638456cb591161027b5780638da5cb5b116102615780638da5cb5b1461092757806391d148541461093a57806394bf804d14610967575f5ffd5b80638456cb5914610911578063860aefcf14610919575f5ffd5b80637dc0d1d0146108cf5780637ecebe00146108e25780637ed51b77146109015780637f2d957514610909575f5ffd5b80633644e515116103e55780635c975abb11610360578063711b58ff1161031657806379ba5097116102fc57806379ba50971461086e5780637d41c86e146108765780637d7b81b714610889575f5ffd5b8063711b58ff1461082557806378d8732814610838575f5ffd5b8063682c205811610346578063682c2058146107eb5780636e553f65146107f357806370a0823114610806575f5ffd5b80635c975abb146107d65780635dd912f5146107e3575f5ffd5b8063402d267d116103b557806353a47bb71161039b57806353a47bb71461078d578063558a7297146107a05780635843a5ad146107b3575f5ffd5b8063402d267d1461077a5780634cdad506146105a3575f5ffd5b80633644e5151461070f57806338d52e0f146107175780633cc87a201461073e5780633f4ba83a14610772575f5ffd5b80631627540c1161048f5780632aa38127116104455780632e1a7d4d1161042b5780632e1a7d4d146106b0578063313ce567146106c3578063346080af146106fc575f5ffd5b80632aa381271461068a5780632d0777d81461069d575f5ffd5b8063186f035411610475578063186f03541461063957806323b872dd1461066457806326537b5714610677575f5ffd5b80631627540c1461061d57806318160ddd14610630575f5ffd5b806307a2d13a116104e45780630a28a477116104ca5780630a28a477146105a3578063126bd46c146105b6578063138e027c146105c9575f5ffd5b806307a2d13a1461057d578063095ea7b314610590575f5ffd5b806301e1d1141461051557806301ffc9a714610530578063025d7e331461055357806306fdde0314610568575b5f5ffd5b61051d610dd0565b6040519081526020015b60405180910390f35b61054361053e3660046141f5565b610e95565b6040519015158152602001610527565b6105666105613660046142fd565b610ef0565b005b610570610f04565b6040516105279190614317565b61051d61058b366004614380565b610f8f565b61054361059e3660046143b2565b610fbb565b61051d6105b1366004614380565b611026565b61051d6105c43660046143da565b611075565b6105fd6105d7366004614404565b600d6020525f908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610527565b61056661062b366004614404565b61115d565b61051d60025481565b60155461064c906001600160a01b031681565b6040516001600160a01b039091168152602001610527565b61054361067236600461441d565b6111d1565b61051d610685366004614380565b6112be565b610566610698366004614404565b611319565b6105666106ab366004614457565b6113f0565b61051d6106be366004614380565b6113fe565b6106ea7f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610527565b61056661070a36600461448e565b61140a565b61051d6114a7565b61064c7f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df181565b6040805180820182525f8082526020918201528151808301909252600e548252600f549082015260405161052791906144c7565b6105666114fc565b61051d610788366004614404565b6115b8565b60075461064c906001600160a01b031681565b6105436107ae3660046144de565b611607565b600e54600f546107c1919082565b60408051928352602083019190915201610527565b6009546105439060ff1681565b610566611708565b61051d61171a565b61051d6108013660046143da565b6117b3565b61051d610814366004614404565b60036020525f908152604090205481565b610543610833366004614506565b6118a6565b601954601a5461084f916001600160a01b03169082565b604080516001600160a01b039093168352602083019190915201610527565b610566611d3d565b61051d6108843660046145fa565b611e21565b61089c610897366004614404565b611e80565b60405161052791908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b60165461064c906001600160a01b031681565b61051d6108f0366004614404565b60056020525f908152604090205481565b610566611ef4565b61056661207b565b610566612202565b6013546014546107c1919082565b60065461064c906001600160a01b031681565b6105436109483660046143da565b600a60209081525f928352604080842090915290825290205460ff1681565b61051d6109753660046143da565b6122be565b6105706123b1565b610566610990366004614404565b6123be565b60175460185461084f916001600160a01b03169082565b601054601154601254610a189267ffffffffffffffff80821693680100000000000000008304821693700100000000000000000000000000000000840483169378010000000000000000000000000000000000000000000000009004909216916001600160a01b031686565b6040805167ffffffffffffffff97881681529587166020870152938616938501939093529316606083015260808201929092526001600160a01b0390911660a082015260c001610527565b61051d610a71366004614380565b6123c8565b60095461064c9061010090046001600160a01b031681565b610543610a9c3660046143b2565b6123d3565b61051d610aaf366004614380565b612449565b610566610ac2366004614404565b612455565b61051d610ad5366004614380565b61252c565b61051d610ae83660046145fa565b61259a565b610543610afb366004614457565b600b60209081525f928352604080842090915290825290205460ff1681565b61051d610b28366004614380565b612730565b61051d610b3b3660046145fa565b61273b565b61051d610b4e366004614404565b6128cd565b61051d610b61366004614380565b612916565b610543610b743660046143b2565b600c60209081525f928352604080842090915290825290205460ff1681565b61051d610ba13660046146be565b612935565b61051d610bb4366004614404565b6001600160a01b03165f908152600d602052604090206003015490565b610566610bdf36600461477a565b612b13565b61051d610bf2366004614404565b6001600160a01b03165f908152600d602052604090206002015490565b61051d610c1d366004614380565b612db7565b610ce26040805160c0810182525f80825260208201819052918101829052606081018290526080810182905260a0810191909152506040805160c08101825260105467ffffffffffffffff808216835268010000000000000000820481166020840152700100000000000000000000000000000000820481169383019390935278010000000000000000000000000000000000000000000000009004909116606082015260115460808201526012546001600160a01b031660a082015290565b60405161052791906147e7565b610566610cfd366004614872565b612dc3565b61051d610d10366004614457565b600460209081525f928352604080842090915290825290205481565b61051d7f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a81565b610566610d613660046142fd565b612de4565b61051d610d743660046143da565b6001600160a01b03165f908152600d6020526040902060020154919050565b61051d610da1366004614380565b612e8d565b61051d610db43660046143da565b6001600160a01b03165f908152600d6020526040902054919050565b6016546002546009546040517fae68676c000000000000000000000000000000000000000000000000000000008152600481019290925261010090046001600160a01b0390811660248301527f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df1811660448301525f92169063ae68676c90606401602060405180830381865afa158015610e6c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e90919061490a565b905090565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167fe9fcb88e000000000000000000000000000000000000000000000000000000001480610eea5750610eea82612efb565b92915050565b610ef8612fde565b610f0181613038565b50565b5f8054610f1090614921565b80601f0160208091040260200160405190810160405280929190818152602001828054610f3c90614921565b8015610f875780601f10610f5e57610100808354040283529160200191610f87565b820191905f5260205f20905b815481529060010190602001808311610f6a57829003601f168201915b505050505081565b6002545f908015610fb257610fad610fa5610dd0565b849083613139565b610fb4565b825b9392505050565b335f8181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925906110159086815260200190565b60405180910390a350600192915050565b60405162461bcd60e51b815260206004820152601760248201527f455243373534305661756c742f6173796e632d666c6f7700000000000000000060448201525f906064015b60405180910390fd5b5f61107e613154565b611087836112be565b6040805160c08101825260105467ffffffffffffffff80821683526801000000000000000082048116602084015270010000000000000000000000000000000082048116938301849052780100000000000000000000000000000000000000000000000090910416606082015260115460808201526012546001600160a01b031660a08201529192505f90611126908490670de0b6b3a7640000613139565b905061113230866132b7565b61114661113f828561499f565b8686613331565b50611155818360a001516134b0565b505092915050565b611165612fde565b600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce229060200160405180910390a150565b6001600160a01b0383165f9081526004602090815260408083203384529091528120545f19811461122a57611206838261499f565b6001600160a01b0386165f9081526004602090815260408083203384529091529020555b6001600160a01b0385165f908152600360205260408120805485929061125190849061499f565b90915550506001600160a01b038085165f81815260036020526040908190208054870190555190918716907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906112ab9087815260200190565b60405180910390a3506001949350505050565b600254600f545f919082906112f7906112df90670de0b6b3a764000061499f565b670de0b6b3a76400006112f0610dd0565b9190613139565b9050811561130f5761130a848284613139565b611311565b835b949350505050565b611321612fde565b6001600160a01b0381166113775760405162461bcd60e51b815260206004820152601660248201527f536166655661756c742f696e76616c69642d7361666500000000000000000000604482015260640161106c565b6040805180820182526001600160a01b038316808252426020909201829052601780547fffffffffffffffffffffffff0000000000000000000000000000000000000000168217905560189190915590517f500119a4be3e95be870ef423be9332d25076b52ba5ca53b00f8fa823b61f6e2b905f90a250565b6113fa82826134ef565b5050565b5f610eea82333361259a565b611412612fde565b5f838152600a602090815260408083206001600160a01b0386168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168515159081179091558151878152928301939093528101919091527f5f0ecfd1ea5555d5b4b6140b49c92365beaf40d0a057dc34a9746990cd4ce8d49060600160405180910390a1505050565b5f7f00000000000000000000000000000000000000000000000000000000000b67d246146114d757610e90613637565b507fb9357afcff3e6e744bbb190775b5787f32d20b0bda7391fdeb316af846f8dc7e90565b335f9081527f6c4ab3a3cc4fea3ac566afdaa38e0e471d1bdfd1aa59eb20affdabfa5e893fed60205260409020547f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a9060ff168061156457506006546001600160a01b031633145b6115b05760405162461bcd60e51b815260206004820152601a60248201527f42617365455243373534302f6e6f742d617574686f72697a6564000000000000604482015260640161106c565b610f016136cf565b5f5f6115c2610dd0565b6013546009549192509060ff16156115dd57505f9392505050565b5f1981036115ec579392505050565b808210156115fe5761130a828261499f565b5f949350505050565b5f6001600160a01b03831633036116865760405162461bcd60e51b815260206004820152602860248201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60448201527f6f70657261746f72000000000000000000000000000000000000000000000000606482015260840161106c565b335f818152600b602090815260408083206001600160a01b0388168085529083529281902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001687151590811790915590519081529192917fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa2679101611015565b611710613735565b611718613154565b565b6040805160c08101825260105467ffffffffffffffff808216835268010000000000000000820481166020840152700100000000000000000000000000000000820481169383019390935278010000000000000000000000000000000000000000000000009004909116606082015260115460808201526012546001600160a01b031660a08201525f906117ad81613788565b91505090565b5f6117bc613735565b6117c683826137a5565b6117cf83612e8d565b9050805f036118205760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b61184c7f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df13330866137ad565b6118568282613867565b60408051848152602081018390526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610eea83826138c9565b5f856001600160a01b0316876001600160a01b03160361192e5760405162461bcd60e51b815260206004820152602860248201527f455243373534305661756c742f63616e6e6f742d7365742d73656c662d61732d60448201527f6f70657261746f72000000000000000000000000000000000000000000000000606482015260840161106c565b8242111561197e5760405162461bcd60e51b815260206004820152601460248201527f455243373534305661756c742f65787069726564000000000000000000000000604482015260640161106c565b6001600160a01b0387165f908152600c6020908152604080832087845290915290205460ff16156119f15760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f617574686f72697a6174696f6e2d7573656400604482015260640161106c565b6001600160a01b0387165f908152600c60209081526040808320878452825280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790559084015190840151606085015191929091901a7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0821115611abf5760405162461bcd60e51b815260206004820181905260248201527f455243373534305661756c742f696e76616c69642d7369676e61747572652d73604482015260640161106c565b5f6001611aca6114a7565b7fa3efcf8cb518126a85cdfd1c1102ee539e0700189f80926e1ac37144450473fa8d8d8d8d8d604051602001611b36969594939291909586526001600160a01b03948516602087015292909316604085015215156060840152608083019190915260a082015260c00190565b60405160208183030381529060405280519060200120604051602001611b8e9291907f190100000000000000000000000000000000000000000000000000000000000081526002810192909252602282015260420190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201205f84529083018083525260ff851690820152606081018690526080810185905260a0016020604051602081039080840390855afa158015611c07573d5f5f3e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615801590611c5b57508a6001600160a01b0316816001600160a01b0316145b611ca75760405162461bcd60e51b815260206004820152600e60248201527f494e56414c49445f5349474e4552000000000000000000000000000000000000604482015260640161106c565b6001600160a01b038b81165f818152600b60209081526040808320948f168084529482529182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168e151590811790915591519182527fceb576d9f15e4e200fdb5096d64d5dfd667e16def20c1eefd14256d8e3faa267910160405180910390a35060019a9950505050505050505050565b6007546001600160a01b03163314611d975760405162461bcd60e51b815260206004820152601360248201527f4f776e65642f6e6f742d6e6f6d696e6174656400000000000000000000000000604482015260640161106c565b600654600754604080516001600160a01b0393841681529290911660208301527fb532073b38c83145e3e5135377a08bf9aab55bc0fd7c1179cd4fb995d2a5159c910160405180910390a160078054600680547fffffffffffffffffffffffff00000000000000000000000000000000000000009081166001600160a01b03841617909155169055565b6014545f90841015611e755760405162461bcd60e51b815260206004820152601760248201527f455243373534305661756c742f6d696e2d616d6f756e74000000000000000000604482015260640161106c565b611311848484613909565b611ea760405180608001604052805f81526020015f81526020015f81526020015f81525090565b506001600160a01b03165f908152600d6020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b611efc612fde565b604080518082019091526017546001600160a01b03168082526018546020830152611f695760405162461bcd60e51b815260206004820152601a60248201527f536166655661756c742f6e6f2d736166652d70726f706f736564000000000000604482015260640161106c565b4281602001516203f480611f7d91906149b2565b1115611ff15760405162461bcd60e51b815260206004820152602160248201527f536166655661756c742f736166652d6e6f742d7965742d61636365707461626c60448201527f6500000000000000000000000000000000000000000000000000000000000000606482015260840161106c565b80516015546040516001600160a01b0392831692909116907f260a9a29aa1c98abc86285a91c26a04cce10cb086152a137bf2f444ca24a45ca905f90a38051601580546001600160a01b039092167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790556017805490911690555f601855610f01613b6f565b612083612fde565b604080518082019091526019546001600160a01b0316808252601a5460208301526120f05760405162461bcd60e51b815260206004820152601c60248201527f536166655661756c742f6e6f2d6f7261636c652d70726f706f73656400000000604482015260640161106c565b4281602001516203f48061210491906149b2565b11156121785760405162461bcd60e51b815260206004820152602360248201527f536166655661756c742f6f7261636c652d6e6f742d7965742d6163636570746160448201527f626c650000000000000000000000000000000000000000000000000000000000606482015260840161106c565b80516016546040516001600160a01b0392831692909116907f05cd89403c6bdeac21c2ff33de395121a31fa1bc2bf3adf4825f1f86e79969dd905f90a38051601680546001600160a01b039092167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790556019805490911690555f601a55610f01613b6f565b335f9081527f6c4ab3a3cc4fea3ac566afdaa38e0e471d1bdfd1aa59eb20affdabfa5e893fed60205260409020547f65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a9060ff168061226a57506006546001600160a01b031633145b6122b65760405162461bcd60e51b815260206004820152601a60248201527f42617365455243373534302f6e6f742d617574686f72697a6564000000000000604482015260640161106c565b610f01613b6f565b5f6122c7613735565b6122d181846137a5565b825f036123205760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b6123298361252c565b90506123577f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df13330846137ad565b6123618284613867565b60408051828152602081018590526001600160a01b0384169133917fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7910160405180910390a3610eea81846138c9565b60018054610f1090614921565b610f0181336134ef565b5f610eea82336122be565b335f908152600360205260408120805483919083906123f390849061499f565b90915550506001600160a01b0383165f81815260036020526040908190208054850190555133907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906110159086815260200190565b5f610eea823333611e21565b61245d612fde565b6001600160a01b0381166124b35760405162461bcd60e51b815260206004820152601860248201527f536166655661756c742f696e76616c69642d6f7261636c650000000000000000604482015260640161106c565b6040805180820182526001600160a01b038316808252426020909201829052601980547fffffffffffffffffffffffff00000000000000000000000000000000000000001682179055601a9190915590517f0138b9794085eddff5afe429af2255342c44615c1c1f81ce93a1e38a476cee58905f90a250565b60408051808201909152601354815260145460208201525f908161254f84610f8f565b60095490915060ff16806125755750815181612569610dd0565b61257391906149b2565b115b806125835750816020015184105b1561259157505f9392505050565b61131184613bd2565b5f6001600160a01b0382163314806125d457506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6126205760405162461bcd60e51b815260206004820152601b60248201527f455243373534305661756c742f696e76616c69642d63616c6c65720000000000604482015260640161106c565b835f0361266f5760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f415353455453000000000000000000000000000000000000000000604482015260640161106c565b6001600160a01b0382165f908152600d602052604090206002810154600382015461269b918791613bf3565b91506126a78582613c16565b6126b18583613c7c565b6126dc7f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df18587613c8e565b60408051868152602081018490526001600160a01b03808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a45b509392505050565b5f610eea82336117b3565b5f6001600160a01b03821633148061277557506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6127c15760405162461bcd60e51b815260206004820152601b60248201527f455243373534305661756c742f696e76616c69642d63616c6c65720000000000604482015260640161106c565b835f036128105760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b6001600160a01b0382165f908152600d602052604090206003810154600282015461283c918791613139565b91506128488582613d32565b6128528286613c7c565b61287d7f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df18584613c8e565b60408051838152602081018790526001600160a01b03808616929087169133917ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db910160405180910390a4612728565b5f5f6128d7610dd0565b6013546009549192509060ff16156128f257505f9392505050565b5f198103612901579392505050565b808210156115fe5761130a610b61838361499f565b6002545f908015610fb257610fad8161292d610dd0565b859190613139565b5f8151835114612971576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612979613154565b6040805160c08101825260105467ffffffffffffffff808216835268010000000000000000820481166020840152700100000000000000000000000000000000820481169383019390935278010000000000000000000000000000000000000000000000009004909116606082015260115460808201526012546001600160a01b031660a08201525f80805b8651811015612af1575f612a31888381518110612a2457612a246149c5565b60200260200101516112be565b90505f612a5f866040015167ffffffffffffffff16670de0b6b3a7640000846131399092919063ffffffff16565b9050612aa7612a6e828461499f565b8a8581518110612a8057612a806149c5565b60200260200101518a8681518110612a9a57612a9a6149c5565b6020026020010151613331565b50612ab282886149b2565b9650612abe81856149b2565b9350888381518110612ad257612ad26149c5565b602002602001015185612ae591906149b2565b94505050600101612a05565b50612afc30836132b7565b612b0a818460a001516134b0565b50505092915050565b42841015612b635760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f45585049524544000000000000000000604482015260640161106c565b5f6001612b6e6114a7565b6001600160a01b038a81165f8181526005602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e0830190915280519201919091207f190100000000000000000000000000000000000000000000000000000000000061010083015261010282019290925261012281019190915261014201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815282825280516020918201205f84529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa158015612caf573d5f5f3e3d5ffd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001519150506001600160a01b03811615801590612d035750876001600160a01b0316816001600160a01b0316145b612d4f5760405162461bcd60e51b815260206004820152600e60248201527f494e56414c49445f5349474e4552000000000000000000000000000000000000604482015260640161106c565b6001600160a01b039081165f9081526004602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b5f610eea82333361273b565b612dcb612fde565b612dd3613735565b612ddb613154565b610f0181613d98565b612dec612fde565b6702c68af0bb14000081602001511180612e0e575080516702c68af0bb140000105b15612e45576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f773cd47b12eabb5832b83bb94bc847b3e000c5b824fdf9da506cb9ecf9e1a53f600e82604051612e779291906149f2565b60405180910390a18051600e5560200151600f55565b60408051808201909152601354815260145460208201525f9081612eb084612916565b60095490915060ff1680612ed65750815184612eca610dd0565b612ed491906149b2565b115b80612ee45750816020015181105b15612ef257505f9392505050565b6113118461400a565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f2f0a18c5000000000000000000000000000000000000000000000000000000001480612f8d57507fffffffff0000000000000000000000000000000000000000000000000000000082167fe3bc4e6500000000000000000000000000000000000000000000000000000000145b80610eea57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001492915050565b6006546001600160a01b031633146117185760405162461bcd60e51b815260206004820152600f60248201527f4f776e65642f6e6f742d6f776e65720000000000000000000000000000000000604482015260640161106c565b6002548015801590613051575061304d610dd0565b8251105b15613088576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6130b37f0000000000000000000000000000000000000000000000000000000000000012600a614afd565b826020015111156130f0576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7ff455e80a7d5b3f148849bb4acc1e909fafb067275e80e335f966127410c9169f6013836040516131229291906149f2565b60405180910390a150805160135560200151601455565b5f825f19048411830215820261314d575f5ffd5b5091020490565b6040805160c08101825260105467ffffffffffffffff808216835268010000000000000000820481166020840152700100000000000000000000000000000000820481169383019390935278010000000000000000000000000000000000000000000000009004909116606082015260115460808201526012546001600160a01b031660a08201525f6131e68261401f565b90505f6131f2836140d8565b90505f61322361058b7f0000000000000000000000000000000000000000000000000000000000000012600a614afd565b90505f61323083856149b2565b11156132515760a08401516132519061324c610b6185876149b2565b613867565b83608001518111156132635760118190555b81156132b1576010805477ffffffffffffffffffffffffffffffffffffffffffffffff1678010000000000000000000000000000000000000000000000004267ffffffffffffffff16021790555b50505050565b6001600160a01b0382165f90815260036020526040812080548392906132de90849061499f565b90915550506002805482900390556040518181525f906001600160a01b038416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020015b60405180910390a35050565b5f83158061333d575082155b1561338a5760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b6001600160a01b0382165f908152600d602052604090208054158015906133b2575080548411155b6133fe5760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b6134088585614142565b83816002015f82825461341b91906149b2565b9250508190555084816003015f82825461343591906149b2565b90915550508054849082905f9061344d90849061499f565b909155505080545f03613461575f60018201555b604080518581526020810187905233916001600160a01b038616917f24111f527e6debb0efcfd4c847fc0ae4d8858cdfc72cc2fce0e757a3fce414f7910160405180910390a350929392505050565b81156113fa576015546113fa907f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df1906001600160a01b031683856137ad565b6001600160a01b03821633148061352857506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b6135745760405162461bcd60e51b815260206004820152601b60248201527f455243373534305661756c742f696e76616c69642d63616c6c65720000000000604482015260640161106c565b6001600160a01b0382165f908152600d602052604090208054806135da5760405162461bcd60e51b815260206004820152601f60248201527f455243373534305661756c742f6e6f2d70656e64696e672d7265717565737400604482015260640161106c565b6135e5308483613c8e565b5f80835560018301556040518181526001600160a01b0384811691908616907f756776540459f688c5059ee7547eed3bfaf8143dc14677d8fcef3007aceec9fc9060200160405180910390a350505050565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f5f6040516136679190614b0b565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6136d761417b565b600980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040513381527f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa906020015b60405180910390a1565b60095460ff16156117185760405162461bcd60e51b815260206004820152600f60248201527f5061757361626c652f7061757365640000000000000000000000000000000000604482015260640161106c565b5f613792826140d8565b61379b8361401f565b610eea91906149b2565b6113fa613154565b5f6040517f23b872dd0000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526001600160a01b038416602482015282604482015260205f6064835f8a5af13d15601f3d1160015f5114161716915050806138605760405162461bcd60e51b815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000604482015260640161106c565b5050505050565b8060025f82825461387891906149b2565b90915550506001600160a01b0382165f818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101613325565b6138d1613154565b6015546113fa907f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df1906001600160a01b031684613c8e565b5f6001600160a01b03821633148061394357506001600160a01b0382165f908152600b6020908152604080832033845290915290205460ff165b61398f5760405162461bcd60e51b815260206004820152601a60248201527f455243373534305661756c742f696e76616c69642d6f776e6572000000000000604482015260640161106c565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152849030906370a0823190602401602060405180830381865afa1580156139ec573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613a10919061490a565b1015613a845760405162461bcd60e51b815260206004820152602160248201527f455243373534305661756c742f696e73756666696369656e742d62616c616e6360448201527f6500000000000000000000000000000000000000000000000000000000000000606482015260840161106c565b835f03613ad35760405162461bcd60e51b815260206004820152600b60248201527f5a45524f5f534841524553000000000000000000000000000000000000000000604482015260640161106c565b613adf308330876137ad565b6001600160a01b0383165f908152600d60205260408120805490918691839190613b0a9084906149b2565b9091555050426001820155604080515f81523360208201529081018690526001600160a01b0384811691908616907f1e7ddf69e1e9242eb5635deb06ffd4691a8962ec488a863a26721288c88985519060600160405180910390a3505f949350505050565b613b77613735565b600980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040513381527f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2589060200161372b565b6009545f9060ff16613bec57613be7826141cd565b610eea565b5f92915050565b5f825f190484118302158202613c07575f5ffd5b50910281810615159190040190565b5f613c348260020154836003015485613bf39092919063ffffffff16565b905082826003015f828254613c49919061499f565b909155505060028201548110613c5f575f613c6f565b808260020154613c6f919061499f565b8260020181905550505050565b60095460ff166113fa576113fa613154565b5f6040517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b038416600482015282602482015260205f6044835f895af13d15601f3d1160015f5114161716915050806132b15760405162461bcd60e51b815260206004820152600f60248201527f5452414e534645525f4641494c45440000000000000000000000000000000000604482015260640161106c565b5f613d508260030154836002015485613bf39092919063ffffffff16565b905080826003015411613d63575f613d73565b808260030154613d73919061499f565b826003018190555082826002015f828254613d8e919061499f565b9091555050505050565b6702c68af0bb140000815f015167ffffffffffffffff161180613dcf575066b1a2bc2ec50000816020015167ffffffffffffffff16115b80613dee575066b1a2bc2ec50000816040015167ffffffffffffffff16115b15613e25576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60a08101516001600160a01b0316613e69576040517f43f3e27e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b67ffffffffffffffff421660608201526011545f03613eba57613eb061058b7f0000000000000000000000000000000000000000000000000000000000000012600a614afd565b6080820152613ec3565b60115460808201525b7faf0d4a9793f0cb3dd1b01241953e01dc6eb6515b9e5cd623f1b3cad0fc1fa456601082604051613ef5929190614bde565b60405180910390a180516010805460208401516040850151606086015167ffffffffffffffff90811678010000000000000000000000000000000000000000000000000277ffffffffffffffffffffffffffffffffffffffffffffffff92821670010000000000000000000000000000000002929092166fffffffffffffffffffffffffffffffff93821668010000000000000000027fffffffffffffffffffffffffffffffff000000000000000000000000000000009095169190961617929092171692909217919091179055608081015160115560a00151601280546001600160a01b039092167fffffffffffffffffffffffff0000000000000000000000000000000000000000909216919091179055565b6009545f9060ff16613bec57613be7826141eb565b5f8061404f61058b7f0000000000000000000000000000000000000000000000000000000000000012600a614afd565b835190915067ffffffffffffffff1680158015906140705750836080015182115b61407a575f611311565b611311600254856080015184614090919061499f565b61409a9190614cb7565b6140c57f00000000000000000000000000000000000000000000000000000000000000126012614cce565b6140d090600a614afd565b839190613bf3565b60208101515f9067ffffffffffffffff16806140f4575f610fb4565b670de0b6b3a7640000614138846060015167ffffffffffffffff164261411a919061499f565b614122610dd0565b61412c9190614cb7565b83906301e13380613139565b610fb49190614ce7565b6015546113fa907f000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df1906001600160a01b031630856137ad565b60095460ff166117185760405162461bcd60e51b815260206004820152601360248201527f5061757361626c652f6e6f742d70617573656400000000000000000000000000604482015260640161106c565b6002545f908015610fb257610fad6141e3610dd0565b849083613bf3565b5f610eea82612916565b5f60208284031215614205575f5ffd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610fb4575f5ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156142a8576142a8614234565b604052919050565b5f604082840312156142c0575f5ffd5b6040805190810167ffffffffffffffff811182821017156142e3576142e3614234565b604052823581526020928301359281019290925250919050565b5f6040828403121561430d575f5ffd5b610fb483836142b0565b602081525f82518060208401525f5b818110156143435760208186018101516040868401015201614326565b505f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b5f60208284031215614390575f5ffd5b5035919050565b80356001600160a01b03811681146143ad575f5ffd5b919050565b5f5f604083850312156143c3575f5ffd5b6143cc83614397565b946020939093013593505050565b5f5f604083850312156143eb575f5ffd5b823591506143fb60208401614397565b90509250929050565b5f60208284031215614414575f5ffd5b610fb482614397565b5f5f5f6060848603121561442f575f5ffd5b61443884614397565b925061444660208501614397565b929592945050506040919091013590565b5f5f60408385031215614468575f5ffd5b61447183614397565b91506143fb60208401614397565b803580151581146143ad575f5ffd5b5f5f5f606084860312156144a0575f5ffd5b833592506144b060208501614397565b91506144be6040850161447f565b90509250925092565b815181526020808301519082015260408101610eea565b5f5f604083850312156144ef575f5ffd5b6144f883614397565b91506143fb6020840161447f565b5f5f5f5f5f5f60c0878903121561451b575f5ffd5b61452487614397565b955061453260208801614397565b94506145406040880161447f565b9350606087013592506080870135915060a087013567ffffffffffffffff811115614569575f5ffd5b8701601f81018913614579575f5ffd5b803567ffffffffffffffff81111561459357614593614234565b6145c460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614261565b8181528a60208385010111156145d8575f5ffd5b816020840160208301375f602083830101528093505050509295509295509295565b5f5f5f6060848603121561460c575f5ffd5b8335925061461c60208501614397565b91506144be60408501614397565b5f67ffffffffffffffff82111561464357614643614234565b5060051b60200190565b5f82601f83011261465c575f5ffd5b813561466f61466a8261462a565b614261565b8082825260208201915060208360051b860101925085831115614690575f5ffd5b602085015b838110156146b4576146a681614397565b835260209283019201614695565b5095945050505050565b5f5f604083850312156146cf575f5ffd5b823567ffffffffffffffff8111156146e5575f5ffd5b8301601f810185136146f5575f5ffd5b803561470361466a8261462a565b8082825260208201915060208360051b850101925087831115614724575f5ffd5b6020840193505b8284101561474657833582526020938401939091019061472b565b9450505050602083013567ffffffffffffffff811115614764575f5ffd5b6147708582860161464d565b9150509250929050565b5f5f5f5f5f5f5f60e0888a031215614790575f5ffd5b61479988614397565b96506147a760208901614397565b95506040880135945060608801359350608088013560ff811681146147ca575f5ffd5b9699959850939692959460a0840135945060c09093013592915050565b60c08101610eea828467ffffffffffffffff815116825267ffffffffffffffff602082015116602083015267ffffffffffffffff604082015116604083015267ffffffffffffffff6060820151166060830152608081015160808301526001600160a01b0360a08201511660a08301525050565b803567ffffffffffffffff811681146143ad575f5ffd5b5f60c0828403128015614883575f5ffd5b5060405160c0810167ffffffffffffffff811182821017156148a7576148a7614234565b6040526148b38361485b565b81526148c16020840161485b565b60208201526148d26040840161485b565b60408201526148e36060840161485b565b6060820152608083810135908201526148fe60a08401614397565b60a08201529392505050565b5f6020828403121561491a575f5ffd5b5051919050565b600181811c9082168061493557607f821691505b60208210810361496c577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b81810381811115610eea57610eea614972565b80820180821115610eea57610eea614972565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b8254815260018301546020820152608081018251604083015260208301516060830152610fb4565b6001815b6001841115614a5557808504811115614a3957614a39614972565b6001841615614a4757908102905b60019390931c928002614a1e565b935093915050565b5f82614a6b57506001610eea565b81614a7757505f610eea565b8160018114614a8d5760028114614a9757614ab3565b6001915050610eea565b60ff841115614aa857614aa8614972565b50506001821b610eea565b5060208310610133831016604e8410600b8410161715614ad6575081810a610eea565b614ae25f198484614a1a565b805f1904821115614af557614af5614972565b029392505050565b5f610fb460ff841683614a5d565b5f5f83545f8160011c90506001821680614b2657607f821691505b602082108103614b5d577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b808015614b715760018114614ba457614bd2565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0084168752821515830287019450614bd2565b5f888152602090205f5b84811015614bca57815489820152600190910190602001614bae565b505082870194505b50929695505050505050565b5f61018082019050835467ffffffffffffffff8116835267ffffffffffffffff8160401c16602084015267ffffffffffffffff8160801c1660408401528060c01c606084015250600184015460808301526001600160a01b03806002860154161660a0830152610fb460c083018467ffffffffffffffff815116825267ffffffffffffffff602082015116602083015267ffffffffffffffff604082015116604083015267ffffffffffffffff6060820151166060830152608081015160808301526001600160a01b0360a08201511660a08301525050565b8082028115828204841417610eea57610eea614972565b60ff8181168382160190811115610eea57610eea614972565b5f82614d1a577f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b50049056fea264697066735822122030cf38f2f7b836050afcf01fc1d0fa6936ff135a2e885041e6482d6df05107aa64736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000b2a4e3b4843adea2abdcc02aa9db8261fd9323ad000000000000000000000000452dc676b4e377a76b4b3048eb3b511a0f1ec057000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df1000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000919d5a6f2cbc0731380c26b4ac4f6183dd3a40c8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000919d5a6f2cbc0731380c26b4ac4f6183dd3a40c80000000000000000000000000000000000000000000000000000000000000006736276555344000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000067362765553440000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : params (tuple):
Arg [1] : asset (address): 0x876aac7648D79f87245E73316eB2D100e75F3Df1
Arg [2] : name (string): sbvUSD
Arg [3] : symbol (string): sbvUSD
Arg [4] : owner (address): 0x919D5a6F2CBc0731380C26B4AC4f6183dD3A40C8
Arg [5] : limits (tuple):
Arg [1] : depositLimit (uint256): 115792089237316195423570985008687907853269984665640564039457584007913129639935
Arg [2] : minAmount (uint256): 0
Arg [6] : fees (tuple):
Arg [1] : performanceFee (uint64): 0
Arg [2] : managementFee (uint64): 0
Arg [3] : withdrawalIncentive (uint64): 0
Arg [4] : feesUpdatedAt (uint64): 0
Arg [5] : highWaterMark (uint256): 0
Arg [6] : feeRecipient (address): 0x919D5a6F2CBc0731380C26B4AC4f6183dD3A40C8
Arg [1] : oracle_ (address): 0xb2a4E3B4843ADea2abdCc02aa9db8261Fd9323AD
Arg [2] : safe_ (address): 0x452DC676b4E377a76B4b3048eB3b511A0F1ec057
-----Encoded View---------------
19 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 000000000000000000000000b2a4e3b4843adea2abdcc02aa9db8261fd9323ad
Arg [2] : 000000000000000000000000452dc676b4e377a76b4b3048eb3b511a0f1ec057
Arg [3] : 000000000000000000000000876aac7648d79f87245e73316eb2d100e75f3df1
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [5] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [6] : 000000000000000000000000919d5a6f2cbc0731380c26b4ac4f6183dd3a40c8
Arg [7] : ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [12] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [14] : 000000000000000000000000919d5a6f2cbc0731380c26b4ac4f6183dd3a40c8
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [16] : 7362765553440000000000000000000000000000000000000000000000000000
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [18] : 7362765553440000000000000000000000000000000000000000000000000000
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.