Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x60806040 | 16241223 | 633 days ago | IN | 0 ETH | 0.1134248 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
VaultManagerListing
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 1 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.17; import { IBorrowStakerCheckpoint } from "../interfaces/IBorrowStaker.sol"; import "borrow/vaultManager/VaultManager.sol"; /// @title VaultManagerListing /// @author Angle Labs, Inc. /// @notice Provides an additional viewer to `VaultManager` to get the full collateral deposited /// by an owner /// @dev This implementation is built to interact with `collateral` that are in fact `staker` contracts wrapping /// another collateral asset. /// /// @dev Some things are worth noting regarding transfers and updates in the `totalBalanceOf` for such `collateral`. /// When adding or removing collateral to/from a vault, the `totalBalanceOf` of an address is updated, even if the asset /// has not been transferred yet, meaning there can be two checkpoints for in fact a single transfer. /// /// @dev Adding collateral to a vault increases the total balance of the `sender`. But after the vault collateral increase, /// since the `sender` still owns the `collateral`, there is a double count in the total balance. This is not a /// problem as the `sender` was already checkpointed in the `_addCollateral`. /// /// @dev In the case of a `burn` or `removeCollateral` action, there is a first checkpoint with the correct balances, /// and then a second one when the vault transfers the `collateral` with a deflated balance in this case. /// /// @dev Conclusion is that the logic on which this contract is built is working as expected as long as no rewards /// are distributed within a same tx from the staking contract. Most protocols already follow this hypothesis, /// but for those who don't, this vault implementation doesn't work /// /// @dev Note that it is a weaker assumption than what is done in the `staker` contract which supposes that no rewards /// can be distributed to the same address within a block contract VaultManagerListing is VaultManager { using SafeERC20 for IERC20; using Address for address; // ================= INTERNAL UTILITY STATE-MODIFYING FUNCTIONS ================ /// @inheritdoc VaultManagerERC721 function _beforeTokenTransfer( address from, address to, uint256 vaultID ) internal override { // If the transfer is between 2 addresses we need to checkpoint both of them. // If it is a burn we also need to checkpoint as the burn didn't trigger yet a change in collateral amount if (from != address(0)) { uint256 collateralAmount = vaultData[vaultID].collateralAmount; _checkpointWrapper(from, collateralAmount, false); if (to != address(0)) _checkpointWrapper(to, collateralAmount, true); } } /// @inheritdoc VaultManager /// @dev Checkpoints the `collateral` of the contract after an update of the `collateralAmount` of vaultID function _checkpointCollateral( uint256 vaultID, uint256 amount, bool add ) internal override { _checkpointWrapper(_ownerOf(vaultID), amount, add); } /// @notice Checkpoint rewards for `user` in the `staker` contract /// @param user Address for which the balance should be updated /// @param amount Amount of collateral added / removed from the vault /// @param add Whether the collateral was added or removed from the vault /// @dev Whenever there is an internal transfer or a transfer from the `vaultManager`, /// we need to update the rewards to correctly track everyone's claim function _checkpointWrapper( address user, uint256 amount, bool add ) internal { IBorrowStakerCheckpoint(address(collateral)).checkpointFromVaultManager(user, amount, add); } }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.7; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IBorrowStakerCheckpoint { function checkpointFromVaultManager( address from, uint256 amount, bool add ) external; } interface IBorrowStaker is IBorrowStakerCheckpoint, IERC20 { function asset() external returns (IERC20 stakingToken); function deposit(uint256 amount, address to) external; function withdraw( uint256 amount, address from, address to ) external; //solhint-disable-next-line function claim_rewards(address user) external returns (uint256[] memory); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; /// @title IAgToken /// @author Angle Labs, Inc. /// @notice Interface for the stablecoins `AgToken` contracts /// @dev This interface only contains functions of the `AgToken` contract which are called by other contracts /// of this module or of the first module of the Angle Protocol interface IAgToken is IERC20Upgradeable { // ======================= Minter Role Only Functions =========================== /// @notice Lets the `StableMaster` contract or another whitelisted contract mint agTokens /// @param account Address to mint to /// @param amount Amount to mint /// @dev The contracts allowed to issue agTokens are the `StableMaster` contract, `VaultManager` contracts /// associated to this stablecoin as well as the flash loan module (if activated) and potentially contracts /// whitelisted by governance function mint(address account, uint256 amount) external; /// @notice Burns `amount` tokens from a `burner` address after being asked to by `sender` /// @param amount Amount of tokens to burn /// @param burner Address to burn from /// @param sender Address which requested the burn from `burner` /// @dev This method is to be called by a contract with the minter right after being requested /// to do so by a `sender` address willing to burn tokens from another `burner` address /// @dev The method checks the allowance between the `sender` and the `burner` function burnFrom( uint256 amount, address burner, address sender ) external; /// @notice Burns `amount` tokens from a `burner` address /// @param amount Amount of tokens to burn /// @param burner Address to burn from /// @dev This method is to be called by a contract with a minter right on the AgToken after being /// requested to do so by an address willing to burn tokens from its address function burnSelf(uint256 amount, address burner) external; // ========================= Treasury Only Functions =========================== /// @notice Adds a minter in the contract /// @param minter Minter address to add /// @dev Zero address checks are performed directly in the `Treasury` contract function addMinter(address minter) external; /// @notice Removes a minter from the contract /// @param minter Minter address to remove /// @dev This function can also be called by a minter wishing to revoke itself function removeMinter(address minter) external; /// @notice Sets a new treasury contract /// @param _treasury New treasury address function setTreasury(address _treasury) external; // ========================= External functions ================================ /// @notice Checks whether an address has the right to mint agTokens /// @param minter Address for which the minting right should be checked /// @return Whether the address has the right to mint agTokens or not function isMinter(address minter) external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; /// @title ICoreBorrow /// @author Angle Labs, Inc. /// @notice Interface for the `CoreBorrow` contract /// @dev This interface only contains functions of the `CoreBorrow` contract which are called by other contracts /// of this module interface ICoreBorrow { /// @notice Checks if an address corresponds to a treasury of a stablecoin with a flash loan /// module initialized on it /// @param treasury Address to check /// @return Whether the address has the `FLASHLOANER_TREASURY_ROLE` or not function isFlashLoanerTreasury(address treasury) external view returns (bool); /// @notice Checks whether an address is governor of the Angle Protocol or not /// @param admin Address to check /// @return Whether the address has the `GOVERNOR_ROLE` or not function isGovernor(address admin) external view returns (bool); /// @notice Checks whether an address is governor or a guardian of the Angle Protocol or not /// @param admin Address to check /// @return Whether the address has the `GUARDIAN_ROLE` or not /// @dev Governance should make sure when adding a governor to also give this governor the guardian /// role by calling the `addGovernor` function function isGovernorOrGuardian(address admin) external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./IAgToken.sol"; import "./ICoreBorrow.sol"; /// @title IFlashAngle /// @author Angle Labs, Inc. /// @notice Interface for the `FlashAngle` contract /// @dev This interface only contains functions of the contract which are called by other contracts /// of this module interface IFlashAngle { /// @notice Reference to the `CoreBorrow` contract managing the FlashLoan module function core() external view returns (ICoreBorrow); /// @notice Sends the fees taken from flash loans to the treasury contract associated to the stablecoin /// @param stablecoin Stablecoin from which profits should be sent /// @return balance Amount of profits sent /// @dev This function can only be called by the treasury contract function accrueInterestToTreasury(IAgToken stablecoin) external returns (uint256 balance); /// @notice Adds support for a stablecoin /// @param _treasury Treasury associated to the stablecoin to add support for /// @dev This function can only be called by the `CoreBorrow` contract function addStablecoinSupport(address _treasury) external; /// @notice Removes support for a stablecoin /// @param _treasury Treasury associated to the stablecoin to remove support for /// @dev This function can only be called by the `CoreBorrow` contract function removeStablecoinSupport(address _treasury) external; /// @notice Sets a new core contract /// @param _core Core contract address to set /// @dev This function can only be called by the `CoreBorrow` contract function setCore(address _core) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./ITreasury.sol"; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; /// @title IOracle /// @author Angle Labs, Inc. /// @notice Interface for the `Oracle` contract /// @dev This interface only contains functions of the contract which are called by other contracts /// of this module interface IOracle { /// @notice Reads the rate from the Chainlink circuit and other data provided /// @return quoteAmount The current rate between the in-currency and out-currency in the base /// of the out currency /// @dev For instance if the out currency is EUR (and hence agEUR), then the base of the returned /// value is 10**18 function read() external view returns (uint256); /// @notice Changes the treasury contract /// @param _treasury Address of the new treasury contract /// @dev This function can be called by an approved `VaultManager` contract which can call /// this function after being requested to do so by a `treasury` contract /// @dev In some situations (like reactor contracts), the `VaultManager` may not directly be linked /// to the `oracle` contract and as such we may need governors to be able to call this function as well function setTreasury(address _treasury) external; /// @notice Reference to the `treasury` contract handling this `VaultManager` function treasury() external view returns (ITreasury treasury); /// @notice Array with the list of Chainlink feeds in the order in which they are read function circuitChainlink() external view returns (AggregatorV3Interface[] memory); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title ISwapper /// @author Angle Labs, Inc. /// @notice Interface for Swapper contracts /// @dev This interface defines the key functions `Swapper` contracts should have when interacting with /// Angle interface ISwapper { /// @notice Notifies a contract that an address should be given `outToken` from `inToken` /// @param inToken Address of the token received /// @param outToken Address of the token to obtain /// @param outTokenRecipient Address to which the outToken should be sent /// @param outTokenOwed Minimum amount of outToken the `outTokenRecipient` address should have at the end of the call /// @param inTokenObtained Amount of collateral obtained by a related address prior /// to the call to this function /// @param data Extra data needed (to encode Uniswap swaps for instance) function swap( IERC20 inToken, IERC20 outToken, address outTokenRecipient, uint256 outTokenOwed, uint256 inTokenObtained, bytes calldata data ) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./IAgToken.sol"; import "./ICoreBorrow.sol"; import "./IFlashAngle.sol"; /// @title ITreasury /// @author Angle Labs, Inc. /// @notice Interface for the `Treasury` contract /// @dev This interface only contains functions of the `Treasury` which are called by other contracts /// of this module interface ITreasury { /// @notice Stablecoin handled by this `treasury` contract function stablecoin() external view returns (IAgToken); /// @notice Checks whether a given address has the governor role /// @param admin Address to check /// @return Whether the address has the governor role /// @dev Access control is only kept in the `CoreBorrow` contract function isGovernor(address admin) external view returns (bool); /// @notice Checks whether a given address has the guardian or the governor role /// @param admin Address to check /// @return Whether the address has the guardian or the governor role /// @dev Access control is only kept in the `CoreBorrow` contract which means that this function /// queries the `CoreBorrow` contract function isGovernorOrGuardian(address admin) external view returns (bool); /// @notice Checks whether a given address has well been initialized in this contract /// as a `VaultManager` /// @param _vaultManager Address to check /// @return Whether the address has been initialized or not function isVaultManager(address _vaultManager) external view returns (bool); /// @notice Sets a new flash loan module for this stablecoin /// @param _flashLoanModule Reference to the new flash loan module /// @dev This function removes the minting right to the old flash loan module and grants /// it to the new module function setFlashLoanModule(address _flashLoanModule) external; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts/interfaces/IERC721Metadata.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./ITreasury.sol"; import "./IOracle.sol"; // ========================= Key Structs and Enums ============================= /// @notice Parameters associated to a given `VaultManager` contract: these all correspond /// to parameters which signification is detailed in the `VaultManagerStorage` file struct VaultParameters { uint256 debtCeiling; uint64 collateralFactor; uint64 targetHealthFactor; uint64 interestRate; uint64 liquidationSurcharge; uint64 maxLiquidationDiscount; bool whitelistingActivated; uint256 baseBoost; } /// @notice Data stored to track someone's loan (or equivalently called position) struct Vault { // Amount of collateral deposited in the vault, in collateral decimals. For example, if the collateral // is USDC with 6 decimals, then `collateralAmount` will be in base 10**6 uint256 collateralAmount; // Normalized value of the debt (that is to say of the stablecoins borrowed). It is expressed // in the base of Angle stablecoins (i.e. `BASE_TOKENS = 10**18`) uint256 normalizedDebt; } /// @notice For a given `vaultID`, this encodes a liquidation opportunity that is to say details about the maximum /// amount that could be repaid by liquidating the position /// @dev All the values are null in the case of a vault which cannot be liquidated under these conditions struct LiquidationOpportunity { // Maximum stablecoin amount that can be repaid upon liquidating the vault uint256 maxStablecoinAmountToRepay; // Collateral amount given to the person in the case where the maximum amount to repay is given uint256 maxCollateralAmountGiven; // Threshold value of stablecoin amount to repay: it is ok for a liquidator to repay below threshold, // but if this threshold is non null and the liquidator wants to repay more than threshold, it should repay // the max stablecoin amount given in this vault uint256 thresholdRepayAmount; // Discount proposed to the liquidator on the collateral uint256 discount; // Amount of debt in the vault uint256 currentDebt; } /// @notice Data stored during a liquidation process to keep in memory what's due to a liquidator and some /// essential data for vaults being liquidated struct LiquidatorData { // Current amount of stablecoins the liquidator should give to the contract uint256 stablecoinAmountToReceive; // Current amount of collateral the contract should give to the liquidator uint256 collateralAmountToGive; // Bad debt accrued across the liquidation process uint256 badDebtFromLiquidation; // Oracle value (in stablecoin base) at the time of the liquidation uint256 oracleValue; // Value of the `interestAccumulator` at the time of the call uint256 newInterestAccumulator; } /// @notice Data to track during a series of action the amount to give or receive in stablecoins and collateral /// to the caller or associated addresses struct PaymentData { // Stablecoin amount the contract should give uint256 stablecoinAmountToGive; // Stablecoin amount owed to the contract uint256 stablecoinAmountToReceive; // Collateral amount the contract should give uint256 collateralAmountToGive; // Collateral amount owed to the contract uint256 collateralAmountToReceive; } /// @notice Actions possible when composing calls to the different entry functions proposed enum ActionType { createVault, closeVault, addCollateral, removeCollateral, repayDebt, borrow, getDebtIn, permit } // ========================= Interfaces ============================= /// @title IVaultManagerFunctions /// @author Angle Labs, Inc. /// @notice Interface for the `VaultManager` contract /// @dev This interface only contains functions of the contract which are called by other contracts /// of this module (without getters) interface IVaultManagerFunctions { /// @notice Accrues interest accumulated across all vaults to the surplus and sends the surplus to the treasury /// @return surplusValue Value of the surplus communicated to the `Treasury` /// @return badDebtValue Value of the bad debt communicated to the `Treasury` /// @dev `surplus` and `badDebt` should be reset to 0 once their current value have been given to the `treasury` contract function accrueInterestToTreasury() external returns (uint256 surplusValue, uint256 badDebtValue); /// @notice Removes debt from a vault after being requested to do so by another `VaultManager` contract /// @param vaultID ID of the vault to remove debt from /// @param amountStablecoins Amount of stablecoins to remove from the debt: this amount is to be converted to an /// internal debt amount /// @param senderBorrowFee Borrowing fees from the contract which requested this: this is to make sure that people are not /// arbitraging difference in minting fees /// @param senderRepayFee Repay fees from the contract which requested this: this is to make sure that people are not arbitraging /// differences in repay fees /// @dev This function can only be called from a vaultManager registered in the same Treasury function getDebtOut( uint256 vaultID, uint256 amountStablecoins, uint256 senderBorrowFee, uint256 senderRepayFee ) external; /// @notice Gets the current debt of a vault /// @param vaultID ID of the vault to check /// @return Debt of the vault function getVaultDebt(uint256 vaultID) external view returns (uint256); /// @notice Gets the total debt across all vaults /// @return Total debt across all vaults, taking into account the interest accumulated /// over time function getTotalDebt() external view returns (uint256); /// @notice Sets the treasury contract /// @param _treasury New treasury contract /// @dev All required checks when setting up a treasury contract are performed in the contract /// calling this function function setTreasury(address _treasury) external; /// @notice Creates a vault /// @param toVault Address for which the va /// @return vaultID ID of the vault created /// @dev This function just creates the vault without doing any collateral or function createVault(address toVault) external returns (uint256); /// @notice Allows composability between calls to the different entry points of this module. Any user calling /// this function can perform any of the allowed actions in the order of their choice /// @param actions Set of actions to perform /// @param datas Data to be decoded for each action: it can include like the `vaultID` or the `stablecoinAmount` to borrow /// @param from Address from which stablecoins will be taken if one action includes burning stablecoins. This address /// should either be the `msg.sender` or be approved by the latter /// @param to Address to which stablecoins and/or collateral will be sent in case of /// @param who Address of the contract to handle in case of repayment of stablecoins from received collateral /// @param repayData Data to pass to the repayment contract in case of /// @return paymentData Struct containing the accounting changes from the protocol's perspective (like how much of collateral /// or how much has been received). Note that the values in the struct are not aggregated and you could have in the output /// a positive amount of stablecoins to receive as well as a positive amount of stablecoins to give /// @dev This function is optimized to reduce gas cost due to payment from or to the user and that expensive calls /// or computations (like `oracleValue`) are done only once /// @dev When specifying `vaultID` in `data`, it is important to know that if you specify `vaultID = 0`, it will simply /// use the latest `vaultID`. This is the default behavior, and unless you're engaging into some complex protocol actions /// it is encouraged to use `vaultID = 0` only when the first action of the batch is `createVault` function angle( ActionType[] memory actions, bytes[] memory datas, address from, address to, address who, bytes memory repayData ) external returns (PaymentData memory paymentData); /// @notice This function is a wrapper built on top of the function above. It enables users to interact with the contract /// without having to provide `who` and `repayData` parameters function angle( ActionType[] memory actions, bytes[] memory datas, address from, address to ) external returns (PaymentData memory paymentData); /// @notice Initializes the `VaultManager` contract /// @param _treasury Treasury address handling the contract /// @param _collateral Collateral supported by this contract /// @param _oracle Oracle contract used /// @param _symbol Symbol used to define the `VaultManager` name and symbol /// @dev The parameters and the oracle are the only elements which could be modified once the /// contract has been initialized /// @dev For the contract to be fully initialized, governance needs to set the parameters for the liquidation /// boost function initialize( ITreasury _treasury, IERC20 _collateral, IOracle _oracle, VaultParameters calldata params, string memory _symbol ) external; /// @notice Minimum amount of debt a vault can have, expressed in `BASE_TOKENS` that is to say the base of the agTokens function dust() external view returns (uint256); } /// @title IVaultManagerStorage /// @author Angle Labs, Inc. /// @notice Interface for the `VaultManager` contract /// @dev This interface contains getters of the contract's public variables used by other contracts /// of this module interface IVaultManagerStorage { /// @notice Encodes the maximum ratio stablecoin/collateral a vault can have before being liquidated. It's what /// determines the minimum collateral ratio of a position function collateralFactor() external view returns (uint64); /// @notice Stablecoin handled by this contract. Another `VaultManager` contract could have /// the same rights as this `VaultManager` on the stablecoin contract function stablecoin() external view returns (IAgToken); /// @notice Reference to the `treasury` contract handling this `VaultManager` function treasury() external view returns (ITreasury); /// @notice Oracle contract to get access to the price of the collateral with respect to the stablecoin function oracle() external view returns (IOracle); /// @notice The `interestAccumulator` variable keeps track of the interest that should accrue to the protocol. /// The stored value is not necessarily the true value: this one is recomputed every time an action takes place /// within the protocol. It is in base `BASE_INTEREST` function interestAccumulator() external view returns (uint256); /// @notice Reference to the collateral handled by this `VaultManager` function collateral() external view returns (IERC20); /// @notice Total normalized amount of stablecoins borrowed, not taking into account the potential bad debt accumulated /// This value is expressed in the base of Angle stablecoins (`BASE_TOKENS = 10**18`) function totalNormalizedDebt() external view returns (uint256); /// @notice Maximum amount of stablecoins that can be issued with this contract. It is expressed in `BASE_TOKENS` function debtCeiling() external view returns (uint256); /// @notice Maps a `vaultID` to its data (namely collateral amount and normalized debt) function vaultData(uint256 vaultID) external view returns (uint256 collateralAmount, uint256 normalizedDebt); /// @notice ID of the last vault created. The `vaultIDCount` variables serves as a counter to generate a unique /// `vaultID` for each vault: it is like `tokenID` in basic ERC721 contracts function vaultIDCount() external view returns (uint256); } /// @title IVaultManager /// @author Angle Labs, Inc. /// @notice Interface for the `VaultManager` contract interface IVaultManager is IVaultManagerFunctions, IVaultManagerStorage, IERC721Metadata { function isApprovedOrOwner(address spender, uint256 vaultID) external view returns (bool); } /// @title IVaultManagerListing /// @author Angle Labs, Inc. /// @notice Interface for the `VaultManagerListing` contract interface IVaultManagerListing is IVaultManager { /// @notice Get the collateral owned by `user` in the contract /// @dev This function effectively sums the collateral amounts of all the vaults owned by `user` function getUserCollateral(address user) external view returns (uint256); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.12; /// @title Interface for verifying contract-based account signatures /// @notice Interface that verifies provided signature for the data /// @dev Interface defined by EIP-1271 interface IERC1271 { /// @notice Returns whether the provided signature is valid for the provided data /// @dev MUST return the bytes4 magic value 0x1626ba7e when function passes. /// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5). /// MUST allow external calls. /// @param hash Hash of the data to be signed /// @param signature Signature byte array associated with _data /// @return magicValue The bytes4 magic value 0x1626ba7e function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; /// @title IVeBoostProxy /// @author Angle Labs, Inc. /// @notice Interface for the `VeBoostProxy` contract /// @dev This interface only contains functions of the contract which are called by other contracts /// of this module /// @dev The `veBoostProxy` contract used by Angle is a full fork of Curve Finance implementation interface IVeBoostProxy { /// @notice Reads the adjusted veANGLE balance of an address (adjusted by delegation) //solhint-disable-next-line function adjusted_balance_of(address) external view returns (uint256); }
// SPDX-License-Identifier: GPL-3.0 /* * █ ***** ▓▓▓ * ▓▓▓▓▓▓▓ * ///. ▓▓▓▓▓▓▓▓▓▓▓▓▓ ***** //////// ▓▓▓▓▓▓▓ * ///////////// ▓▓▓ ▓▓ ////////////////// █ ▓▓ ▓▓ ▓▓ /////////////////////// ▓▓ ▓▓ ▓▓ ▓▓ //////////////////////////// ▓▓ ▓▓ ▓▓ ▓▓ /////////▓▓▓///////▓▓▓///////// ▓▓ ▓▓ ▓▓ ,////////////////////////////////////// ▓▓ ▓▓ ▓▓ ////////////////////////////////////////// ▓▓ ▓▓ //////////////////////▓▓▓▓///////////////////// ,//////////////////////////////////////////////////// .////////////////////////////////////////////////////////// .//////////////////////////██.,//////////////////////////█ .//////////////////////████..,./////////////////////██ ...////////////////███████.....,.////////////////███ ,.,////////////████████ ........,///////////████ .,.,//////█████████ ,.......///////████ ,..//████████ ........./████ ..,██████ .....,███ .██ ,.,█ ▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓ ▓▓ ▓▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓▓ ▓▓ ▓▓▓▓▓ ▓▓▓ ▓▓ ▓▓▓ ▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ */ pragma solidity ^0.8.12; import "./VaultManagerPermit.sol"; /// @title VaultManager /// @author Angle Labs, Inc. /// @notice This contract allows people to deposit collateral and open up loans of a given AgToken. It handles all the loan /// logic (fees and interest rate) as well as the liquidation logic /// @dev This implementation only supports non-rebasing ERC20 tokens as collateral /// @dev This contract is encoded as a NFT contract contract VaultManager is VaultManagerPermit, IVaultManagerFunctions { using SafeERC20 for IERC20; using Address for address; /// @inheritdoc IVaultManagerFunctions uint256 public dust; /// @notice Minimum amount of collateral (in stablecoin value, e.g in `BASE_TOKENS = 10**18`) that can be left /// in a vault during a liquidation where the health factor function is decreasing uint256 internal _dustCollateral; /// @notice If the amount of debt of a vault that gets liquidated is below this amount, then the liquidator /// can liquidate all the debt of the vault (and not just what's needed to get to the target health factor) uint256 public dustLiquidation; uint256[47] private __gapVaultManager; /// @inheritdoc IVaultManagerFunctions function initialize( ITreasury _treasury, IERC20 _collateral, IOracle _oracle, VaultParameters calldata params, string memory _symbol ) external initializer { if (_oracle.treasury() != _treasury) revert InvalidTreasury(); treasury = _treasury; collateral = _collateral; _collatBase = 10**(IERC20Metadata(address(collateral)).decimals()); stablecoin = IAgToken(_treasury.stablecoin()); oracle = _oracle; string memory _name = string.concat("Angle Protocol ", _symbol, " Vault"); name = _name; __ERC721Permit_init(_name); symbol = string.concat(_symbol, "-vault"); interestAccumulator = BASE_INTEREST; lastInterestAccumulatorUpdated = block.timestamp; // Checking if the parameters have been correctly initialized if ( params.collateralFactor > params.liquidationSurcharge || params.liquidationSurcharge > BASE_PARAMS || BASE_PARAMS > params.targetHealthFactor || params.maxLiquidationDiscount >= BASE_PARAMS || params.baseBoost == 0 ) revert InvalidSetOfParameters(); debtCeiling = params.debtCeiling; collateralFactor = params.collateralFactor; targetHealthFactor = params.targetHealthFactor; interestRate = params.interestRate; liquidationSurcharge = params.liquidationSurcharge; maxLiquidationDiscount = params.maxLiquidationDiscount; whitelistingActivated = params.whitelistingActivated; yLiquidationBoost = [params.baseBoost]; paused = true; } // ================================= MODIFIERS ================================= /// @notice Checks whether the `msg.sender` has the governor role or not modifier onlyGovernor() { if (!treasury.isGovernor(msg.sender)) revert NotGovernor(); _; } /// @notice Checks whether the `msg.sender` has the governor role or the guardian role modifier onlyGovernorOrGuardian() { if (!treasury.isGovernorOrGuardian(msg.sender)) revert NotGovernorOrGuardian(); _; } /// @notice Checks whether the `msg.sender` is the treasury contract modifier onlyTreasury() { if (msg.sender != address(treasury)) revert NotTreasury(); _; } /// @notice Checks whether the contract is paused modifier whenNotPaused() { if (paused) revert Paused(); _; } // ============================== VAULT FUNCTIONS ============================== /// @inheritdoc IVaultManagerFunctions function createVault(address toVault) external whenNotPaused returns (uint256) { return _mint(toVault); } /// @inheritdoc IVaultManagerFunctions function angle( ActionType[] memory actions, bytes[] memory datas, address from, address to ) external returns (PaymentData memory) { return angle(actions, datas, from, to, address(0), new bytes(0)); } /// @inheritdoc IVaultManagerFunctions function angle( ActionType[] memory actions, bytes[] memory datas, address from, address to, address who, bytes memory repayData ) public whenNotPaused nonReentrant returns (PaymentData memory paymentData) { if (actions.length != datas.length || actions.length == 0) revert IncompatibleLengths(); // `newInterestAccumulator` and `oracleValue` are expensive to compute. Therefore, they are computed // only once inside the first action where they are necessary, then they are passed forward to further actions uint256 newInterestAccumulator; uint256 oracleValue; uint256 collateralAmount; uint256 stablecoinAmount; uint256 vaultID; for (uint256 i; i < actions.length; ++i) { ActionType action = actions[i]; // Processing actions which do not need the value of the oracle or of the `interestAccumulator` if (action == ActionType.createVault) { _mint(abi.decode(datas[i], (address))); } else if (action == ActionType.addCollateral) { (vaultID, collateralAmount) = abi.decode(datas[i], (uint256, uint256)); if (vaultID == 0) vaultID = vaultIDCount; _addCollateral(vaultID, collateralAmount); paymentData.collateralAmountToReceive += collateralAmount; } else if (action == ActionType.permit) { address owner; bytes32 r; bytes32 s; // Watch out naming conventions for permit are not respected to save some space and reduce the stack size // `vaultID` is used in place of the `deadline` parameter // Same for `collateralAmount` used in place of `value` // `stablecoinAmount` is used in place of the `v` (owner, collateralAmount, vaultID, stablecoinAmount, r, s) = abi.decode( datas[i], (address, uint256, uint256, uint256, bytes32, bytes32) ); IERC20PermitUpgradeable(address(collateral)).permit( owner, address(this), collateralAmount, vaultID, uint8(stablecoinAmount), r, s ); } else { // Processing actions which rely on the `interestAccumulator`: first accruing it to make // sure surplus is correctly taken into account between debt changes if (newInterestAccumulator == 0) newInterestAccumulator = _accrue(); if (action == ActionType.repayDebt) { (vaultID, stablecoinAmount) = abi.decode(datas[i], (uint256, uint256)); if (vaultID == 0) vaultID = vaultIDCount; stablecoinAmount = _repayDebt(vaultID, stablecoinAmount, newInterestAccumulator); uint256 stablecoinAmountPlusRepayFee = (stablecoinAmount * BASE_PARAMS) / (BASE_PARAMS - repayFee); surplus += stablecoinAmountPlusRepayFee - stablecoinAmount; paymentData.stablecoinAmountToReceive += stablecoinAmountPlusRepayFee; } else { // Processing actions which need the oracle value if (oracleValue == 0) oracleValue = oracle.read(); if (action == ActionType.closeVault) { vaultID = abi.decode(datas[i], (uint256)); if (vaultID == 0) vaultID = vaultIDCount; (stablecoinAmount, collateralAmount) = _closeVault( vaultID, oracleValue, newInterestAccumulator ); paymentData.collateralAmountToGive += collateralAmount; paymentData.stablecoinAmountToReceive += stablecoinAmount; } else if (action == ActionType.removeCollateral) { (vaultID, collateralAmount) = abi.decode(datas[i], (uint256, uint256)); if (vaultID == 0) vaultID = vaultIDCount; _removeCollateral(vaultID, collateralAmount, oracleValue, newInterestAccumulator); paymentData.collateralAmountToGive += collateralAmount; } else if (action == ActionType.borrow) { (vaultID, stablecoinAmount) = abi.decode(datas[i], (uint256, uint256)); if (vaultID == 0) vaultID = vaultIDCount; stablecoinAmount = _borrow(vaultID, stablecoinAmount, oracleValue, newInterestAccumulator); paymentData.stablecoinAmountToGive += stablecoinAmount; } else if (action == ActionType.getDebtIn) { address vaultManager; uint256 dstVaultID; (vaultID, vaultManager, dstVaultID, stablecoinAmount) = abi.decode( datas[i], (uint256, address, uint256, uint256) ); if (vaultID == 0) vaultID = vaultIDCount; _getDebtIn( vaultID, IVaultManager(vaultManager), dstVaultID, stablecoinAmount, oracleValue, newInterestAccumulator ); } } } } // Processing the different cases for the repayment, there are 4 of them: // - (1) Stablecoins to receive + collateral to send // - (2) Stablecoins to receive + collateral to receive // - (3) Stablecoins to send + collateral to send // - (4) Stablecoins to send + collateral to receive if (paymentData.stablecoinAmountToReceive >= paymentData.stablecoinAmountToGive) { uint256 stablecoinPayment = paymentData.stablecoinAmountToReceive - paymentData.stablecoinAmountToGive; if (paymentData.collateralAmountToGive >= paymentData.collateralAmountToReceive) { // In the case where all amounts are null, the function will enter here and nothing will be done // for the repayment _handleRepay( // Collateral payment is the difference between what to give and what to receive paymentData.collateralAmountToGive - paymentData.collateralAmountToReceive, stablecoinPayment, from, to, who, repayData ); } else { if (stablecoinPayment != 0) stablecoin.burnFrom(stablecoinPayment, from, msg.sender); // In this case the collateral amount is necessarily non null collateral.safeTransferFrom( msg.sender, address(this), paymentData.collateralAmountToReceive - paymentData.collateralAmountToGive ); } } else { uint256 stablecoinPayment = paymentData.stablecoinAmountToGive - paymentData.stablecoinAmountToReceive; // `stablecoinPayment` is strictly positive in this case stablecoin.mint(to, stablecoinPayment); if (paymentData.collateralAmountToGive > paymentData.collateralAmountToReceive) { collateral.safeTransfer(to, paymentData.collateralAmountToGive - paymentData.collateralAmountToReceive); } else { uint256 collateralPayment = paymentData.collateralAmountToReceive - paymentData.collateralAmountToGive; if (collateralPayment != 0) { if (repayData.length != 0) { ISwapper(who).swap( IERC20(address(stablecoin)), collateral, msg.sender, // As per the `ISwapper` interface, we must first give the amount of token owed by the address before // the amount of token it (or another related address) obtained collateralPayment, stablecoinPayment, repayData ); } collateral.safeTransferFrom(msg.sender, address(this), collateralPayment); } } } } /// @inheritdoc IVaultManagerFunctions function getDebtOut( uint256 vaultID, uint256 stablecoinAmount, uint256 senderBorrowFee, uint256 senderRepayFee ) external whenNotPaused { if (!treasury.isVaultManager(msg.sender)) revert NotVaultManager(); // Getting debt out of a vault is equivalent to repaying a portion of your debt, and this could leave exploits: // someone could borrow from a vault and transfer its debt to a `VaultManager` contract where debt repayment will // be cheaper: in which case we're making people pay the delta uint256 _repayFee; if (repayFee > senderRepayFee) { _repayFee = repayFee - senderRepayFee; } // Checking the delta of borrow fees to eliminate the risk of exploits here: a similar thing could happen: people // could mint from where it is cheap to mint and then transfer their debt to places where it is more expensive // to mint uint256 _borrowFee; if (senderBorrowFee > borrowFee) { _borrowFee = senderBorrowFee - borrowFee; } uint256 stablecoinAmountLessFeePaid = (stablecoinAmount * (BASE_PARAMS - _repayFee) * (BASE_PARAMS - _borrowFee)) / (BASE_PARAMS**2); surplus += stablecoinAmount - stablecoinAmountLessFeePaid; _repayDebt(vaultID, stablecoinAmountLessFeePaid, 0); } // =============================== VIEW FUNCTIONS ============================== /// @inheritdoc IVaultManagerFunctions function getVaultDebt(uint256 vaultID) external view returns (uint256) { return (vaultData[vaultID].normalizedDebt * _calculateCurrentInterestAccumulator()) / BASE_INTEREST; } /// @inheritdoc IVaultManagerFunctions function getTotalDebt() external view returns (uint256) { return (totalNormalizedDebt * _calculateCurrentInterestAccumulator()) / BASE_INTEREST; } /// @notice Checks whether a given vault is liquidable and if yes gives information regarding its liquidation /// @param vaultID ID of the vault to check /// @param liquidator Address of the liquidator which will be performing the liquidation /// @return liqOpp Description of the opportunity of liquidation /// @dev This function will revert if it's called on a vault that does not exist function checkLiquidation(uint256 vaultID, address liquidator) external view returns (LiquidationOpportunity memory liqOpp) { liqOpp = _checkLiquidation( vaultData[vaultID], liquidator, oracle.read(), _calculateCurrentInterestAccumulator() ); } // ====================== INTERNAL UTILITY VIEW FUNCTIONS ====================== /// @notice Computes the health factor of a given vault. This can later be used to check whether a given vault is solvent /// (i.e. should be liquidated or not) /// @param vault Data of the vault to check /// @param oracleValue Oracle value at the time of the call (it is in the base of the stablecoin, that is for agTokens 10**18) /// @param newInterestAccumulator Value of the `interestAccumulator` at the time of the call /// @return healthFactor Health factor of the vault: if it's inferior to 1 (`BASE_PARAMS` in fact) this means that the vault can be liquidated /// @return currentDebt Current value of the debt of the vault (taking into account interest) /// @return collateralAmountInStable Collateral in the vault expressed in stablecoin value function _isSolvent( Vault memory vault, uint256 oracleValue, uint256 newInterestAccumulator ) internal view returns ( uint256 healthFactor, uint256 currentDebt, uint256 collateralAmountInStable ) { currentDebt = (vault.normalizedDebt * newInterestAccumulator) / BASE_INTEREST; collateralAmountInStable = (vault.collateralAmount * oracleValue) / _collatBase; if (currentDebt == 0) healthFactor = type(uint256).max; else healthFactor = (collateralAmountInStable * collateralFactor) / currentDebt; } /// @notice Calculates the current value of the `interestAccumulator` without updating the value /// in storage /// @dev This function avoids expensive exponentiation and the calculation is performed using a binomial approximation /// (1+x)^n = 1+n*x+[n/2*(n-1)]*x^2+[n/6*(n-1)*(n-2)*x^3... /// @dev The approximation slightly undercharges borrowers with the advantage of a great gas cost reduction /// @dev This function was mostly inspired from Aave implementation function _calculateCurrentInterestAccumulator() internal view returns (uint256) { uint256 exp = block.timestamp - lastInterestAccumulatorUpdated; uint256 ratePerSecond = interestRate; if (exp == 0 || ratePerSecond == 0) return interestAccumulator; uint256 expMinusOne = exp - 1; uint256 expMinusTwo = exp > 2 ? exp - 2 : 0; uint256 basePowerTwo = (ratePerSecond * ratePerSecond + HALF_BASE_INTEREST) / BASE_INTEREST; uint256 basePowerThree = (basePowerTwo * ratePerSecond + HALF_BASE_INTEREST) / BASE_INTEREST; uint256 secondTerm = (exp * expMinusOne * basePowerTwo) / 2; uint256 thirdTerm = (exp * expMinusOne * expMinusTwo * basePowerThree) / 6; return (interestAccumulator * (BASE_INTEREST + ratePerSecond * exp + secondTerm + thirdTerm)) / BASE_INTEREST; } // ================= INTERNAL UTILITY STATE-MODIFYING FUNCTIONS ================ /// @notice Closes a vault without handling the repayment of the concerned address /// @param vaultID ID of the vault to close /// @param oracleValue Oracle value at the start of the call /// @param newInterestAccumulator Interest rate accumulator value at the start of the call /// @return Current debt of the vault to be repaid /// @return Value of the collateral in the vault to reimburse /// @dev The returned values are here to facilitate composability between calls function _closeVault( uint256 vaultID, uint256 oracleValue, uint256 newInterestAccumulator ) internal onlyApprovedOrOwner(msg.sender, vaultID) returns (uint256, uint256) { Vault memory vault = vaultData[vaultID]; (uint256 healthFactor, uint256 currentDebt, ) = _isSolvent(vault, oracleValue, newInterestAccumulator); if (healthFactor <= BASE_PARAMS) revert InsolventVault(); totalNormalizedDebt -= vault.normalizedDebt; _burn(vaultID); uint256 currentDebtPlusRepayFee = (currentDebt * BASE_PARAMS) / (BASE_PARAMS - repayFee); surplus += currentDebtPlusRepayFee - currentDebt; return (currentDebtPlusRepayFee, vault.collateralAmount); } /// @notice Increases the collateral balance of a vault /// @param vaultID ID of the vault to increase the collateral balance of /// @param collateralAmount Amount by which increasing the collateral balance of function _addCollateral(uint256 vaultID, uint256 collateralAmount) internal { if (!_exists(vaultID)) revert NonexistentVault(); _checkpointCollateral(vaultID, collateralAmount, true); vaultData[vaultID].collateralAmount += collateralAmount; emit CollateralAmountUpdated(vaultID, collateralAmount, 1); } /// @notice Decreases the collateral balance from a vault (without proceeding to collateral transfers) /// @param vaultID ID of the vault to decrease the collateral balance of /// @param collateralAmount Amount of collateral to reduce the balance of /// @param oracleValue Oracle value at the start of the call (given here to avoid double computations) /// @param interestAccumulator_ Value of the interest rate accumulator (potentially zero if it has not been /// computed yet) function _removeCollateral( uint256 vaultID, uint256 collateralAmount, uint256 oracleValue, uint256 interestAccumulator_ ) internal onlyApprovedOrOwner(msg.sender, vaultID) { _checkpointCollateral(vaultID, collateralAmount, false); vaultData[vaultID].collateralAmount -= collateralAmount; (uint256 healthFactor, , ) = _isSolvent(vaultData[vaultID], oracleValue, interestAccumulator_); if (healthFactor <= BASE_PARAMS) revert InsolventVault(); emit CollateralAmountUpdated(vaultID, collateralAmount, 0); } /// @notice Increases the debt balance of a vault and takes into account borrowing fees /// @param vaultID ID of the vault to increase borrow balance of /// @param stablecoinAmount Amount of stablecoins to borrow /// @param oracleValue Oracle value at the start of the call /// @param newInterestAccumulator Value of the interest rate accumulator /// @return toMint Amount of stablecoins to mint function _borrow( uint256 vaultID, uint256 stablecoinAmount, uint256 oracleValue, uint256 newInterestAccumulator ) internal onlyApprovedOrOwner(msg.sender, vaultID) returns (uint256 toMint) { stablecoinAmount = _increaseDebt(vaultID, stablecoinAmount, oracleValue, newInterestAccumulator); uint256 borrowFeePaid = (borrowFee * stablecoinAmount) / BASE_PARAMS; surplus += borrowFeePaid; toMint = stablecoinAmount - borrowFeePaid; } /// @notice Gets debt in a vault from another vault potentially in another `VaultManager` contract /// @param srcVaultID ID of the vault from this contract for which growing debt /// @param vaultManager Address of the `VaultManager` where the targeted vault is /// @param dstVaultID ID of the vault in the target contract /// @param stablecoinAmount Amount of stablecoins to grow the debt of. This amount will be converted /// to a normalized value in both `VaultManager` contracts /// @param oracleValue Oracle value at the start of the call (potentially zero if it has not been computed yet) /// @param newInterestAccumulator Value of the interest rate accumulator (potentially zero if it has not been /// computed yet) /// @dev A solvency check is performed after the debt increase in the source `vaultID` /// @dev Only approved addresses by the source vault owner can perform this action, however any vault /// from any vaultManager contract can see its debt reduced by this means function _getDebtIn( uint256 srcVaultID, IVaultManager vaultManager, uint256 dstVaultID, uint256 stablecoinAmount, uint256 oracleValue, uint256 newInterestAccumulator ) internal onlyApprovedOrOwner(msg.sender, srcVaultID) { emit DebtTransferred(srcVaultID, dstVaultID, address(vaultManager), stablecoinAmount); // The `stablecoinAmount` needs to be rounded down in the `_increaseDebt` function to reduce the room for exploits stablecoinAmount = _increaseDebt(srcVaultID, stablecoinAmount, oracleValue, newInterestAccumulator); if (address(vaultManager) == address(this)) { // No repayFees taken in this case, otherwise the same stablecoin may end up paying fees twice _repayDebt(dstVaultID, stablecoinAmount, newInterestAccumulator); } else { // No need to check the integrity of `VaultManager` here because `_getDebtIn` can be entered only through the // `angle` function which is non reentrant. Also, `getDebtOut` failing would be at the attacker loss, as they // would get their debt increasing in the current vault without decreasing it in the remote vault. vaultManager.getDebtOut(dstVaultID, stablecoinAmount, borrowFee, repayFee); } } /// @notice Increases the debt of a given vault and verifies that this vault is still solvent /// @param vaultID ID of the vault to increase the debt of /// @param stablecoinAmount Amount of stablecoin to increase the debt of: this amount is converted in /// normalized debt using the pre-computed (or not) `newInterestAccumulator` value /// @param oracleValue Oracle value at the start of the call (given here to avoid double computations) /// @param newInterestAccumulator Value of the interest rate accumulator (potentially zero if it has not been /// computed yet) /// @return Amount of stablecoins to issue from this debt increase /// @dev The `stablecoinAmount` outputted need to be rounded down with respect to the change amount so that /// amount of stablecoins minted is smaller than the debt increase function _increaseDebt( uint256 vaultID, uint256 stablecoinAmount, uint256 oracleValue, uint256 newInterestAccumulator ) internal returns (uint256) { // We normalize the amount by dividing it by `newInterestAccumulator`. This makes accounting easier, since // it allows us to process all (past and future) debts like debts created at the inception of the contract. uint256 changeAmount = (stablecoinAmount * BASE_INTEREST) / newInterestAccumulator; // if there was no previous debt, we have to check that the debt creation will be higher than `dust` if (vaultData[vaultID].normalizedDebt == 0) if (stablecoinAmount <= dust) revert DustyLeftoverAmount(); vaultData[vaultID].normalizedDebt += changeAmount; totalNormalizedDebt += changeAmount; if (totalNormalizedDebt * newInterestAccumulator > debtCeiling * BASE_INTEREST) revert DebtCeilingExceeded(); (uint256 healthFactor, , ) = _isSolvent(vaultData[vaultID], oracleValue, newInterestAccumulator); if (healthFactor <= BASE_PARAMS) revert InsolventVault(); emit InternalDebtUpdated(vaultID, changeAmount, 1); return (changeAmount * newInterestAccumulator) / BASE_INTEREST; } /// @notice Decreases the debt of a given vault and verifies that this vault still has an amount of debt superior /// to a dusty amount or no debt at all /// @param vaultID ID of the vault to decrease the debt of /// @param stablecoinAmount Amount of stablecoin to decrease the debt of: this amount is converted in /// normalized debt using the pre-computed (or not) `newInterestAccumulator` value /// To repay the whole debt, one can pass `type(uint256).max` /// @param newInterestAccumulator Value of the interest rate accumulator (potentially zero if it has not been /// computed yet, like in `getDebtOut`) /// @return Amount of stablecoins to be burnt to correctly repay the debt /// @dev If `stablecoinAmount` is `type(uint256).max`, this function will repay all the debt of the vault function _repayDebt( uint256 vaultID, uint256 stablecoinAmount, uint256 newInterestAccumulator ) internal returns (uint256) { if (newInterestAccumulator == 0) newInterestAccumulator = _accrue(); uint256 newVaultNormalizedDebt = vaultData[vaultID].normalizedDebt; // To save one variable declaration, `changeAmount` is first expressed in stablecoin amount before being converted // to a normalized amount. Here we first store the maximum amount that can be repaid given the current debt uint256 changeAmount = (newVaultNormalizedDebt * newInterestAccumulator) / BASE_INTEREST; // In some situations (e.g. liquidations), the `stablecoinAmount` is rounded above and we want to make // sure to avoid underflows in all situations if (stablecoinAmount >= changeAmount) { stablecoinAmount = changeAmount; changeAmount = newVaultNormalizedDebt; } else { changeAmount = (stablecoinAmount * BASE_INTEREST) / newInterestAccumulator; } newVaultNormalizedDebt -= changeAmount; totalNormalizedDebt -= changeAmount; if (newVaultNormalizedDebt != 0 && newVaultNormalizedDebt * newInterestAccumulator <= dust * BASE_INTEREST) revert DustyLeftoverAmount(); vaultData[vaultID].normalizedDebt = newVaultNormalizedDebt; emit InternalDebtUpdated(vaultID, changeAmount, 0); return stablecoinAmount; } /// @notice Handles the simultaneous repayment of stablecoins with a transfer of collateral /// @param collateralAmountToGive Amount of collateral the contract should give /// @param stableAmountToRepay Amount of stablecoins the contract should burn from the call /// @param from Address from which stablecoins should be burnt: it should be the `msg.sender` or at least /// approved by it /// @param to Address to which collateral should be sent /// @param who Address which should be notified if needed of the transfer /// @param data Data to pass to the `who` contract for it to successfully give the correct amount of stablecoins /// to the `from` address /// @dev This function allows for capital-efficient liquidations and repayments of loans function _handleRepay( uint256 collateralAmountToGive, uint256 stableAmountToRepay, address from, address to, address who, bytes memory data ) internal { if (collateralAmountToGive != 0) collateral.safeTransfer(to, collateralAmountToGive); if (stableAmountToRepay != 0) { if (data.length != 0) { ISwapper(who).swap( collateral, IERC20(address(stablecoin)), from, stableAmountToRepay, collateralAmountToGive, data ); } stablecoin.burnFrom(stableAmountToRepay, from, msg.sender); } } // ====================== TREASURY RELATIONSHIP FUNCTIONS ====================== /// @inheritdoc IVaultManagerFunctions function accrueInterestToTreasury() external onlyTreasury returns (uint256 surplusValue, uint256 badDebtValue) { _accrue(); surplusValue = surplus; badDebtValue = badDebt; surplus = 0; badDebt = 0; if (surplusValue >= badDebtValue) { surplusValue -= badDebtValue; badDebtValue = 0; stablecoin.mint(address(treasury), surplusValue); } else { badDebtValue -= surplusValue; surplusValue = 0; } emit AccruedToTreasury(surplusValue, badDebtValue); } /// @notice Accrues interest accumulated across all vaults to the surplus and updates the `interestAccumulator` /// @return newInterestAccumulator Computed value of the interest accumulator /// @dev It should also be called when updating the value of the per second interest rate or when the `totalNormalizedDebt` /// value is about to change function _accrue() internal returns (uint256 newInterestAccumulator) { newInterestAccumulator = _calculateCurrentInterestAccumulator(); uint256 interestAccrued = (totalNormalizedDebt * (newInterestAccumulator - interestAccumulator)) / BASE_INTEREST; surplus += interestAccrued; interestAccumulator = newInterestAccumulator; lastInterestAccumulatorUpdated = block.timestamp; emit InterestAccumulatorUpdated(newInterestAccumulator, block.timestamp); return newInterestAccumulator; } // ================================ LIQUIDATIONS =============================== /// @notice Liquidates an ensemble of vaults specified by their IDs /// @dev This function is a simplified wrapper of the function below. It is built to remove for liquidators the need to specify /// a `who` and a `data` parameter function liquidate( uint256[] memory vaultIDs, uint256[] memory amounts, address from, address to ) external returns (LiquidatorData memory) { return liquidate(vaultIDs, amounts, from, to, address(0), new bytes(0)); } /// @notice Liquidates an ensemble of vaults specified by their IDs /// @param vaultIDs List of the vaults to liquidate /// @param amounts Amount of stablecoin to bring for the liquidation of each vault /// @param from Address from which the stablecoins for the liquidation should be taken: this address should be the `msg.sender` /// or have received an approval /// @param to Address to which discounted collateral should be sent /// @param who Address of the contract to handle repayment of stablecoins from received collateral /// @param data Data to pass to the repayment contract in case of. If empty, liquidators simply have to bring the exact amount of /// stablecoins to get the discounted collateral. If not, it is used by the repayment contract to swap a portion or all /// of the collateral received to stablecoins to be sent to the `from` address. More details in the `_handleRepay` function /// @return liqData Data about the liquidation process for the liquidator to track everything that has been going on (like how much /// stablecoins have been repaid, how much collateral has been received) /// @dev This function will revert if it's called on a vault that cannot be liquidated or that does not exist function liquidate( uint256[] memory vaultIDs, uint256[] memory amounts, address from, address to, address who, bytes memory data ) public whenNotPaused nonReentrant returns (LiquidatorData memory liqData) { uint256 vaultIDsLength = vaultIDs.length; if (vaultIDsLength != amounts.length || vaultIDsLength == 0) revert IncompatibleLengths(); // Stores all the data about an ongoing liquidation of multiple vaults liqData.oracleValue = oracle.read(); liqData.newInterestAccumulator = _accrue(); emit LiquidatedVaults(vaultIDs); for (uint256 i; i < vaultIDsLength; ++i) { Vault memory vault = vaultData[vaultIDs[i]]; // Computing if liquidation can take place for a vault LiquidationOpportunity memory liqOpp = _checkLiquidation( vault, msg.sender, liqData.oracleValue, liqData.newInterestAccumulator ); // Makes sure not to leave a dusty amount in the vault by either not liquidating too much // or everything if ( (liqOpp.thresholdRepayAmount != 0 && amounts[i] >= liqOpp.thresholdRepayAmount) || amounts[i] > liqOpp.maxStablecoinAmountToRepay ) amounts[i] = liqOpp.maxStablecoinAmountToRepay; // liqOpp.discount stores in fact `1-discount` uint256 collateralReleased = (amounts[i] * BASE_PARAMS * _collatBase) / (liqOpp.discount * liqData.oracleValue); _checkpointCollateral( vaultIDs[i], vault.collateralAmount <= collateralReleased ? vault.collateralAmount : collateralReleased, false ); // Because we're rounding up in some divisions, `collateralReleased` can be greater than the `collateralAmount` of the vault // In this case, `stablecoinAmountToReceive` is still rounded up if (vault.collateralAmount <= collateralReleased) { // Liquidators should never get more collateral than what's in the vault collateralReleased = vault.collateralAmount; // Remove all the vault's debt (debt repayed + bad debt) from VaultManager totalDebt totalNormalizedDebt -= vault.normalizedDebt; // Reinitializing the `vaultID`: we're not burning the vault in this case for integration purposes delete vaultData[vaultIDs[i]]; { uint256 debtReimbursed = (amounts[i] * liquidationSurcharge) / BASE_PARAMS; liqData.badDebtFromLiquidation += debtReimbursed < liqOpp.currentDebt ? liqOpp.currentDebt - debtReimbursed : 0; } // There may be an edge case in which: `amounts[i] = (currentDebt * BASE_PARAMS) / surcharge + 1` // In this case, as long as `surcharge < BASE_PARAMS`, there cannot be any underflow in the operation // above emit InternalDebtUpdated(vaultIDs[i], vault.normalizedDebt, 0); } else { vaultData[vaultIDs[i]].collateralAmount -= collateralReleased; _repayDebt( vaultIDs[i], (amounts[i] * liquidationSurcharge) / BASE_PARAMS, liqData.newInterestAccumulator ); } liqData.collateralAmountToGive += collateralReleased; liqData.stablecoinAmountToReceive += amounts[i]; } // Normalization of good and bad debt is already handled in the `accrueInterestToTreasury` function surplus += (liqData.stablecoinAmountToReceive * (BASE_PARAMS - liquidationSurcharge)) / BASE_PARAMS; badDebt += liqData.badDebtFromLiquidation; _handleRepay(liqData.collateralAmountToGive, liqData.stablecoinAmountToReceive, from, to, who, data); } /// @notice Internal version of the `checkLiquidation` function /// @dev This function takes two additional parameters as when entering this function `oracleValue` /// and `newInterestAccumulator` should have always been computed function _checkLiquidation( Vault memory vault, address liquidator, uint256 oracleValue, uint256 newInterestAccumulator ) internal view returns (LiquidationOpportunity memory liqOpp) { // Checking if the vault can be liquidated (uint256 healthFactor, uint256 currentDebt, uint256 collateralAmountInStable) = _isSolvent( vault, oracleValue, newInterestAccumulator ); // Health factor of a vault that does not exist is `type(uint256).max` if (healthFactor >= BASE_PARAMS) revert HealthyVault(); uint256 liquidationDiscount = (_computeLiquidationBoost(liquidator) * (BASE_PARAMS - healthFactor)) / BASE_PARAMS; // In fact `liquidationDiscount` is stored here as 1 minus discount to save some computation costs // This value is necessarily != 0 as `maxLiquidationDiscount < BASE_PARAMS` liquidationDiscount = liquidationDiscount >= maxLiquidationDiscount ? BASE_PARAMS - maxLiquidationDiscount : BASE_PARAMS - liquidationDiscount; // Same for the surcharge here: it's in fact 1 - the fee taken by the protocol uint256 surcharge = liquidationSurcharge; uint256 maxAmountToRepay; uint256 thresholdRepayAmount; // Checking if we're in a situation where the health factor is an increasing or a decreasing function of the // amount repaid. In the first case, the health factor is an increasing function which means that the liquidator // can bring the vault to the target health ratio if (healthFactor * liquidationDiscount * surcharge >= collateralFactor * BASE_PARAMS**2) { // This is the max amount to repay that will bring the person to the target health factor // Denom is always positive when a vault gets liquidated in this case and when the health factor // is an increasing function of the amount of stablecoins repaid // And given that most parameters are in base 9, the numerator can very hardly overflow here maxAmountToRepay = ((targetHealthFactor * currentDebt - collateralAmountInStable * collateralFactor) * BASE_PARAMS * liquidationDiscount) / (surcharge * targetHealthFactor * liquidationDiscount - (BASE_PARAMS**2) * collateralFactor); // Need to check for the dustas liquidating should not leave a dusty amount in the vault uint256 dustParameter = dustLiquidation; if (currentDebt * BASE_PARAMS <= maxAmountToRepay * surcharge + dustParameter * BASE_PARAMS) { // If liquidating to the target threshold would leave a dusty amount: the liquidator can repay all. // We're avoiding here propagation of rounding errors and rounding up the max amount to repay to make // sure all the debt ends up being paid maxAmountToRepay = (vault.normalizedDebt * newInterestAccumulator * BASE_PARAMS) / (surcharge * BASE_INTEREST) + 1; // In this case the threshold amount is such that it leaves just enough dust: amount is rounded // down such that if a liquidator repays this amount then there is more than `dustLiquidation` left in // the liquidated vault if (currentDebt > dustParameter) thresholdRepayAmount = ((currentDebt - dustParameter) * BASE_PARAMS) / surcharge; // If there is from the beginning a dusty debt, then liquidator should repay everything that's left else thresholdRepayAmount = 1; } } else { // In this case, the liquidator can repay stablecoins such that they'll end up getting exactly the collateral // in the liquidated vault maxAmountToRepay = (vault.collateralAmount * liquidationDiscount * oracleValue) / (BASE_PARAMS * _collatBase) + 1; // It should however make sure not to leave a dusty amount of collateral (in stablecoin value) in the vault if (collateralAmountInStable > _dustCollateral) // There's no issue with this amount being rounded down thresholdRepayAmount = ((collateralAmountInStable - _dustCollateral) * liquidationDiscount) / BASE_PARAMS; // If there is from the beginning a dusty amount of collateral, liquidator should repay everything that's left else thresholdRepayAmount = 1; } liqOpp.maxStablecoinAmountToRepay = maxAmountToRepay; liqOpp.maxCollateralAmountGiven = (maxAmountToRepay * BASE_PARAMS * _collatBase) / (oracleValue * liquidationDiscount); liqOpp.thresholdRepayAmount = thresholdRepayAmount; liqOpp.discount = liquidationDiscount; liqOpp.currentDebt = currentDebt; } // ================================== SETTERS ================================== /// @notice Sets parameters encoded as uint64 /// @param param Value for the parameter /// @param what Parameter to change /// @dev This function performs the required checks when updating a parameter /// @dev When setting parameters governance or the guardian should make sure that when `HF < CF/((1-surcharge)(1-discount))` /// and hence when liquidating a vault is going to decrease its health factor, `discount = max discount`. /// Otherwise, it may be profitable for the liquidator to liquidate in multiple times: as it will decrease /// the HF and therefore increase the discount between each time function setUint64(uint64 param, bytes32 what) external onlyGovernorOrGuardian { if (what == "CF") { if (param > liquidationSurcharge) revert TooHighParameterValue(); collateralFactor = param; } else if (what == "THF") { if (param < BASE_PARAMS) revert TooSmallParameterValue(); targetHealthFactor = param; } else if (what == "BF") { if (param > BASE_PARAMS) revert TooHighParameterValue(); borrowFee = param; } else if (what == "RF") { // As liquidation surcharge is stored as `1-fee` and as we need `repayFee` to be smaller // than the liquidation surcharge, we need to have: // `liquidationSurcharge <= BASE_PARAMS - repayFee` and as such `liquidationSurcharge + repayFee <= BASE_PARAMS` if (param + liquidationSurcharge > BASE_PARAMS) revert TooHighParameterValue(); repayFee = param; } else if (what == "IR") { _accrue(); interestRate = param; } else if (what == "LS") { if (collateralFactor > param || param + repayFee > BASE_PARAMS) revert InvalidParameterValue(); liquidationSurcharge = param; } else if (what == "MLD") { if (param > BASE_PARAMS) revert TooHighParameterValue(); maxLiquidationDiscount = param; } else { revert InvalidParameterType(); } emit FiledUint64(param, what); } /// @notice Sets `debtCeiling` /// @param _debtCeiling New value for `debtCeiling` /// @dev `debtCeiling` should not be bigger than `type(uint256).max / 10**27` otherwise there could be overflows function setDebtCeiling(uint256 _debtCeiling) external onlyGovernorOrGuardian { debtCeiling = _debtCeiling; emit DebtCeilingUpdated(_debtCeiling); } /// @notice Sets the parameters for the liquidation booster which encodes the slope of the discount function setLiquidationBoostParameters( address _veBoostProxy, uint256[] memory xBoost, uint256[] memory yBoost ) external virtual onlyGovernorOrGuardian { if (yBoost[0] == 0) revert InvalidSetOfParameters(); yLiquidationBoost = yBoost; emit LiquidationBoostParametersUpdated(_veBoostProxy, xBoost, yBoost); } /// @notice Pauses external permissionless functions of the contract function togglePause() external onlyGovernorOrGuardian { paused = !paused; } /// @notice Changes the ERC721 metadata URI function setBaseURI(string memory baseURI_) external onlyGovernorOrGuardian { _baseURI = baseURI_; } /// @notice Changes the whitelisting of an address /// @param target Address to toggle /// @dev If the `target` address is the zero address then this function toggles whitelisting /// for all addresses function toggleWhitelist(address target) external onlyGovernor { if (target != address(0)) { isWhitelisted[target] = 1 - isWhitelisted[target]; } else { whitelistingActivated = !whitelistingActivated; } } /// @notice Changes the reference to the oracle contract used to get the price of the oracle /// @param _oracle Reference to the oracle contract function setOracle(address _oracle) external onlyGovernor { if (IOracle(_oracle).treasury() != treasury) revert InvalidTreasury(); oracle = IOracle(_oracle); } /// @notice Sets the dust variables /// @param _dust New minimum debt allowed /// @param _dustLiquidation New `dustLiquidation` value /// @param dustCollateral_ New minimum collateral allowed in a vault after a liquidation /// @dev dustCollateral_ is in stable value function setDusts( uint256 _dust, uint256 _dustLiquidation, uint256 dustCollateral_ ) external onlyGovernor { if (_dust > _dustLiquidation) revert InvalidParameterValue(); dust = _dust; dustLiquidation = _dustLiquidation; _dustCollateral = dustCollateral_; } /// @inheritdoc IVaultManagerFunctions function setTreasury(address _treasury) external onlyTreasury { treasury = ITreasury(_treasury); // This function makes sure to propagate the change to the associated contract // even though a single oracle contract could be used in different places oracle.setTreasury(_treasury); } // ============================= VIRTUAL FUNCTIONS ============================= /// @notice Returns the liquidation boost of a given address, that is the slope of the discount function /// @return The slope of the discount function function _computeLiquidationBoost(address) internal view virtual returns (uint256) { return yLiquidationBoost[0]; } /// @notice Hook called before any collateral internal changes /// @param vaultID Vault which sees its collateral amount changed /// @param amount Collateral amount balance of the owner of vaultID increase/decrease /// @param add Whether the balance should be increased/decreased /// @param vaultID Vault which sees its collateral amount changed function _checkpointCollateral( uint256 vaultID, uint256 amount, bool add ) internal virtual {} }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./VaultManagerStorage.sol"; /// @title VaultManagerERC721 /// @author Angle Labs, Inc. /// @dev Base ERC721 Implementation of VaultManager abstract contract VaultManagerERC721 is IERC721MetadataUpgradeable, VaultManagerStorage { using SafeERC20 for IERC20; using Address for address; /// @inheritdoc IERC721MetadataUpgradeable string public name; /// @inheritdoc IERC721MetadataUpgradeable string public symbol; // ================================= MODIFIERS ================================= /// @notice Checks if the person interacting with the vault with `vaultID` is approved /// @param caller Address of the person seeking to interact with the vault /// @param vaultID ID of the concerned vault modifier onlyApprovedOrOwner(address caller, uint256 vaultID) { if (!_isApprovedOrOwner(caller, vaultID)) revert NotApproved(); _; } // ================================ ERC721 LOGIC =============================== /// @notice Checks whether a given address is approved for a vault or owns this vault /// @param spender Address for which vault ownership should be checked /// @param vaultID ID of the vault to check /// @return Whether the `spender` address owns or is approved for `vaultID` function isApprovedOrOwner(address spender, uint256 vaultID) external view returns (bool) { return _isApprovedOrOwner(spender, vaultID); } /// @inheritdoc IERC721MetadataUpgradeable function tokenURI(uint256 vaultID) external view returns (string memory) { if (!_exists(vaultID)) revert NonexistentVault(); // There is no vault with `vaultID` equal to 0, so the following variable is // always greater than zero uint256 temp = vaultID; uint256 digits; while (temp != 0) { ++digits; temp /= 10; } bytes memory buffer = new bytes(digits); while (vaultID != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(vaultID % 10))); vaultID /= 10; } return bytes(_baseURI).length != 0 ? string(abi.encodePacked(_baseURI, string(buffer))) : ""; } /// @inheritdoc IERC721Upgradeable function balanceOf(address owner) external view returns (uint256) { if (owner == address(0)) revert ZeroAddress(); return _balances[owner]; } /// @inheritdoc IERC721Upgradeable function ownerOf(uint256 vaultID) external view returns (address) { return _ownerOf(vaultID); } /// @inheritdoc IERC721Upgradeable function approve(address to, uint256 vaultID) external { address owner = _ownerOf(vaultID); if (to == owner) revert ApprovalToOwner(); if (msg.sender != owner && !isApprovedForAll(owner, msg.sender)) revert NotApproved(); _approve(to, vaultID); } /// @inheritdoc IERC721Upgradeable function getApproved(uint256 vaultID) external view returns (address) { if (!_exists(vaultID)) revert NonexistentVault(); return _getApproved(vaultID); } /// @inheritdoc IERC721Upgradeable function setApprovalForAll(address operator, bool approved) external { _setApprovalForAll(msg.sender, operator, approved); } /// @inheritdoc IERC721Upgradeable function isApprovedForAll(address owner, address operator) public view returns (bool) { return _operatorApprovals[owner][operator] == 1; } /// @inheritdoc IERC721Upgradeable function transferFrom( address from, address to, uint256 vaultID ) external onlyApprovedOrOwner(msg.sender, vaultID) { _transfer(from, to, vaultID); } /// @inheritdoc IERC721Upgradeable function safeTransferFrom( address from, address to, uint256 vaultID ) external { safeTransferFrom(from, to, vaultID, ""); } /// @inheritdoc IERC721Upgradeable function safeTransferFrom( address from, address to, uint256 vaultID, bytes memory _data ) public onlyApprovedOrOwner(msg.sender, vaultID) { _safeTransfer(from, to, vaultID, _data); } // ================================ ERC165 LOGIC =============================== /// @inheritdoc IERC165Upgradeable function supportsInterface(bytes4 interfaceId) external pure returns (bool) { return interfaceId == type(IERC721MetadataUpgradeable).interfaceId || interfaceId == type(IERC721Upgradeable).interfaceId || interfaceId == type(IVaultManager).interfaceId || interfaceId == type(IERC165Upgradeable).interfaceId; } // ================== INTERNAL FUNCTIONS FOR THE ERC721 LOGIC ================== /// @notice Internal version of the `ownerOf` function function _ownerOf(uint256 vaultID) internal view returns (address owner) { owner = _owners[vaultID]; if (owner == address(0)) revert NonexistentVault(); } /// @notice Internal version of the `getApproved` function function _getApproved(uint256 vaultID) internal view returns (address) { return _vaultApprovals[vaultID]; } /// @notice Internal version of the `safeTransferFrom` function (with the data parameter) function _safeTransfer( address from, address to, uint256 vaultID, bytes memory _data ) internal { _transfer(from, to, vaultID); if (!_checkOnERC721Received(from, to, vaultID, _data)) revert NonERC721Receiver(); } /// @notice Checks whether a vault exists /// @param vaultID ID of the vault to check /// @return Whether `vaultID` has been created function _exists(uint256 vaultID) internal view returns (bool) { return _owners[vaultID] != address(0); } /// @notice Internal version of the `isApprovedOrOwner` function function _isApprovedOrOwner(address spender, uint256 vaultID) internal view returns (bool) { // The following checks if the vault exists address owner = _ownerOf(vaultID); return (spender == owner || _getApproved(vaultID) == spender || _operatorApprovals[owner][spender] == 1); } /// @notice Internal version of the `createVault` function /// Mints `vaultID` and transfers it to `to` /// @dev This method is equivalent to the `_safeMint` method used in OpenZeppelin ERC721 contract /// @dev Emits a {Transfer} event function _mint(address to) internal returns (uint256 vaultID) { if (whitelistingActivated && (isWhitelisted[to] != 1 || isWhitelisted[msg.sender] != 1)) revert NotWhitelisted(); if (to == address(0)) revert ZeroAddress(); unchecked { vaultIDCount += 1; } vaultID = vaultIDCount; _beforeTokenTransfer(address(0), to, vaultID); unchecked { _balances[to] += 1; } _owners[vaultID] = to; emit Transfer(address(0), to, vaultID); if (!_checkOnERC721Received(address(0), to, vaultID, "")) revert NonERC721Receiver(); } /// @notice Destroys `vaultID` /// @dev `vaultID` must exist /// @dev Emits a {Transfer} event function _burn(uint256 vaultID) internal { address owner = _ownerOf(vaultID); _beforeTokenTransfer(owner, address(0), vaultID); // Clear approvals _approve(address(0), vaultID); // The following line cannot underflow as the owner's balance is necessarily // greater than 1 unchecked { _balances[owner] -= 1; } delete _owners[vaultID]; delete vaultData[vaultID]; emit Transfer(owner, address(0), vaultID); } /// @notice Transfers `vaultID` from `from` to `to` as opposed to {transferFrom}, /// this imposes no restrictions on msg.sender /// @dev `to` cannot be the zero address and `perpetualID` must be owned by `from` /// @dev Emits a {Transfer} event /// @dev A whitelist check is performed if necessary on the `to` address function _transfer( address from, address to, uint256 vaultID ) internal { if (_ownerOf(vaultID) != from) revert NotApproved(); if (to == address(0)) revert ZeroAddress(); if (whitelistingActivated && isWhitelisted[to] != 1) revert NotWhitelisted(); _beforeTokenTransfer(from, to, vaultID); // Clear approvals from the previous owner _approve(address(0), vaultID); unchecked { _balances[from] -= 1; _balances[to] += 1; } _owners[vaultID] = to; emit Transfer(from, to, vaultID); } /// @notice Approves `to` to operate on `vaultID` function _approve(address to, uint256 vaultID) internal { _vaultApprovals[vaultID] = to; emit Approval(_ownerOf(vaultID), to, vaultID); } /// @notice Internal version of the `setApprovalForAll` function /// @dev It contains an `approver` field to be used in case someone signs a permit for a particular /// address, and this signature is given to the contract by another address (like a router) function _setApprovalForAll( address approver, address operator, bool approved ) internal { if (operator == approver) revert ApprovalToCaller(); uint256 approval = approved ? 1 : 0; _operatorApprovals[approver][operator] = approval; emit ApprovalForAll(approver, operator, approved); } /// @notice Internal function to invoke {IERC721Receiver-onERC721Received} on a target address /// The call is not executed if the target address is not a contract /// @param from Address representing the previous owner of the given token ID /// @param to Target address that will receive the tokens /// @param vaultID ID of the token to be transferred /// @param _data Bytes optional data to send along with the call /// @return Bool whether the call correctly returned the expected value function _checkOnERC721Received( address from, address to, uint256 vaultID, bytes memory _data ) private returns (bool) { if (to.isContract()) { try IERC721ReceiverUpgradeable(to).onERC721Received(msg.sender, from, vaultID, _data) returns ( bytes4 retval ) { return retval == IERC721ReceiverUpgradeable.onERC721Received.selector; } catch (bytes memory reason) { if (reason.length == 0) { revert NonERC721Receiver(); } else { // solhint-disable-next-line no-inline-assembly assembly { revert(add(32, reason), mload(reason)) } } } } else { return true; } } /// @notice Hook that is called before any token transfer. This includes minting and burning. /// Calling conditions: /// /// - When `from` and `to` are both non-zero, `from`'s `vaultID` will be /// transferred to `to`. /// - When `from` is zero, `vaultID` will be minted for `to`. /// - When `to` is zero, `from`'s `vaultID` will be burned. /// - `from` and `to` are never both zero. function _beforeTokenTransfer( address from, address to, uint256 vaultID ) internal virtual {} }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "./VaultManagerERC721.sol"; import "../interfaces/external/IERC1271.sol"; /// @title VaultManagerPermit /// @author Angle Labs, Inc. /// @dev Base Implementation of permit functions for the `VaultManager` contract abstract contract VaultManagerPermit is Initializable, VaultManagerERC721 { using Address for address; mapping(address => uint256) private _nonces; /* solhint-disable var-name-mixedcase */ bytes32 private _HASHED_NAME; bytes32 private _HASHED_VERSION; bytes32 private _PERMIT_TYPEHASH; /* solhint-enable var-name-mixedcase */ error ExpiredDeadline(); error InvalidSignature(); //solhint-disable-next-line function __ERC721Permit_init(string memory _name) internal onlyInitializing { _PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,bool approved,uint256 nonce,uint256 deadline)" ); _HASHED_NAME = keccak256(bytes(_name)); _HASHED_VERSION = keccak256(bytes("1")); } /// @notice Allows an address to give or revoke approval for all its vaults to another address /// @param owner Address signing the permit and giving (or revoking) its approval for all the controlled vaults /// @param spender Address to give approval to /// @param approved Whether to give or revoke the approval /// @param deadline Deadline parameter for the signature to be valid /// @dev The `v`, `r`, and `s` parameters are used as signature data function permit( address owner, address spender, bool approved, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external { if (block.timestamp > deadline) revert ExpiredDeadline(); // Additional signature checks performed in the `ECDSAUpgradeable.recover` function if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0 || (v != 27 && v != 28)) revert InvalidSignature(); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", _domainSeparatorV4(), keccak256( abi.encode( _PERMIT_TYPEHASH, // 0x3f43a9c6bafb5c7aab4e0cfe239dc5d4c15caf0381c6104188191f78a6640bd8, owner, spender, approved, _useNonce(owner), deadline ) ) ) ); if (owner.isContract()) { if (IERC1271(owner).isValidSignature(digest, abi.encodePacked(r, s, v)) != 0x1626ba7e) revert InvalidSignature(); } else { address signer = ecrecover(digest, v, r, s); if (signer != owner || signer == address(0)) revert InvalidSignature(); } _setApprovalForAll(owner, spender, approved); } /// @notice Returns the current nonce for an `owner` address function nonces(address owner) public view returns (uint256) { return _nonces[owner]; } /// @notice Returns the domain separator for the current chain. // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32) { return _domainSeparatorV4(); } /// @notice Internal version of the `DOMAIN_SEPARATOR` function function _domainSeparatorV4() internal view returns (bytes32) { return keccak256( abi.encode( // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)') 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f, _HASHED_NAME, _HASHED_VERSION, block.chainid, address(this) ) ); } /// @notice Consumes a nonce for an address: returns the current value and increments it function _useNonce(address owner) internal returns (uint256 current) { current = _nonces[owner]; _nonces[owner] = current + 1; } uint256[49] private __gap; }
// SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.12; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/interfaces/IERC721MetadataUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/introspection/IERC165Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../interfaces/IAgToken.sol"; import "../interfaces/IOracle.sol"; import "../interfaces/ISwapper.sol"; import "../interfaces/ITreasury.sol"; import "../interfaces/IVaultManager.sol"; import "../interfaces/governance/IVeBoostProxy.sol"; /// @title VaultManagerStorage /// @author Angle Labs, Inc. /// @dev Variables, references, parameters and events needed in the `VaultManager` contract // solhint-disable-next-line max-states-count contract VaultManagerStorage is IVaultManagerStorage, Initializable, ReentrancyGuardUpgradeable { /// @notice Base used for parameter computation: almost all the parameters of this contract are set in `BASE_PARAMS` uint256 public constant BASE_PARAMS = 10**9; /// @notice Base used for interest rate computation uint256 public constant BASE_INTEREST = 10**27; /// @notice Used for interest rate computation uint256 public constant HALF_BASE_INTEREST = 10**27 / 2; // ================================= REFERENCES ================================ /// @inheritdoc IVaultManagerStorage ITreasury public treasury; /// @inheritdoc IVaultManagerStorage IERC20 public collateral; /// @inheritdoc IVaultManagerStorage IAgToken public stablecoin; /// @inheritdoc IVaultManagerStorage IOracle public oracle; /// @notice Reference to the contract which computes adjusted veANGLE balances for liquidators boosts IVeBoostProxy public veBoostProxy; /// @notice Base of the collateral uint256 internal _collatBase; // ================================= PARAMETERS ================================ // Unless specified otherwise, parameters of this contract are expressed in `BASE_PARAMS` /// @notice Maximum amount of stablecoins that can be issued with this contract (in `BASE_TOKENS`). This parameter should /// not be bigger than `type(uint256).max / BASE_INTEREST` otherwise there may be some overflows in the `increaseDebt` function uint256 public debtCeiling; /// @notice Threshold veANGLE balance values for the computation of the boost for liquidators: the length of this array /// should normally be 2. The base of the x-values in this array should be `BASE_TOKENS` uint256[] public xLiquidationBoost; /// @notice Values of the liquidation boost at the threshold values of x uint256[] public yLiquidationBoost; /// @inheritdoc IVaultManagerStorage uint64 public collateralFactor; /// @notice Maximum Health factor at which a vault can end up after a liquidation (unless it's fully liquidated) uint64 public targetHealthFactor; /// @notice Upfront fee taken when borrowing stablecoins: this fee is optional and should in practice not be used uint64 public borrowFee; /// @notice Upfront fee taken when repaying stablecoins: this fee is optional as well. It should be smaller /// than the liquidation surcharge (cf below) to avoid exploits where people voluntarily get liquidated at a 0 /// discount to pay smaller repaying fees uint64 public repayFee; /// @notice Per second interest taken to borrowers taking agToken loans. Contrarily to other parameters, it is set in `BASE_INTEREST` /// that is to say in base 10**27 uint64 public interestRate; /// @notice Fee taken by the protocol during a liquidation. Technically, this value is not the fee per se, it's 1 - fee. /// For instance for a 2% fee, `liquidationSurcharge` should be 98% uint64 public liquidationSurcharge; /// @notice Maximum discount given to liquidators uint64 public maxLiquidationDiscount; /// @notice Whether whitelisting is required to own a vault or not bool public whitelistingActivated; /// @notice Whether the contract is paused or not bool public paused; // ================================= VARIABLES ================================= /// @notice Timestamp at which the `interestAccumulator` was updated uint256 public lastInterestAccumulatorUpdated; /// @inheritdoc IVaultManagerStorage uint256 public interestAccumulator; /// @inheritdoc IVaultManagerStorage uint256 public totalNormalizedDebt; /// @notice Surplus accumulated by the contract: surplus is always in stablecoins, and is then reset /// when the value is communicated to the treasury contract uint256 public surplus; /// @notice Bad debt made from liquidated vaults which ended up having no collateral and a positive amount /// of stablecoins uint256 public badDebt; // ================================== MAPPINGS ================================= /// @inheritdoc IVaultManagerStorage mapping(uint256 => Vault) public vaultData; /// @notice Maps an address to 1 if it's whitelisted and can open or own a vault mapping(address => uint256) public isWhitelisted; // ================================ ERC721 DATA ================================ /// @inheritdoc IVaultManagerStorage uint256 public vaultIDCount; /// @notice URI string internal _baseURI; // Mapping from `vaultID` to owner address mapping(uint256 => address) internal _owners; // Mapping from owner address to vault owned count mapping(address => uint256) internal _balances; // Mapping from `vaultID` to approved address mapping(uint256 => address) internal _vaultApprovals; // Mapping from owner to operator approvals mapping(address => mapping(address => uint256)) internal _operatorApprovals; uint256[50] private __gap; // =================================== EVENTS ================================== event AccruedToTreasury(uint256 surplusEndValue, uint256 badDebtEndValue); event CollateralAmountUpdated(uint256 vaultID, uint256 collateralAmount, uint8 isIncrease); event InterestAccumulatorUpdated(uint256 value, uint256 timestamp); event InternalDebtUpdated(uint256 vaultID, uint256 internalAmount, uint8 isIncrease); event FiledUint64(uint64 param, bytes32 what); event DebtCeilingUpdated(uint256 debtCeiling); event LiquidationBoostParametersUpdated(address indexed _veBoostProxy, uint256[] xBoost, uint256[] yBoost); event LiquidatedVaults(uint256[] vaultIDs); event DebtTransferred(uint256 srcVaultID, uint256 dstVaultID, address dstVaultManager, uint256 amount); // =================================== ERRORS ================================== error ApprovalToOwner(); error ApprovalToCaller(); error DustyLeftoverAmount(); error DebtCeilingExceeded(); error HealthyVault(); error IncompatibleLengths(); error InsolventVault(); error InvalidParameterValue(); error InvalidParameterType(); error InvalidSetOfParameters(); error InvalidTreasury(); error NonERC721Receiver(); error NonexistentVault(); error NotApproved(); error NotGovernor(); error NotGovernorOrGuardian(); error NotTreasury(); error NotWhitelisted(); error NotVaultManager(); error Paused(); error TooHighParameterValue(); error TooSmallParameterValue(); error ZeroAddress(); /// @custom:oz-upgrades-unsafe-allow constructor constructor() initializer {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns ( uint8 ); function description() external view returns ( string memory ); function version() external view returns ( uint256 ); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData( uint80 _roundId ) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../token/ERC721/extensions/IERC721Metadata.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.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 ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../token/ERC721/extensions/IERC721MetadataUpgradeable.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. Equivalent to `reinitializer(1)`. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * `initializer` is equivalent to `reinitializer(1)`, so a reinitializer may be used after the original * initialization step. This is essential to configure modules that are added through upgrades and that require * initialization. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuardUpgradeable is Initializable { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20PermitUpgradeable { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721ReceiverUpgradeable { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165Upgradeable.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721Upgradeable is IERC165Upgradeable { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId, bytes calldata data ) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 tokenId ) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.0; import "../IERC721Upgradeable.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721MetadataUpgradeable is IERC721Upgradeable { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.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 ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165Upgradeable { /** * @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); }
{ "remappings": [ "@chainlink/=node_modules/@chainlink/", "@ensdomains/=lib/borrow-contracts/node_modules/@ensdomains/", "@openzeppelin/=node_modules/@openzeppelin/", "@uniswap/=lib/borrow-contracts/node_modules/@uniswap/", "borrow-contracts/=lib/borrow-contracts/contracts/", "borrow/=lib/borrow-contracts/contracts/", "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/" ], "optimizer": { "enabled": true, "runs": 1 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"ApprovalToCaller","type":"error"},{"inputs":[],"name":"ApprovalToOwner","type":"error"},{"inputs":[],"name":"DebtCeilingExceeded","type":"error"},{"inputs":[],"name":"DustyLeftoverAmount","type":"error"},{"inputs":[],"name":"ExpiredDeadline","type":"error"},{"inputs":[],"name":"HealthyVault","type":"error"},{"inputs":[],"name":"IncompatibleLengths","type":"error"},{"inputs":[],"name":"InsolventVault","type":"error"},{"inputs":[],"name":"InvalidParameterType","type":"error"},{"inputs":[],"name":"InvalidParameterValue","type":"error"},{"inputs":[],"name":"InvalidSetOfParameters","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidTreasury","type":"error"},{"inputs":[],"name":"NonERC721Receiver","type":"error"},{"inputs":[],"name":"NonexistentVault","type":"error"},{"inputs":[],"name":"NotApproved","type":"error"},{"inputs":[],"name":"NotGovernor","type":"error"},{"inputs":[],"name":"NotGovernorOrGuardian","type":"error"},{"inputs":[],"name":"NotTreasury","type":"error"},{"inputs":[],"name":"NotVaultManager","type":"error"},{"inputs":[],"name":"NotWhitelisted","type":"error"},{"inputs":[],"name":"Paused","type":"error"},{"inputs":[],"name":"TooHighParameterValue","type":"error"},{"inputs":[],"name":"TooSmallParameterValue","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"surplusEndValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"badDebtEndValue","type":"uint256"}],"name":"AccruedToTreasury","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"vaultID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"isIncrease","type":"uint8"}],"name":"CollateralAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"debtCeiling","type":"uint256"}],"name":"DebtCeilingUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"srcVaultID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dstVaultID","type":"uint256"},{"indexed":false,"internalType":"address","name":"dstVaultManager","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DebtTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"param","type":"uint64"},{"indexed":false,"internalType":"bytes32","name":"what","type":"bytes32"}],"name":"FiledUint64","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"InterestAccumulatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"vaultID","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"internalAmount","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"isIncrease","type":"uint8"}],"name":"InternalDebtUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"vaultIDs","type":"uint256[]"}],"name":"LiquidatedVaults","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_veBoostProxy","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"xBoost","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"yBoost","type":"uint256[]"}],"name":"LiquidationBoostParametersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"BASE_INTEREST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BASE_PARAMS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"HALF_BASE_INTEREST","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterestToTreasury","outputs":[{"internalType":"uint256","name":"surplusValue","type":"uint256"},{"internalType":"uint256","name":"badDebtValue","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ActionType[]","name":"actions","type":"uint8[]"},{"internalType":"bytes[]","name":"datas","type":"bytes[]"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"angle","outputs":[{"components":[{"internalType":"uint256","name":"stablecoinAmountToGive","type":"uint256"},{"internalType":"uint256","name":"stablecoinAmountToReceive","type":"uint256"},{"internalType":"uint256","name":"collateralAmountToGive","type":"uint256"},{"internalType":"uint256","name":"collateralAmountToReceive","type":"uint256"}],"internalType":"struct PaymentData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum ActionType[]","name":"actions","type":"uint8[]"},{"internalType":"bytes[]","name":"datas","type":"bytes[]"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"who","type":"address"},{"internalType":"bytes","name":"repayData","type":"bytes"}],"name":"angle","outputs":[{"components":[{"internalType":"uint256","name":"stablecoinAmountToGive","type":"uint256"},{"internalType":"uint256","name":"stablecoinAmountToReceive","type":"uint256"},{"internalType":"uint256","name":"collateralAmountToGive","type":"uint256"},{"internalType":"uint256","name":"collateralAmountToReceive","type":"uint256"}],"internalType":"struct PaymentData","name":"paymentData","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"vaultID","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"badDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowFee","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultID","type":"uint256"},{"internalType":"address","name":"liquidator","type":"address"}],"name":"checkLiquidation","outputs":[{"components":[{"internalType":"uint256","name":"maxStablecoinAmountToRepay","type":"uint256"},{"internalType":"uint256","name":"maxCollateralAmountGiven","type":"uint256"},{"internalType":"uint256","name":"thresholdRepayAmount","type":"uint256"},{"internalType":"uint256","name":"discount","type":"uint256"},{"internalType":"uint256","name":"currentDebt","type":"uint256"}],"internalType":"struct LiquidationOpportunity","name":"liqOpp","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralFactor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"toVault","type":"address"}],"name":"createVault","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"debtCeiling","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dust","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dustLiquidation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultID","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultID","type":"uint256"},{"internalType":"uint256","name":"stablecoinAmount","type":"uint256"},{"internalType":"uint256","name":"senderBorrowFee","type":"uint256"},{"internalType":"uint256","name":"senderRepayFee","type":"uint256"}],"name":"getDebtOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getTotalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultID","type":"uint256"}],"name":"getVaultDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ITreasury","name":"_treasury","type":"address"},{"internalType":"contract IERC20","name":"_collateral","type":"address"},{"internalType":"contract IOracle","name":"_oracle","type":"address"},{"components":[{"internalType":"uint256","name":"debtCeiling","type":"uint256"},{"internalType":"uint64","name":"collateralFactor","type":"uint64"},{"internalType":"uint64","name":"targetHealthFactor","type":"uint64"},{"internalType":"uint64","name":"interestRate","type":"uint64"},{"internalType":"uint64","name":"liquidationSurcharge","type":"uint64"},{"internalType":"uint64","name":"maxLiquidationDiscount","type":"uint64"},{"internalType":"bool","name":"whitelistingActivated","type":"bool"},{"internalType":"uint256","name":"baseBoost","type":"uint256"}],"internalType":"struct VaultParameters","name":"params","type":"tuple"},{"internalType":"string","name":"_symbol","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestAccumulator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRate","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"vaultID","type":"uint256"}],"name":"isApprovedOrOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isWhitelisted","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastInterestAccumulatorUpdated","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"vaultIDs","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"who","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"liquidate","outputs":[{"components":[{"internalType":"uint256","name":"stablecoinAmountToReceive","type":"uint256"},{"internalType":"uint256","name":"collateralAmountToGive","type":"uint256"},{"internalType":"uint256","name":"badDebtFromLiquidation","type":"uint256"},{"internalType":"uint256","name":"oracleValue","type":"uint256"},{"internalType":"uint256","name":"newInterestAccumulator","type":"uint256"}],"internalType":"struct LiquidatorData","name":"liqData","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"vaultIDs","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"liquidate","outputs":[{"components":[{"internalType":"uint256","name":"stablecoinAmountToReceive","type":"uint256"},{"internalType":"uint256","name":"collateralAmountToGive","type":"uint256"},{"internalType":"uint256","name":"badDebtFromLiquidation","type":"uint256"},{"internalType":"uint256","name":"oracleValue","type":"uint256"},{"internalType":"uint256","name":"newInterestAccumulator","type":"uint256"}],"internalType":"struct LiquidatorData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidationSurcharge","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLiquidationDiscount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultID","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"bool","name":"approved","type":"bool"},{"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":[],"name":"repayFee","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"vaultID","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"vaultID","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"baseURI_","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_debtCeiling","type":"uint256"}],"name":"setDebtCeiling","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_dust","type":"uint256"},{"internalType":"uint256","name":"_dustLiquidation","type":"uint256"},{"internalType":"uint256","name":"dustCollateral_","type":"uint256"}],"name":"setDusts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_veBoostProxy","type":"address"},{"internalType":"uint256[]","name":"xBoost","type":"uint256[]"},{"internalType":"uint256[]","name":"yBoost","type":"uint256[]"}],"name":"setLiquidationBoostParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_oracle","type":"address"}],"name":"setOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_treasury","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"param","type":"uint64"},{"internalType":"bytes32","name":"what","type":"bytes32"}],"name":"setUint64","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stablecoin","outputs":[{"internalType":"contract IAgToken","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":"surplus","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"targetHealthFactor","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"togglePause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"toggleWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultID","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalNormalizedDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"vaultID","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasury","outputs":[{"internalType":"contract ITreasury","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"vaultData","outputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"uint256","name":"normalizedDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultIDCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"veBoostProxy","outputs":[{"internalType":"contract IVeBoostProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistingActivated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"xLiquidationBoost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"yLiquidationBoost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60806040523480156200001157600080fd5b50600054610100900460ff1615808015620000335750600054600160ff909116105b8062000063575062000050306200013d60201b620032c81760201c565b15801562000063575060005460ff166001145b620000cb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b6000805460ff191660011790558015620000ef576000805461ff0019166101001790555b801562000136576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b506200014c565b6001600160a01b03163b151590565b615e0a806200015c6000396000f3fe608060405234801561001057600080fd5b50600436106103015760003560e01c8063010db1951461030657806301ffc9a71461032f57806306fdde0314610352578063081812fc14610367578063087a60071461037a578063095ea7b3146103915780630e198f22146103a657806313888565146103af57806323b872dd146103b8578063254cf439146103cb578063307439af146103fd57806334ce998a1461041d57806335836f15146104255780633644e5151461043857806339393ac91461044057806339eb4dc6146104535780633ae2325f146104675780633af32abf1461047a5780633c2e941b1461049a57806342842e0e146104a3578063430c2081146104b65780634f7e43df146104c957806355f804b3146104e35780635c975abb146104f657806361d027b31461050a5780636352211e1461051d57806370a082311461053057806374107543146105435780637aacfffa146105565780637adbf973146105925780637c0f59f4146105a55780637c3a00fd146105bf5780637dc0d1d0146105d25780637e53bd97146105e55780637e56d47c146105f85780637ecebe001461060b578063835986b41461063457806389050f1d1461064757806395d89b41146106595780639a3b6f2f146106615780639f48118f146106a7578063a22cb465146106b2578063af2c8c2e146106c5578063b1511cc9146106ce578063b4bd6f46146106e1578063b88d4fde146106f4578063bbcac55714610707578063bfc7ad2e14610710578063c13cacae14610719578063c4ae31681461072c578063c66d8b0114610734578063c87b56dd1461074e578063d8dfeb4514610761578063d9b1cb5b14610774578063de1f776514610787578063de8fc69814610799578063df011c41146107ac578063e182b883146107bf578063e1c84ea4146107d2578063e626648a146107db578063e985e9c5146107f5578063e9cbd82214610808578063f0f442601461081b578063f51cc7dd1461082e578063fad9aba314610841578063fc29b0211461084a578063fd527cf81461085d575b600080fd5b603754610319906001600160a01b031681565b6040516103269190614cfc565b60405180910390f35b61034261033d366004614d26565b610865565b6040519015158152602001610326565b61035a6108d2565b6040516103269190614d93565b610319610375366004614da6565b610960565b610383603f5481565b604051908152602001610326565b6103a461039f366004614dd4565b610991565b005b610383603e5481565b61038360415481565b6103a46103c6366004614e00565b610a1d565b603c546103e590600160401b90046001600160401b031681565b6040516001600160401b039091168152602001610326565b61041061040b366004614e41565b610a58565b6040516103269190614e71565b610383610b0a565b610383610433366004614da6565b610b3c565b610383610b78565b6103a461044e366004614eaa565b610b82565b603d5461034290600160c01b900460ff1681565b610383610475366004614da6565b610c82565b610383610488366004614eaa565b60446020526000908152604090205481565b61038360455481565b6103a46104b1366004614e00565b610ca3565b6103426104c4366004614dd4565b610cbe565b603d546103e590600160801b90046001600160401b031681565b6103a46104f1366004614f7c565b610cca565b603d5461034290600160c81b900460ff1681565b603354610319906001600160a01b031681565b61031961052b366004614da6565b610d68565b61038361053e366004614eaa565b610d73565b6103a4610551366004614fc7565b610db8565b61057d610564366004614da6565b6043602052600090815260409020805460019091015482565b60408051928352602083019190915201610326565b6103a46105a0366004614eaa565b61117d565b603c546103e590600160c01b90046001600160401b031681565b603d546103e5906001600160401b031681565b603654610319906001600160a01b031681565b6103a46105f3366004615071565b6112c4565b6104106106063660046150e6565b6113e9565b610383610619366004614eaa565b6001600160a01b03166000908152607f602052604090205490565b6103a46106423660046151aa565b6119a2565b610383676765c793fa10079d601a1b81565b61035a611b5a565b61067461066f3660046152c4565b611b67565b60405161032691908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b610383633b9aca0081565b6103a46106c036600461535a565b611b9c565b61038360405481565b6103a46106dc366004614da6565b611ba7565b6103836106ef366004614eaa565b611c70565b6103a4610702366004615388565b611cac565b61038360425481565b61038360b65481565b6103a46107273660046153f3565b611ce9565b6103a4611da6565b603d546103e590600160401b90046001600160401b031681565b61035a61075c366004614da6565b611e55565b603454610319906001600160a01b031681565b6103a461078236600461541f565b611fab565b610383676765c793fa10079d601b1b81565b6106746107a73660046154b3565b612547565b603c546103e5906001600160401b031681565b6103836107cd366004614da6565b612de2565b61038360395481565b603c546103e590600160801b90046001600160401b031681565b610342610803366004615511565b612df2565b603554610319906001600160a01b031681565b6103a4610829366004614eaa565b612e20565b6103a461083c36600461554e565b612ec0565b61038360b45481565b6104106108583660046155c8565b61317f565b61057d6131a9565b60006001600160e01b03198216635b5e139f60e01b148061089657506001600160e01b031982166380ac58cd60e01b145b806108b157506001600160e01b0319821663430c208160e01b145b806108cc57506001600160e01b031982166301ffc9a760e01b145b92915050565b607d80546108df90615624565b80601f016020809104026020016040519081016040528092919081815260200182805461090b90615624565b80156109585780601f1061092d57610100808354040283529160200191610958565b820191906000526020600020905b81548152906001019060200180831161093b57829003601f168201915b505050505081565b600061096b826132d7565b6109885760405163062a39dd60e11b815260040160405180910390fd5b6108cc826132f4565b600061099c8261330f565b9050806001600160a01b0316836001600160a01b0316036109d0576040516349fa8bc360e11b815260040160405180910390fd5b336001600160a01b038216148015906109f057506109ee8133612df2565b155b15610a0e5760405163c19f17a960e01b815260040160405180910390fd5b610a188383613345565b505050565b3381610a2982826133b3565b610a465760405163c19f17a960e01b815260040160405180910390fd5b610a51858585613431565b5050505050565b610a60614c45565b60008381526043602090815260409182902082518084018452815481526001909101548183015260365483516315f789a960e21b81529351610b0394929387936001600160a01b03909316926357de26a492600480830193928290030181865afa158015610ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af6919061565e565b610afe613563565b6136f8565b9392505050565b6000676765c793fa10079d601b1b610b20613563565b604054610b2d919061568d565b610b3791906156ba565b905090565b6000676765c793fa10079d601b1b610b52613563565b600084815260436020526040902060010154610b6e919061568d565b6108cc91906156ba565b6000610b37613a61565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b890610bb2903390600401614cfc565b602060405180830381865afa158015610bcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf391906156ce565b610c1057604051633b8d9d7560e21b815260040160405180910390fd5b6001600160a01b03811615610c5f576001600160a01b038116600090815260446020526040902054610c439060016156eb565b6001600160a01b03821660009081526044602052604090205550565b603d805460ff60c01b198116600160c01b9182900460ff16159091021790555b50565b603b8181548110610c9257600080fd5b600091825260209091200154905081565b610a1883838360405180602001604052806000815250611cac565b6000610b0383836133b3565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990610cfa903390600401614cfc565b602060405180830381865afa158015610d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3b91906156ce565b610d5857604051632678482f60e21b815260040160405180910390fd5b6046610d648282615744565b5050565b60006108cc8261330f565b60006001600160a01b038216610d9c5760405163d92e233d60e01b815260040160405180910390fd5b506001600160a01b031660009081526048602052604090205490565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990610de8903390600401614cfc565b602060405180830381865afa158015610e05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2991906156ce565b610e4657604051632678482f60e21b815260040160405180910390fd5b806121a360f11b03610ea957603d546001600160401b03600160401b90910481169083161115610e8957604051637650e96360e11b815260040160405180910390fd5b603c80546001600160401b0319166001600160401b038416179055611136565b80622a242360e91b03610f0d57633b9aca00826001600160401b03161015610ee45760405163da6a17b960e01b815260040160405180910390fd5b603c8054600160401b600160801b031916600160401b6001600160401b03851602179055611136565b8061212360f11b03610f7057633b9aca00826001600160401b03161115610f4757604051637650e96360e11b815260040160405180910390fd5b603c8054600160801b600160c01b031916600160801b6001600160401b03851602179055611136565b8061292360f11b03610fec57603d54633b9aca0090610f9f90600160401b90046001600160401b031684615803565b6001600160401b03161115610fc757604051637650e96360e11b815260040160405180910390fd5b603c80546001600160c01b0316600160c01b6001600160401b03851602179055611136565b806124a960f11b0361102157611000613acd565b50603d80546001600160401b0319166001600160401b038416179055611136565b80614c5360f01b036110b957603c546001600160401b03808416911611806110725750603c54633b9aca009061106790600160c01b90046001600160401b031684615803565b6001600160401b0316115b156110905760405163180d062b60e31b815260040160405180910390fd5b603d8054600160401b600160801b031916600160401b6001600160401b03851602179055611136565b806213531160ea1b0361111d57633b9aca00826001600160401b031611156110f457604051637650e96360e11b815260040160405180910390fd5b603d8054600160801b600160c01b031916600160801b6001600160401b03851602179055611136565b60405163e1daa9cf60e01b815260040160405180910390fd5b604080516001600160401b0384168152602081018390527f13b367dac93b85d1ed9b3d8961d8b48e1a677c9800bb1613b4b0416b2d5b61d091015b60405180910390a15050565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b8906111ad903390600401614cfc565b602060405180830381865afa1580156111ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ee91906156ce565b61120b57604051633b8d9d7560e21b815260040160405180910390fd5b603354604080516361d027b360e01b815290516001600160a01b03928316928416916361d027b39160048083019260209291908290030181865afa158015611257573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127b919061582a565b6001600160a01b0316146112a2576040516302979eb960e31b815260040160405180910390fd5b603680546001600160a01b0319166001600160a01b0392909216919091179055565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de9906112f4903390600401614cfc565b602060405180830381865afa158015611311573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133591906156ce565b61135257604051632678482f60e21b815260040160405180910390fd5b8060008151811061136557611365615847565b602002602001015160000361138d57604051631746545d60e11b815260040160405180910390fd5b80516113a090603b906020840190614c74565b50826001600160a01b03167feb74d4d9fea592587c926aeb35eb6a7893fb28db0c1c8eb2eb3c586e7164b76c83836040516113dc929190615898565b60405180910390a2505050565b6113f1614c45565b603d54600160c81b900460ff161561141c576040516313d0ff5960e31b815260040160405180910390fd5b6002600154036114475760405162461bcd60e51b815260040161143e906158bd565b60405180910390fd5b6002600155865186518114158061145c575080155b1561147a576040516346282e8d60e01b815260040160405180910390fd5b603660009054906101000a90046001600160a01b03166001600160a01b03166357de26a46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f1919061565e565b60608301526114fe613acd565b60808301526040517f965a177723c641ee49150b583a0b9ad4730bb20d3474e00ae5a65e777c00d67b90611533908a906158f4565b60405180910390a160005b81811015611912576000604360008b848151811061155e5761155e615847565b6020026020010151815260200190815260200160002060405180604001604052908160008201548152602001600182015481525050905060006115ab8233876060015188608001516136f8565b905080604001516000141580156115df575080604001518a84815181106115d4576115d4615847565b602002602001015110155b80611606575080600001518a84815181106115fc576115fc615847565b6020026020010151115b1561162f5780600001518a848151811061162257611622615847565b6020026020010181815250505b600085606001518260600151611645919061568d565b603854633b9aca008d878151811061165f5761165f615847565b6020026020010151611671919061568d565b61167b919061568d565b61168591906156ba565b90506116c08c858151811061169c5761169c615847565b602002602001015182856000015111156116b657826116b9565b84515b6000613b6e565b825181106117f6575081516020830151604080546000906116e29084906156eb565b92505081905550604360008d86815181106116ff576116ff615847565b60209081029190910181015182528101919091526040016000908120818155600101819055603d548c51633b9aca0091600160401b90046001600160401b0316908e908890811061175257611752615847565b6020026020010151611764919061568d565b61176e91906156ba565b905082608001518110611782576000611792565b80836080015161179291906156eb565b876040018181516117a39190615907565b905250508b51600080516020615d75833981519152908d90869081106117cb576117cb615847565b6020026020010151846020015160006040516117e99392919061591a565b60405180910390a16118b7565b80604360008e878151811061180d5761180d615847565b60200260200101518152602001908152602001600020600001600082825461183591906156eb565b925050819055506118b58c858151811061185157611851615847565b6020026020010151633b9aca00603d60089054906101000a90046001600160401b03166001600160401b03168e888151811061188f5761188f615847565b60200260200101516118a1919061568d565b6118ab91906156ba565b8860800151613b81565b505b80866020018181516118c99190615907565b9052508a518b90859081106118e0576118e0615847565b6020026020010151866000018181516118f99190615907565b90525061190b92508391506159339050565b905061153e565b50603d54633b9aca009061193690600160401b90046001600160401b0316826156eb565b8351611942919061568d565b61194c91906156ba565b6041600082825461195d9190615907565b9091555050604082015160428054600090611979908490615907565b909155505060208201518251611993919088888888613cb3565b50600180559695505050505050565b603d54600160c81b900460ff16156119cd576040516313d0ff5960e31b815260040160405180910390fd5b6033546040516333b52a9f60e11b81526001600160a01b039091169063676a553e906119fd903390600401614cfc565b602060405180830381865afa158015611a1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3e91906156ce565b611a5b5760405163027f480760e01b815260040160405180910390fd5b603c54600090600160c01b90046001600160401b0316821015611a9957603c54611a96908390600160c01b90046001600160401b03166156eb565b90505b603c54600090600160801b90046001600160401b0316841115611ad657603c54611ad390600160801b90046001600160401b0316856156eb565b90505b6000611ae76002633b9aca00615a30565b611af583633b9aca006156eb565b611b0385633b9aca006156eb565b611b0d908961568d565b611b17919061568d565b611b2191906156ba565b9050611b2d81876156eb565b60416000828254611b3e9190615907565b90915550611b50905087826000613b81565b5050505050505050565b607e80546108df90615624565b611b6f614cbf565b60408051600080825260208201909252611b9191879187918791879190612547565b90505b949350505050565b610d64338383613dbd565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990611bd7903390600401614cfc565b602060405180830381865afa158015611bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1891906156ce565b611c3557604051632678482f60e21b815260040160405180910390fd5b60398190556040518181527fdd63b3dcdbebad734892f7c7a26d0f647fbc7eec973e0775f5229018ac4ab47a9060200160405180910390a150565b603d54600090600160c81b900460ff1615611c9e576040516313d0ff5960e31b815260040160405180910390fd5b6108cc82613e72565b919050565b3382611cb882826133b3565b611cd55760405163c19f17a960e01b815260040160405180910390fd5b611ce186868686613fac565b505050505050565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b890611d19903390600401614cfc565b602060405180830381865afa158015611d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5a91906156ce565b611d7757604051633b8d9d7560e21b815260040160405180910390fd5b81831115611d985760405163180d062b60e31b815260040160405180910390fd5b60b49290925560b65560b555565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990611dd6903390600401614cfc565b602060405180830381865afa158015611df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1791906156ce565b611e3457604051632678482f60e21b815260040160405180910390fd5b603d805460ff60c81b198116600160c81b9182900460ff1615909102179055565b6060611e60826132d7565b611e7d5760405163062a39dd60e11b815260040160405180910390fd5b8160005b8115611ea457611e9081615933565b9050611e9d600a836156ba565b9150611e81565b6000816001600160401b03811115611ebe57611ebe614ec7565b6040519080825280601f01601f191660200182016040528015611ee8576020820181803683370190505b5090505b8415611f5357611efd6001836156eb565b9150611f0a600a86615a3f565b611f15906030615907565b60f81b818381518110611f2a57611f2a615847565b60200101906001600160f81b031916908160001a905350611f4c600a866156ba565b9450611eec565b60468054611f6090615624565b9050600003611f7e5760405180602001604052806000815250611fa2565b604681604051602001611f92929190615a53565b6040516020818303038152906040525b95945050505050565b600054610100900460ff1615808015611fcb5750600054600160ff909116105b80611fec5750611fda306132c8565b158015611fec575060005460ff166001145b61204f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161143e565b6000805460ff191660011790558015612072576000805461ff0019166101001790555b856001600160a01b0316846001600160a01b03166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120de919061582a565b6001600160a01b031614612105576040516302979eb960e31b815260040160405180910390fd5b603380546001600160a01b038089166001600160a01b0319928316179092556034805492881692909116821790556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801561216e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121929190615ada565b61219d90600a615a30565b603881905550856001600160a01b031663e9cbd8226040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612205919061582a565b603580546001600160a01b03199081166001600160a01b039384161790915560368054909116918616919091179055604051600090612248908490602001615af7565b60408051601f198184030181529190529050607d6122668282615744565b5061227081613fe6565b826040516020016122819190615b3e565b604051602081830303815290604052607e908161229e9190615744565b50676765c793fa10079d601b1b603f5542603e556122c260a0850160808601615b68565b6001600160401b03166122db6040860160208701615b68565b6001600160401b0316118061230b5750633b9aca0061230060a0860160808701615b68565b6001600160401b0316115b8061233157506123216060850160408601615b68565b6001600160401b0316633b9aca00115b806123585750633b9aca0061234c60c0860160a08701615b68565b6001600160401b031610155b80612365575060e0840135155b1561238357604051631746545d60e11b815260040160405180910390fd5b83356039556123986040850160208601615b68565b603c80546001600160401b0319166001600160401b03929092169190911790556123c86060850160408601615b68565b603c80546001600160401b0392909216600160401b02600160401b600160801b03199092169190911790556124036080850160608601615b68565b603d80546001600160401b0319166001600160401b039290921691909117905561243360a0850160808601615b68565b603d80546001600160401b0392909216600160401b02600160401b600160801b031990921691909117905561246e60c0850160a08601615b68565b603d80546001600160401b0392909216600160801b02600160801b600160c01b03199092169190911790556124a960e0850160c08601615b83565b603d8054911515600160c01b0260ff60c01b19909216919091179055604080516020810190915260e085013581526124e590603b906001614c74565b5050603d805460ff60c81b1916600160c81b1790558015611ce1576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050505050565b61254f614cbf565b603d54600160c81b900460ff161561257a576040516313d0ff5960e31b815260040160405180910390fd5b60026001540361259c5760405162461bcd60e51b815260040161143e906158bd565b6002600155855187511415806125b157508651155b156125cf576040516346282e8d60e01b815260040160405180910390fd5b6000806000806000805b8c51811015612b4b5760008d82815181106125f6576125f6615847565b602002602001015190506000600781111561261357612613615ba0565b81600781111561262557612625615ba0565b036126655761265f8d838151811061263f5761263f615847565b602002602001015180602001905181019061265a919061582a565b613e72565b50612b3a565b600281600781111561267957612679615ba0565b036126e2578c828151811061269057612690615847565b60200260200101518060200190518101906126ab9190615bb6565b9550925060008390036126be5760455492505b6126c883866140bc565b84886060018181516126da9190615907565b905250612b3a565b60078160078111156126f6576126f6615ba0565b036127ce5760008060008f858151811061271257612712615847565b602002602001015180602001905181019061272d9190615bda565b60345460405163d505accf60e01b81526001600160a01b038089166004830152306024830152604482018890526064820187905260ff8616608483015260a4820185905260c48201849052969f50939d50939b509497509550929350169063d505accf9060e401600060405180830381600087803b1580156127ae57600080fd5b505af11580156127c2573d6000803e3d6000fd5b50505050505050612b3a565b866000036127e1576127de613acd565b96505b60048160078111156127f5576127f5615ba0565b036128c4578c828151811061280c5761280c615847565b60200260200101518060200190518101906128279190615bb6565b94509250600083900361283a5760455492505b612845838589613b81565b603c5490945060009061286c90600160c01b90046001600160401b0316633b9aca006156eb565b61287a633b9aca008761568d565b61288491906156ba565b905061289085826156eb565b604160008282546128a19190615907565b9250508190555080896020018181516128ba9190615907565b905250612b3a9050565b8560000361294657603660009054906101000a90046001600160a01b03166001600160a01b03166357de26a46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561291f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612943919061565e565b95505b600181600781111561295a5761295a615ba0565b036129d8578c828151811061297157612971615847565b602002602001015180602001905181019061298c919061565e565b92508260000361299c5760455492505b6129a7838789614134565b809650819550505084886040018181516129c19190615907565b9052506020880180518591906126da908390615907565b60038160078111156129ec576129ec615ba0565b03612a4f578c8281518110612a0357612a03615847565b6020026020010151806020019051810190612a1e9190615bb6565b955092506000839003612a315760455492505b612a3d8386888a614254565b84886040018181516126da9190615907565b6005816007811115612a6357612a63615ba0565b03612ac8578c8281518110612a7a57612a7a615847565b6020026020010151806020019051810190612a959190615bb6565b945092506000839003612aa85760455492505b612ab48385888a61433a565b935083886000018181516126da9190615907565b6006816007811115612adc57612adc615ba0565b03612b3a576000808e8481518110612af657612af6615847565b6020026020010151806020019051810190612b119190615c2d565b9850919650925090506000859003612b295760455494505b612b37858383898c8e6143d5565b50505b50612b4481615933565b90506125d9565b508551602087015110612c425785516020870151600091612b6b916156eb565b90508660600151876040015110612ba257612b9d87606001518860400151612b9391906156eb565b828d8d8d8d613cb3565b612c3c565b8015612c0f57603554604051630d43af8160e21b81526001600160a01b039091169063350ebe0490612bdc9084908f903390600401615c6b565b600060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050505b612c3c333089604001518a60600151612c2891906156eb565b6034546001600160a01b0316929190614510565b50612dce565b60208601518651600091612c55916156eb565b6035546040516340c10f1960e01b81529192506001600160a01b0316906340c10f1990612c88908d908590600401615c8a565b600060405180830381600087803b158015612ca257600080fd5b505af1158015612cb6573d6000803e3d6000fd5b50505050866060015187604001511115612cfa57612cf58a88606001518960400151612ce291906156eb565b6034546001600160a01b0316919061457b565b612dcc565b600087604001518860600151612d1091906156eb565b90508015612dca57885115612db257896001600160a01b031663a5d4096b603560009054906101000a90046001600160a01b0316603460009054906101000a90046001600160a01b03163385878f6040518763ffffffff1660e01b8152600401612d7f96959493929190615ca3565b600060405180830381600087803b158015612d9957600080fd5b505af1158015612dad573d6000803e3d6000fd5b505050505b603454612dca906001600160a01b0316333084614510565b505b505b505060018055509198975050505050505050565b603a8181548110610c9257600080fd5b6001600160a01b039182166000908152604a6020908152604080832093909416825291909152205460011490565b6033546001600160a01b03163314612e4b5760405163b90cdbb160e01b815260040160405180910390fd5b603380546001600160a01b0319166001600160a01b0383811691909117909155603654604051630787a21360e51b815291169063f0f4426090612e92908490600401614cfc565b600060405180830381600087803b158015612eac57600080fd5b505af1158015610a51573d6000803e3d6000fd5b83421115612ee15760405163f87d927160e01b815260040160405180910390fd5b6fa2a8918ca85bafe22016d0b997e4df60600160ff1b03811180612f1857508260ff16601b14158015612f1857508260ff16601c14155b15612f3657604051638baa579f60e01b815260040160405180910390fd5b6000612f40613a61565b608254898989612f4f8d61459a565b6040805160208101969096526001600160a01b03948516908601529290911660608401521515608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001612fc692919061190160f01b81526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050612ff0886001600160a01b03166132c8565b156130cc57604080516020810185905280820184905260f886901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b038a1691631626ba7e91613056918591606501615ce5565b602060405180830381865afa158015613073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130979190615cfe565b6001600160e01b031916631626ba7e60e01b146130c757604051638baa579f60e01b815260040160405180910390fd5b613174565b6040805160008082526020820180845284905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613120573d6000803e3d6000fd5b505050602060405103519050886001600160a01b0316816001600160a01b031614158061315457506001600160a01b038116155b1561317257604051638baa579f60e01b815260040160405180910390fd5b505b611b50888888613dbd565b613187614c45565b60408051600080825260208201909252611b91918791879187918791906113e9565b60335460009081906001600160a01b031633146131d95760405163b90cdbb160e01b815260040160405180910390fd5b6131e1613acd565b50506041805460428054600093849055929055915080821061327a5761320781836156eb565b6035546033546040516340c10f1960e01b8152929450600093506001600160a01b03918216926340c10f19926132439216908690600401615c8a565b600060405180830381600087803b15801561325d57600080fd5b505af1158015613271573d6000803e3d6000fd5b5050505061328b565b61328482826156eb565b9050600091505b60408051838152602081018390527ffeb12225c131aab793a00c5239afb778932d170fa28ce6e9d23703e4bd892121910160405180910390a19091565b6001600160a01b03163b151590565b6000908152604760205260409020546001600160a01b0316151590565b6000908152604960205260409020546001600160a01b031690565b6000818152604760205260409020546001600160a01b031680611ca75760405163062a39dd60e11b815260040160405180910390fd5b600081815260496020526040902080546001600160a01b0319166001600160a01b038416908117909155819061337a8261330f565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000806133bf8361330f565b9050806001600160a01b0316846001600160a01b031614806133fa5750836001600160a01b03166133ef846132f4565b6001600160a01b0316145b80611b9457506001600160a01b038082166000908152604a6020908152604080832093881683529290522054600114949350505050565b826001600160a01b03166134448261330f565b6001600160a01b03161461346b5760405163c19f17a960e01b815260040160405180910390fd5b6001600160a01b0382166134925760405163d92e233d60e01b815260040160405180910390fd5b603d54600160c01b900460ff1680156134c457506001600160a01b038216600090815260446020526040902054600114155b156134e257604051630b094f2760e31b815260040160405180910390fd5b6134ed8383836145de565b6134f8600082613345565b6001600160a01b038084166000818152604860209081526040808320805460001901905593861680835284832080546001019055858352604790915283822080546001600160a01b03191682179055925184939291600080516020615db583398151915291a4505050565b600080603e544261357491906156eb565b603d549091506001600160401b031681158061358e575080155b1561359d57603f549250505090565b60006135aa6001846156eb565b90506000600284116135bd5760006135c8565b6135c86002856156eb565b90506000676765c793fa10079d601b1b676765c793fa10079d601a1b6135ee868061568d565b6135f89190615907565b61360291906156ba565b90506000676765c793fa10079d601b1b676765c793fa10079d601a1b613628878561568d565b6136329190615907565b61363c91906156ba565b9050600060028361364d878a61568d565b613657919061568d565b61366191906156ba565b9050600060068386613673898c61568d565b61367d919061568d565b613687919061568d565b61369191906156ba565b9050676765c793fa10079d601b1b81836136ab8b8b61568d565b6136c090676765c793fa10079d601b1b615907565b6136ca9190615907565b6136d49190615907565b603f546136e1919061568d565b6136eb91906156ba565b9850505050505050505090565b613700614c45565b6000806000613710888787614625565b925092509250633b9aca00831061373a576040516315fe9b6160e21b815260040160405180910390fd5b6000633b9aca0061374b85826156eb565b6137548a6146b1565b61375e919061568d565b61376891906156ba565b603d54909150600160801b90046001600160401b03168110156137985761379381633b9aca006156eb565b6137b9565b603d546137b990600160801b90046001600160401b0316633b9aca006156eb565b603d54909150600160401b90046001600160401b03166000806137e16002633b9aca00615a30565b603c546137f791906001600160401b031661568d565b83613802868a61568d565b61380c919061568d565b1061399157603c546001600160401b031661382c6002633b9aca00615a30565b613836919061568d565b603c54859061385590600160401b90046001600160401b03168661568d565b61385f919061568d565b61386991906156eb565b603c548590633b9aca0090613887906001600160401b03168961568d565b603c546138a5908b90600160401b90046001600160401b031661568d565b6138af91906156eb565b6138b9919061568d565b6138c3919061568d565b6138cd91906156ba565b60b6549092506138e1633b9aca008261568d565b6138eb858561568d565b6138f59190615907565b613903633b9aca008961568d565b1161398b5761391d676765c793fa10079d601b1b8561568d565b633b9aca008b8f60200151613932919061568d565b61393c919061568d565b61394691906156ba565b613951906001615907565b9250808711156139865783633b9aca0061396b838a6156eb565b613975919061568d565b61397f91906156ba565b915061398b565b600191505b50613a0f565b6038546139a290633b9aca0061568d565b8c518b906139b190879061568d565b6139bb919061568d565b6139c591906156ba565b6139d0906001615907565b915060b554851115613a0b57633b9aca008460b554876139f091906156eb565b6139fa919061568d565b613a0491906156ba565b9050613a0f565b5060015b818852613a1c848b61568d565b603854613a2d633b9aca008561568d565b613a37919061568d565b613a4191906156ba565b602089015260408801525050606085015250608083015250949350505050565b60808054608154604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f602082015290810192909252606082015246918101919091523060a082015260009060c00160405160208183030381529060405280519060200120905090565b6000613ad7613563565b90506000676765c793fa10079d601b1b603f5483613af591906156eb565b604054613b02919061568d565b613b0c91906156ba565b90508060416000828254613b209190615907565b9091555050603f82905542603e8190556040805184815260208101929092527fd1fa8ba00a3bf20274346919dce0de62d2a140af2c71fe7e29fa6472eea3bb9d910160405180910390a15090565b610a18613b7a8461330f565b83836146d9565b600081600003613b9657613b93613acd565b91505b60008481526043602052604081206001015490676765c793fa10079d601b1b613bbf858461568d565b613bc991906156ba565b9050808510613bda57935080613bfd565b83613bf0676765c793fa10079d601b1b8761568d565b613bfa91906156ba565b90505b613c0781836156eb565b91508060406000828254613c1b91906156eb565b90915550508115801590613c505750676765c793fa10079d601b1b60b454613c43919061568d565b613c4d858461568d565b11155b15613c6e5760405163228af07f60e21b815260040160405180910390fd5b60008681526043602052604080822060010184905551600080516020615d7583398151915291613ca1918991859161591a565b60405180910390a15092949350505050565b8515613cd057603454613cd0906001600160a01b0316848861457b565b8415611ce157805115613d4f5760345460355460405163a5d4096b60e01b81526001600160a01b038086169363a5d4096b93613d1c9391831692169089908b908d908990600401615ca3565b600060405180830381600087803b158015613d3657600080fd5b505af1158015613d4a573d6000803e3d6000fd5b505050505b603554604051630d43af8160e21b81526001600160a01b039091169063350ebe0490613d8390889088903390600401615c6b565b600060405180830381600087803b158015613d9d57600080fd5b505af1158015613db1573d6000803e3d6000fd5b50505050505050505050565b826001600160a01b0316826001600160a01b031603613def576040516320c5195360e21b815260040160405180910390fd5b600081613dfd576000613e00565b60015b6001600160a01b038581166000818152604a602090815260408083209489168084529482529182902060ff959095169485905590518615158152939450919290917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b603d54600090600160c01b900460ff168015613ec157506001600160a01b0382166000908152604460205260409020546001141580613ec1575033600090815260446020526040902054600114155b15613edf57604051630b094f2760e31b815260040160405180910390fd5b6001600160a01b038216613f065760405163d92e233d60e01b815260040160405180910390fd5b506045805460010190819055613f1e600083836145de565b6001600160a01b038216600081815260486020908152604080832080546001019055848352604790915280822080546001600160a01b031916841790555183929190600080516020615db5833981519152908290a4613f8f600083836040518060200160405280600081525061474c565b611ca7576040516320149b4360e21b815260040160405180910390fd5b613fb7848484613431565b613fc38484848461474c565b613fe0576040516320149b4360e21b815260040160405180910390fd5b50505050565b600054610100900460ff166140515760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161143e565b7f3f43a9c6bafb5c7aab4e0cfe239dc5d4c15caf0381c6104188191f78a6640bd860825580516020918201206080556040805180820190915260018152603160f81b9101527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6608155565b6140c5826132d7565b6140e25760405163062a39dd60e11b815260040160405180910390fd5b6140ee82826001613b6e565b6000828152604360205260408120805483929061410c908490615907565b9091555050604051600080516020615d9583398151915290611171908490849060019061591a565b600080338561414382826133b3565b6141605760405163c19f17a960e01b815260040160405180910390fd5b600087815260436020908152604080832081518083019092528054825260010154918101919091529080614195838a8a614625565b5091509150633b9aca0082116141be57604051631527804d60e31b815260040160405180910390fd5b8260200151604060008282546141d491906156eb565b909155506141e390508a614852565b603c5460009061420790600160c01b90046001600160401b0316633b9aca006156eb565b614215633b9aca008461568d565b61421f91906156ba565b905061422b82826156eb565b6041600082825461423c9190615907565b90915550509251929a92995091975050505050505050565b338461426082826133b3565b61427d5760405163c19f17a960e01b815260040160405180910390fd5b61428986866000613b6e565b600086815260436020526040812080548792906142a79084906156eb565b9091555050600086815260436020908152604080832081518083019092528054825260010154918101919091526142df908686614625565b50509050633b9aca00811161430757604051631527804d60e31b815260040160405180910390fd5b600080516020615d95833981519152878760006040516143299392919061591a565b60405180910390a150505050505050565b6000338561434882826133b3565b6143655760405163c19f17a960e01b815260040160405180910390fd5b614371878787876148dd565b603c54909650600090633b9aca009061439b908990600160801b90046001600160401b031661568d565b6143a591906156ba565b905080604160008282546143b99190615907565b909155506143c9905081886156eb565b98975050505050505050565b33866143e182826133b3565b6143fe5760405163c19f17a960e01b815260040160405180910390fd5b60408051898152602081018890526001600160a01b038916818301526060810187905290517fddd3b70af631334f7552aadb582ed091018e62e103fa8b150ca66cc700d4dac69181900360800190a1614459888686866148dd565b9450306001600160a01b0388160361447c57614476868685613b81565b50611b50565b603c546040516320d661ad60e21b815260048101889052602481018790526001600160401b03600160801b830481166044830152600160c01b90920490911660648201526001600160a01b0388169063835986b490608401600060405180830381600087803b1580156144ee57600080fd5b505af1158015614502573d6000803e3d6000fd5b505050505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052613fe09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614a71565b610a188363a9059cbb60e01b8484604051602401614544929190615c8a565b6001600160a01b0381166000908152607f60205260409020546145be816001615907565b6001600160a01b039092166000908152607f602052604090209190915590565b6001600160a01b03831615610a18576000818152604360205260408120549061460a90859083906146d9565b6001600160a01b03831615613fe057613fe0838260016146d9565b6000806000676765c793fa10079d601b1b848760200151614646919061568d565b61465091906156ba565b9150603854858760000151614665919061568d565b61466f91906156ba565b9050816000036146835760001992506146a8565b603c54829061469b906001600160401b03168361568d565b6146a591906156ba565b92505b93509350939050565b6000603b6000815481106146c7576146c7615847565b90600052602060002001549050919050565b6034546040516301627e4560e01b81526001600160a01b038581166004830152602482018590528315156044830152909116906301627e4590606401600060405180830381600087803b15801561472f57600080fd5b505af1158015614743573d6000803e3d6000fd5b50505050505050565b6000614760846001600160a01b03166132c8565b1561484a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614797903390899088908890600401615d1b565b6020604051808303816000875af19250505080156147d2575060408051601f3d908101601f191682019092526147cf91810190615cfe565b60015b614830573d808015614800576040519150601f19603f3d011682016040523d82523d6000602084013e614805565b606091505b508051600003614828576040516320149b4360e21b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611b94565b506001611b94565b600061485d8261330f565b905061486b816000846145de565b614876600083613345565b6001600160a01b038116600081815260486020908152604080832080546000190190558583526047825280832080546001600160a01b0319169055604390915280822082815560010182905551849290600080516020615db5833981519152908390a45050565b600080826148f6676765c793fa10079d601b1b8761568d565b61490091906156ba565b6000878152604360205260408120600101549192500361493c5760b454851161493c5760405163228af07f60e21b815260040160405180910390fd5b6000868152604360205260408120600101805483929061495d908490615907565b9250508190555080604060008282546149769190615907565b909155505060395461499490676765c793fa10079d601b1b9061568d565b836040546149a2919061568d565b11156149c1576040516371239a6160e11b815260040160405180910390fd5b600086815260436020908152604080832081518083019092528054825260010154918101919091526149f4908686614625565b50509050633b9aca008111614a1c57604051631527804d60e31b815260040160405180910390fd5b600080516020615d7583398151915287836001604051614a3e9392919061591a565b60405180910390a1676765c793fa10079d601b1b614a5c858461568d565b614a6691906156ba565b979650505050505050565b6000614ac6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b439092919063ffffffff16565b805190915015610a185780806020019051810190614ae491906156ce565b610a185760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161143e565b6060611b94848460008585614b57856132c8565b614ba35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161143e565b600080866001600160a01b03168587604051614bbf9190615d58565b60006040518083038185875af1925050503d8060008114614bfc576040519150601f19603f3d011682016040523d82523d6000602084013e614c01565b606091505b5091509150614a6682828660608315614c1b575081610b03565b825115614c2b5782518084602001fd5b8160405162461bcd60e51b815260040161143e9190614d93565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215614caf579160200282015b82811115614caf578251825591602001919060010190614c94565b50614cbb929150614ce7565b5090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b5b80821115614cbb5760008155600101614ce8565b6001600160a01b0391909116815260200190565b6001600160e01b031981168114610c7f57600080fd5b600060208284031215614d3857600080fd5b8135610b0381614d10565b60005b83811015614d5e578181015183820152602001614d46565b50506000910152565b60008151808452614d7f816020860160208601614d43565b601f01601f19169290920160200192915050565b602081526000610b036020830184614d67565b600060208284031215614db857600080fd5b5035919050565b6001600160a01b0381168114610c7f57600080fd5b60008060408385031215614de757600080fd5b8235614df281614dbf565b946020939093013593505050565b600080600060608486031215614e1557600080fd5b8335614e2081614dbf565b92506020840135614e3081614dbf565b929592945050506040919091013590565b60008060408385031215614e5457600080fd5b823591506020830135614e6681614dbf565b809150509250929050565b60a081016108cc828480518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b600060208284031215614ebc57600080fd5b8135610b0381614dbf565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614f0557614f05614ec7565b604052919050565b600082601f830112614f1e57600080fd5b81356001600160401b03811115614f3757614f37614ec7565b614f4a601f8201601f1916602001614edd565b818152846020838601011115614f5f57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215614f8e57600080fd5b81356001600160401b03811115614fa457600080fd5b611b9484828501614f0d565b80356001600160401b0381168114611ca757600080fd5b60008060408385031215614fda57600080fd5b614df283614fb0565b60006001600160401b03821115614ffc57614ffc614ec7565b5060051b60200190565b600082601f83011261501757600080fd5b8135602061502c61502783614fe3565b614edd565b82815260059290921b8401810191818101908684111561504b57600080fd5b8286015b84811015615066578035835291830191830161504f565b509695505050505050565b60008060006060848603121561508657600080fd5b833561509181614dbf565b925060208401356001600160401b03808211156150ad57600080fd5b6150b987838801615006565b935060408601359150808211156150cf57600080fd5b506150dc86828701615006565b9150509250925092565b60008060008060008060c087890312156150ff57600080fd5b86356001600160401b038082111561511657600080fd5b6151228a838b01615006565b9750602089013591508082111561513857600080fd5b6151448a838b01615006565b96506040890135915061515682614dbf565b90945060608801359061516882614dbf565b90935060808801359061517a82614dbf565b90925060a0880135908082111561519057600080fd5b5061519d89828a01614f0d565b9150509295509295509295565b600080600080608085870312156151c057600080fd5b5050823594602084013594506040840135936060013592509050565b600082601f8301126151ed57600080fd5b813560206151fd61502783614fe3565b82815260059290921b8401810191818101908684111561521c57600080fd5b8286015b84811015615066578035600881106152385760008081fd5b8352918301918301615220565b600082601f83011261525657600080fd5b8135602061526661502783614fe3565b82815260059290921b8401810191818101908684111561528557600080fd5b8286015b848110156150665780356001600160401b038111156152a85760008081fd5b6152b68986838b0101614f0d565b845250918301918301615289565b600080600080608085870312156152da57600080fd5b84356001600160401b03808211156152f157600080fd5b6152fd888389016151dc565b9550602087013591508082111561531357600080fd5b5061532087828801615245565b935050604085013561533181614dbf565b9150606085013561534181614dbf565b939692955090935050565b8015158114610c7f57600080fd5b6000806040838503121561536d57600080fd5b823561537881614dbf565b91506020830135614e668161534c565b6000806000806080858703121561539e57600080fd5b84356153a981614dbf565b935060208501356153b981614dbf565b92506040850135915060608501356001600160401b038111156153db57600080fd5b6153e787828801614f0d565b91505092959194509250565b60008060006060848603121561540857600080fd5b505081359360208301359350604090920135919050565b600080600080600085870361018081121561543957600080fd5b863561544481614dbf565b9550602087013561545481614dbf565b9450604087013561546481614dbf565b9350610100605f198201121561547957600080fd5b506060860191506101608601356001600160401b0381111561549a57600080fd5b6154a688828901614f0d565b9150509295509295909350565b60008060008060008060c087890312156154cc57600080fd5b86356001600160401b03808211156154e357600080fd5b6154ef8a838b016151dc565b9750602089013591508082111561550557600080fd5b6151448a838b01615245565b6000806040838503121561552457600080fd5b823561552f81614dbf565b91506020830135614e6681614dbf565b60ff81168114610c7f57600080fd5b600080600080600080600060e0888a03121561556957600080fd5b873561557481614dbf565b9650602088013561558481614dbf565b955060408801356155948161534c565b94506060880135935060808801356155ab8161553f565b9699959850939692959460a0840135945060c09093013592915050565b600080600080608085870312156155de57600080fd5b84356001600160401b03808211156155f557600080fd5b61560188838901615006565b9550602087013591508082111561561757600080fd5b5061532087828801615006565b600181811c9082168061563857607f821691505b60208210810361565857634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561567057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176108cc576108cc615677565b634e487b7160e01b600052601260045260246000fd5b6000826156c9576156c96156a4565b500490565b6000602082840312156156e057600080fd5b8151610b038161534c565b818103818111156108cc576108cc615677565b601f821115610a1857600081815260208120601f850160051c810160208610156157255750805b601f850160051c820191505b81811015611ce157828155600101615731565b81516001600160401b0381111561575d5761575d614ec7565b6157718161576b8454615624565b846156fe565b602080601f8311600181146157a6576000841561578e5750858301515b600019600386901b1c1916600185901b178555611ce1565b600085815260208120601f198616915b828110156157d5578886015182559484019460019091019084016157b6565b50858210156157f35787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160401b0381811683821601908082111561582357615823615677565b5092915050565b60006020828403121561583c57600080fd5b8151610b0381614dbf565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b8381101561588d57815187529582019590820190600101615871565b509495945050505050565b6040815260006158ab604083018561585d565b8281036020840152611fa2818561585d565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602081526000610b03602083018461585d565b808201808211156108cc576108cc615677565b928352602083019190915260ff16604082015260600190565b60006001820161594557615945615677565b5060010190565b600181815b8085111561598757816000190482111561596d5761596d615677565b8085161561597a57918102915b93841c9390800290615951565b509250929050565b60008261599e575060016108cc565b816159ab575060006108cc565b81600181146159c157600281146159cb576159e7565b60019150506108cc565b60ff8411156159dc576159dc615677565b50506001821b6108cc565b5060208310610133831016604e8410600b8410161715615a0a575081810a6108cc565b615a14838361594c565b8060001904821115615a2857615a28615677565b029392505050565b6000610b0360ff84168361598f565b600082615a4e57615a4e6156a4565b500690565b6000808454615a6181615624565b60018281168015615a795760018114615a8e57615abd565b60ff1984168752821515830287019450615abd565b8860005260208060002060005b85811015615ab45781548a820152908401908201615a9b565b50505082870194505b505050508351615ad1818360208801614d43565b01949350505050565b600060208284031215615aec57600080fd5b8151610b038161553f565b6e020b733b63290283937ba37b1b7b61608d1b815260008251615b2181600f850160208701614d43565b650815985d5b1d60d21b600f939091019283015250601501919050565b60008251615b50818460208701614d43565b650b5d985d5b1d60d21b920191825250600601919050565b600060208284031215615b7a57600080fd5b610b0382614fb0565b600060208284031215615b9557600080fd5b8135610b038161534c565b634e487b7160e01b600052602160045260246000fd5b60008060408385031215615bc957600080fd5b505080516020909101519092909150565b60008060008060008060c08789031215615bf357600080fd5b8651615bfe81614dbf565b6020880151604089015160608a015160808b015160a0909b0151939c929b509099909850965090945092505050565b60008060008060808587031215615c4357600080fd5b845193506020850151615c5581614dbf565b6040860151606090960151949790965092505050565b9283526001600160a01b03918216602084015216604082015260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b038781168252868116602083015285166040820152606081018490526080810183905260c060a082018190526000906143c990830184614d67565b828152604060208201526000611b946040830184614d67565b600060208284031215615d1057600080fd5b8151610b0381614d10565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615d4e90830184614d67565b9695505050505050565b60008251615d6a818460208701614d43565b919091019291505056fe70cf49afe7355562d5b022e594790f22b71ad8cc7eec902fa5feac7c67f71091722cb71fa87c947148cefc06dd890af5802a6a00207c5ddecf1191bf71ce3cd4ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220989782b92d84509719429db7a0d2acce087612fa7a3bed7609b857375005fdab64736f6c63430008110033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103015760003560e01c8063010db1951461030657806301ffc9a71461032f57806306fdde0314610352578063081812fc14610367578063087a60071461037a578063095ea7b3146103915780630e198f22146103a657806313888565146103af57806323b872dd146103b8578063254cf439146103cb578063307439af146103fd57806334ce998a1461041d57806335836f15146104255780633644e5151461043857806339393ac91461044057806339eb4dc6146104535780633ae2325f146104675780633af32abf1461047a5780633c2e941b1461049a57806342842e0e146104a3578063430c2081146104b65780634f7e43df146104c957806355f804b3146104e35780635c975abb146104f657806361d027b31461050a5780636352211e1461051d57806370a082311461053057806374107543146105435780637aacfffa146105565780637adbf973146105925780637c0f59f4146105a55780637c3a00fd146105bf5780637dc0d1d0146105d25780637e53bd97146105e55780637e56d47c146105f85780637ecebe001461060b578063835986b41461063457806389050f1d1461064757806395d89b41146106595780639a3b6f2f146106615780639f48118f146106a7578063a22cb465146106b2578063af2c8c2e146106c5578063b1511cc9146106ce578063b4bd6f46146106e1578063b88d4fde146106f4578063bbcac55714610707578063bfc7ad2e14610710578063c13cacae14610719578063c4ae31681461072c578063c66d8b0114610734578063c87b56dd1461074e578063d8dfeb4514610761578063d9b1cb5b14610774578063de1f776514610787578063de8fc69814610799578063df011c41146107ac578063e182b883146107bf578063e1c84ea4146107d2578063e626648a146107db578063e985e9c5146107f5578063e9cbd82214610808578063f0f442601461081b578063f51cc7dd1461082e578063fad9aba314610841578063fc29b0211461084a578063fd527cf81461085d575b600080fd5b603754610319906001600160a01b031681565b6040516103269190614cfc565b60405180910390f35b61034261033d366004614d26565b610865565b6040519015158152602001610326565b61035a6108d2565b6040516103269190614d93565b610319610375366004614da6565b610960565b610383603f5481565b604051908152602001610326565b6103a461039f366004614dd4565b610991565b005b610383603e5481565b61038360415481565b6103a46103c6366004614e00565b610a1d565b603c546103e590600160401b90046001600160401b031681565b6040516001600160401b039091168152602001610326565b61041061040b366004614e41565b610a58565b6040516103269190614e71565b610383610b0a565b610383610433366004614da6565b610b3c565b610383610b78565b6103a461044e366004614eaa565b610b82565b603d5461034290600160c01b900460ff1681565b610383610475366004614da6565b610c82565b610383610488366004614eaa565b60446020526000908152604090205481565b61038360455481565b6103a46104b1366004614e00565b610ca3565b6103426104c4366004614dd4565b610cbe565b603d546103e590600160801b90046001600160401b031681565b6103a46104f1366004614f7c565b610cca565b603d5461034290600160c81b900460ff1681565b603354610319906001600160a01b031681565b61031961052b366004614da6565b610d68565b61038361053e366004614eaa565b610d73565b6103a4610551366004614fc7565b610db8565b61057d610564366004614da6565b6043602052600090815260409020805460019091015482565b60408051928352602083019190915201610326565b6103a46105a0366004614eaa565b61117d565b603c546103e590600160c01b90046001600160401b031681565b603d546103e5906001600160401b031681565b603654610319906001600160a01b031681565b6103a46105f3366004615071565b6112c4565b6104106106063660046150e6565b6113e9565b610383610619366004614eaa565b6001600160a01b03166000908152607f602052604090205490565b6103a46106423660046151aa565b6119a2565b610383676765c793fa10079d601a1b81565b61035a611b5a565b61067461066f3660046152c4565b611b67565b60405161032691908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b610383633b9aca0081565b6103a46106c036600461535a565b611b9c565b61038360405481565b6103a46106dc366004614da6565b611ba7565b6103836106ef366004614eaa565b611c70565b6103a4610702366004615388565b611cac565b61038360425481565b61038360b65481565b6103a46107273660046153f3565b611ce9565b6103a4611da6565b603d546103e590600160401b90046001600160401b031681565b61035a61075c366004614da6565b611e55565b603454610319906001600160a01b031681565b6103a461078236600461541f565b611fab565b610383676765c793fa10079d601b1b81565b6106746107a73660046154b3565b612547565b603c546103e5906001600160401b031681565b6103836107cd366004614da6565b612de2565b61038360395481565b603c546103e590600160801b90046001600160401b031681565b610342610803366004615511565b612df2565b603554610319906001600160a01b031681565b6103a4610829366004614eaa565b612e20565b6103a461083c36600461554e565b612ec0565b61038360b45481565b6104106108583660046155c8565b61317f565b61057d6131a9565b60006001600160e01b03198216635b5e139f60e01b148061089657506001600160e01b031982166380ac58cd60e01b145b806108b157506001600160e01b0319821663430c208160e01b145b806108cc57506001600160e01b031982166301ffc9a760e01b145b92915050565b607d80546108df90615624565b80601f016020809104026020016040519081016040528092919081815260200182805461090b90615624565b80156109585780601f1061092d57610100808354040283529160200191610958565b820191906000526020600020905b81548152906001019060200180831161093b57829003601f168201915b505050505081565b600061096b826132d7565b6109885760405163062a39dd60e11b815260040160405180910390fd5b6108cc826132f4565b600061099c8261330f565b9050806001600160a01b0316836001600160a01b0316036109d0576040516349fa8bc360e11b815260040160405180910390fd5b336001600160a01b038216148015906109f057506109ee8133612df2565b155b15610a0e5760405163c19f17a960e01b815260040160405180910390fd5b610a188383613345565b505050565b3381610a2982826133b3565b610a465760405163c19f17a960e01b815260040160405180910390fd5b610a51858585613431565b5050505050565b610a60614c45565b60008381526043602090815260409182902082518084018452815481526001909101548183015260365483516315f789a960e21b81529351610b0394929387936001600160a01b03909316926357de26a492600480830193928290030181865afa158015610ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af6919061565e565b610afe613563565b6136f8565b9392505050565b6000676765c793fa10079d601b1b610b20613563565b604054610b2d919061568d565b610b3791906156ba565b905090565b6000676765c793fa10079d601b1b610b52613563565b600084815260436020526040902060010154610b6e919061568d565b6108cc91906156ba565b6000610b37613a61565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b890610bb2903390600401614cfc565b602060405180830381865afa158015610bcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf391906156ce565b610c1057604051633b8d9d7560e21b815260040160405180910390fd5b6001600160a01b03811615610c5f576001600160a01b038116600090815260446020526040902054610c439060016156eb565b6001600160a01b03821660009081526044602052604090205550565b603d805460ff60c01b198116600160c01b9182900460ff16159091021790555b50565b603b8181548110610c9257600080fd5b600091825260209091200154905081565b610a1883838360405180602001604052806000815250611cac565b6000610b0383836133b3565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990610cfa903390600401614cfc565b602060405180830381865afa158015610d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3b91906156ce565b610d5857604051632678482f60e21b815260040160405180910390fd5b6046610d648282615744565b5050565b60006108cc8261330f565b60006001600160a01b038216610d9c5760405163d92e233d60e01b815260040160405180910390fd5b506001600160a01b031660009081526048602052604090205490565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990610de8903390600401614cfc565b602060405180830381865afa158015610e05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2991906156ce565b610e4657604051632678482f60e21b815260040160405180910390fd5b806121a360f11b03610ea957603d546001600160401b03600160401b90910481169083161115610e8957604051637650e96360e11b815260040160405180910390fd5b603c80546001600160401b0319166001600160401b038416179055611136565b80622a242360e91b03610f0d57633b9aca00826001600160401b03161015610ee45760405163da6a17b960e01b815260040160405180910390fd5b603c8054600160401b600160801b031916600160401b6001600160401b03851602179055611136565b8061212360f11b03610f7057633b9aca00826001600160401b03161115610f4757604051637650e96360e11b815260040160405180910390fd5b603c8054600160801b600160c01b031916600160801b6001600160401b03851602179055611136565b8061292360f11b03610fec57603d54633b9aca0090610f9f90600160401b90046001600160401b031684615803565b6001600160401b03161115610fc757604051637650e96360e11b815260040160405180910390fd5b603c80546001600160c01b0316600160c01b6001600160401b03851602179055611136565b806124a960f11b0361102157611000613acd565b50603d80546001600160401b0319166001600160401b038416179055611136565b80614c5360f01b036110b957603c546001600160401b03808416911611806110725750603c54633b9aca009061106790600160c01b90046001600160401b031684615803565b6001600160401b0316115b156110905760405163180d062b60e31b815260040160405180910390fd5b603d8054600160401b600160801b031916600160401b6001600160401b03851602179055611136565b806213531160ea1b0361111d57633b9aca00826001600160401b031611156110f457604051637650e96360e11b815260040160405180910390fd5b603d8054600160801b600160c01b031916600160801b6001600160401b03851602179055611136565b60405163e1daa9cf60e01b815260040160405180910390fd5b604080516001600160401b0384168152602081018390527f13b367dac93b85d1ed9b3d8961d8b48e1a677c9800bb1613b4b0416b2d5b61d091015b60405180910390a15050565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b8906111ad903390600401614cfc565b602060405180830381865afa1580156111ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ee91906156ce565b61120b57604051633b8d9d7560e21b815260040160405180910390fd5b603354604080516361d027b360e01b815290516001600160a01b03928316928416916361d027b39160048083019260209291908290030181865afa158015611257573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127b919061582a565b6001600160a01b0316146112a2576040516302979eb960e31b815260040160405180910390fd5b603680546001600160a01b0319166001600160a01b0392909216919091179055565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de9906112f4903390600401614cfc565b602060405180830381865afa158015611311573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133591906156ce565b61135257604051632678482f60e21b815260040160405180910390fd5b8060008151811061136557611365615847565b602002602001015160000361138d57604051631746545d60e11b815260040160405180910390fd5b80516113a090603b906020840190614c74565b50826001600160a01b03167feb74d4d9fea592587c926aeb35eb6a7893fb28db0c1c8eb2eb3c586e7164b76c83836040516113dc929190615898565b60405180910390a2505050565b6113f1614c45565b603d54600160c81b900460ff161561141c576040516313d0ff5960e31b815260040160405180910390fd5b6002600154036114475760405162461bcd60e51b815260040161143e906158bd565b60405180910390fd5b6002600155865186518114158061145c575080155b1561147a576040516346282e8d60e01b815260040160405180910390fd5b603660009054906101000a90046001600160a01b03166001600160a01b03166357de26a46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f1919061565e565b60608301526114fe613acd565b60808301526040517f965a177723c641ee49150b583a0b9ad4730bb20d3474e00ae5a65e777c00d67b90611533908a906158f4565b60405180910390a160005b81811015611912576000604360008b848151811061155e5761155e615847565b6020026020010151815260200190815260200160002060405180604001604052908160008201548152602001600182015481525050905060006115ab8233876060015188608001516136f8565b905080604001516000141580156115df575080604001518a84815181106115d4576115d4615847565b602002602001015110155b80611606575080600001518a84815181106115fc576115fc615847565b6020026020010151115b1561162f5780600001518a848151811061162257611622615847565b6020026020010181815250505b600085606001518260600151611645919061568d565b603854633b9aca008d878151811061165f5761165f615847565b6020026020010151611671919061568d565b61167b919061568d565b61168591906156ba565b90506116c08c858151811061169c5761169c615847565b602002602001015182856000015111156116b657826116b9565b84515b6000613b6e565b825181106117f6575081516020830151604080546000906116e29084906156eb565b92505081905550604360008d86815181106116ff576116ff615847565b60209081029190910181015182528101919091526040016000908120818155600101819055603d548c51633b9aca0091600160401b90046001600160401b0316908e908890811061175257611752615847565b6020026020010151611764919061568d565b61176e91906156ba565b905082608001518110611782576000611792565b80836080015161179291906156eb565b876040018181516117a39190615907565b905250508b51600080516020615d75833981519152908d90869081106117cb576117cb615847565b6020026020010151846020015160006040516117e99392919061591a565b60405180910390a16118b7565b80604360008e878151811061180d5761180d615847565b60200260200101518152602001908152602001600020600001600082825461183591906156eb565b925050819055506118b58c858151811061185157611851615847565b6020026020010151633b9aca00603d60089054906101000a90046001600160401b03166001600160401b03168e888151811061188f5761188f615847565b60200260200101516118a1919061568d565b6118ab91906156ba565b8860800151613b81565b505b80866020018181516118c99190615907565b9052508a518b90859081106118e0576118e0615847565b6020026020010151866000018181516118f99190615907565b90525061190b92508391506159339050565b905061153e565b50603d54633b9aca009061193690600160401b90046001600160401b0316826156eb565b8351611942919061568d565b61194c91906156ba565b6041600082825461195d9190615907565b9091555050604082015160428054600090611979908490615907565b909155505060208201518251611993919088888888613cb3565b50600180559695505050505050565b603d54600160c81b900460ff16156119cd576040516313d0ff5960e31b815260040160405180910390fd5b6033546040516333b52a9f60e11b81526001600160a01b039091169063676a553e906119fd903390600401614cfc565b602060405180830381865afa158015611a1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3e91906156ce565b611a5b5760405163027f480760e01b815260040160405180910390fd5b603c54600090600160c01b90046001600160401b0316821015611a9957603c54611a96908390600160c01b90046001600160401b03166156eb565b90505b603c54600090600160801b90046001600160401b0316841115611ad657603c54611ad390600160801b90046001600160401b0316856156eb565b90505b6000611ae76002633b9aca00615a30565b611af583633b9aca006156eb565b611b0385633b9aca006156eb565b611b0d908961568d565b611b17919061568d565b611b2191906156ba565b9050611b2d81876156eb565b60416000828254611b3e9190615907565b90915550611b50905087826000613b81565b5050505050505050565b607e80546108df90615624565b611b6f614cbf565b60408051600080825260208201909252611b9191879187918791879190612547565b90505b949350505050565b610d64338383613dbd565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990611bd7903390600401614cfc565b602060405180830381865afa158015611bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1891906156ce565b611c3557604051632678482f60e21b815260040160405180910390fd5b60398190556040518181527fdd63b3dcdbebad734892f7c7a26d0f647fbc7eec973e0775f5229018ac4ab47a9060200160405180910390a150565b603d54600090600160c81b900460ff1615611c9e576040516313d0ff5960e31b815260040160405180910390fd5b6108cc82613e72565b919050565b3382611cb882826133b3565b611cd55760405163c19f17a960e01b815260040160405180910390fd5b611ce186868686613fac565b505050505050565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b890611d19903390600401614cfc565b602060405180830381865afa158015611d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5a91906156ce565b611d7757604051633b8d9d7560e21b815260040160405180910390fd5b81831115611d985760405163180d062b60e31b815260040160405180910390fd5b60b49290925560b65560b555565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990611dd6903390600401614cfc565b602060405180830381865afa158015611df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1791906156ce565b611e3457604051632678482f60e21b815260040160405180910390fd5b603d805460ff60c81b198116600160c81b9182900460ff1615909102179055565b6060611e60826132d7565b611e7d5760405163062a39dd60e11b815260040160405180910390fd5b8160005b8115611ea457611e9081615933565b9050611e9d600a836156ba565b9150611e81565b6000816001600160401b03811115611ebe57611ebe614ec7565b6040519080825280601f01601f191660200182016040528015611ee8576020820181803683370190505b5090505b8415611f5357611efd6001836156eb565b9150611f0a600a86615a3f565b611f15906030615907565b60f81b818381518110611f2a57611f2a615847565b60200101906001600160f81b031916908160001a905350611f4c600a866156ba565b9450611eec565b60468054611f6090615624565b9050600003611f7e5760405180602001604052806000815250611fa2565b604681604051602001611f92929190615a53565b6040516020818303038152906040525b95945050505050565b600054610100900460ff1615808015611fcb5750600054600160ff909116105b80611fec5750611fda306132c8565b158015611fec575060005460ff166001145b61204f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161143e565b6000805460ff191660011790558015612072576000805461ff0019166101001790555b856001600160a01b0316846001600160a01b03166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120de919061582a565b6001600160a01b031614612105576040516302979eb960e31b815260040160405180910390fd5b603380546001600160a01b038089166001600160a01b0319928316179092556034805492881692909116821790556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801561216e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121929190615ada565b61219d90600a615a30565b603881905550856001600160a01b031663e9cbd8226040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612205919061582a565b603580546001600160a01b03199081166001600160a01b039384161790915560368054909116918616919091179055604051600090612248908490602001615af7565b60408051601f198184030181529190529050607d6122668282615744565b5061227081613fe6565b826040516020016122819190615b3e565b604051602081830303815290604052607e908161229e9190615744565b50676765c793fa10079d601b1b603f5542603e556122c260a0850160808601615b68565b6001600160401b03166122db6040860160208701615b68565b6001600160401b0316118061230b5750633b9aca0061230060a0860160808701615b68565b6001600160401b0316115b8061233157506123216060850160408601615b68565b6001600160401b0316633b9aca00115b806123585750633b9aca0061234c60c0860160a08701615b68565b6001600160401b031610155b80612365575060e0840135155b1561238357604051631746545d60e11b815260040160405180910390fd5b83356039556123986040850160208601615b68565b603c80546001600160401b0319166001600160401b03929092169190911790556123c86060850160408601615b68565b603c80546001600160401b0392909216600160401b02600160401b600160801b03199092169190911790556124036080850160608601615b68565b603d80546001600160401b0319166001600160401b039290921691909117905561243360a0850160808601615b68565b603d80546001600160401b0392909216600160401b02600160401b600160801b031990921691909117905561246e60c0850160a08601615b68565b603d80546001600160401b0392909216600160801b02600160801b600160c01b03199092169190911790556124a960e0850160c08601615b83565b603d8054911515600160c01b0260ff60c01b19909216919091179055604080516020810190915260e085013581526124e590603b906001614c74565b5050603d805460ff60c81b1916600160c81b1790558015611ce1576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050505050565b61254f614cbf565b603d54600160c81b900460ff161561257a576040516313d0ff5960e31b815260040160405180910390fd5b60026001540361259c5760405162461bcd60e51b815260040161143e906158bd565b6002600155855187511415806125b157508651155b156125cf576040516346282e8d60e01b815260040160405180910390fd5b6000806000806000805b8c51811015612b4b5760008d82815181106125f6576125f6615847565b602002602001015190506000600781111561261357612613615ba0565b81600781111561262557612625615ba0565b036126655761265f8d838151811061263f5761263f615847565b602002602001015180602001905181019061265a919061582a565b613e72565b50612b3a565b600281600781111561267957612679615ba0565b036126e2578c828151811061269057612690615847565b60200260200101518060200190518101906126ab9190615bb6565b9550925060008390036126be5760455492505b6126c883866140bc565b84886060018181516126da9190615907565b905250612b3a565b60078160078111156126f6576126f6615ba0565b036127ce5760008060008f858151811061271257612712615847565b602002602001015180602001905181019061272d9190615bda565b60345460405163d505accf60e01b81526001600160a01b038089166004830152306024830152604482018890526064820187905260ff8616608483015260a4820185905260c48201849052969f50939d50939b509497509550929350169063d505accf9060e401600060405180830381600087803b1580156127ae57600080fd5b505af11580156127c2573d6000803e3d6000fd5b50505050505050612b3a565b866000036127e1576127de613acd565b96505b60048160078111156127f5576127f5615ba0565b036128c4578c828151811061280c5761280c615847565b60200260200101518060200190518101906128279190615bb6565b94509250600083900361283a5760455492505b612845838589613b81565b603c5490945060009061286c90600160c01b90046001600160401b0316633b9aca006156eb565b61287a633b9aca008761568d565b61288491906156ba565b905061289085826156eb565b604160008282546128a19190615907565b9250508190555080896020018181516128ba9190615907565b905250612b3a9050565b8560000361294657603660009054906101000a90046001600160a01b03166001600160a01b03166357de26a46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561291f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612943919061565e565b95505b600181600781111561295a5761295a615ba0565b036129d8578c828151811061297157612971615847565b602002602001015180602001905181019061298c919061565e565b92508260000361299c5760455492505b6129a7838789614134565b809650819550505084886040018181516129c19190615907565b9052506020880180518591906126da908390615907565b60038160078111156129ec576129ec615ba0565b03612a4f578c8281518110612a0357612a03615847565b6020026020010151806020019051810190612a1e9190615bb6565b955092506000839003612a315760455492505b612a3d8386888a614254565b84886040018181516126da9190615907565b6005816007811115612a6357612a63615ba0565b03612ac8578c8281518110612a7a57612a7a615847565b6020026020010151806020019051810190612a959190615bb6565b945092506000839003612aa85760455492505b612ab48385888a61433a565b935083886000018181516126da9190615907565b6006816007811115612adc57612adc615ba0565b03612b3a576000808e8481518110612af657612af6615847565b6020026020010151806020019051810190612b119190615c2d565b9850919650925090506000859003612b295760455494505b612b37858383898c8e6143d5565b50505b50612b4481615933565b90506125d9565b508551602087015110612c425785516020870151600091612b6b916156eb565b90508660600151876040015110612ba257612b9d87606001518860400151612b9391906156eb565b828d8d8d8d613cb3565b612c3c565b8015612c0f57603554604051630d43af8160e21b81526001600160a01b039091169063350ebe0490612bdc9084908f903390600401615c6b565b600060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050505b612c3c333089604001518a60600151612c2891906156eb565b6034546001600160a01b0316929190614510565b50612dce565b60208601518651600091612c55916156eb565b6035546040516340c10f1960e01b81529192506001600160a01b0316906340c10f1990612c88908d908590600401615c8a565b600060405180830381600087803b158015612ca257600080fd5b505af1158015612cb6573d6000803e3d6000fd5b50505050866060015187604001511115612cfa57612cf58a88606001518960400151612ce291906156eb565b6034546001600160a01b0316919061457b565b612dcc565b600087604001518860600151612d1091906156eb565b90508015612dca57885115612db257896001600160a01b031663a5d4096b603560009054906101000a90046001600160a01b0316603460009054906101000a90046001600160a01b03163385878f6040518763ffffffff1660e01b8152600401612d7f96959493929190615ca3565b600060405180830381600087803b158015612d9957600080fd5b505af1158015612dad573d6000803e3d6000fd5b505050505b603454612dca906001600160a01b0316333084614510565b505b505b505060018055509198975050505050505050565b603a8181548110610c9257600080fd5b6001600160a01b039182166000908152604a6020908152604080832093909416825291909152205460011490565b6033546001600160a01b03163314612e4b5760405163b90cdbb160e01b815260040160405180910390fd5b603380546001600160a01b0319166001600160a01b0383811691909117909155603654604051630787a21360e51b815291169063f0f4426090612e92908490600401614cfc565b600060405180830381600087803b158015612eac57600080fd5b505af1158015610a51573d6000803e3d6000fd5b83421115612ee15760405163f87d927160e01b815260040160405180910390fd5b6fa2a8918ca85bafe22016d0b997e4df60600160ff1b03811180612f1857508260ff16601b14158015612f1857508260ff16601c14155b15612f3657604051638baa579f60e01b815260040160405180910390fd5b6000612f40613a61565b608254898989612f4f8d61459a565b6040805160208101969096526001600160a01b03948516908601529290911660608401521515608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001612fc692919061190160f01b81526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050612ff0886001600160a01b03166132c8565b156130cc57604080516020810185905280820184905260f886901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b038a1691631626ba7e91613056918591606501615ce5565b602060405180830381865afa158015613073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130979190615cfe565b6001600160e01b031916631626ba7e60e01b146130c757604051638baa579f60e01b815260040160405180910390fd5b613174565b6040805160008082526020820180845284905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613120573d6000803e3d6000fd5b505050602060405103519050886001600160a01b0316816001600160a01b031614158061315457506001600160a01b038116155b1561317257604051638baa579f60e01b815260040160405180910390fd5b505b611b50888888613dbd565b613187614c45565b60408051600080825260208201909252611b91918791879187918791906113e9565b60335460009081906001600160a01b031633146131d95760405163b90cdbb160e01b815260040160405180910390fd5b6131e1613acd565b50506041805460428054600093849055929055915080821061327a5761320781836156eb565b6035546033546040516340c10f1960e01b8152929450600093506001600160a01b03918216926340c10f19926132439216908690600401615c8a565b600060405180830381600087803b15801561325d57600080fd5b505af1158015613271573d6000803e3d6000fd5b5050505061328b565b61328482826156eb565b9050600091505b60408051838152602081018390527ffeb12225c131aab793a00c5239afb778932d170fa28ce6e9d23703e4bd892121910160405180910390a19091565b6001600160a01b03163b151590565b6000908152604760205260409020546001600160a01b0316151590565b6000908152604960205260409020546001600160a01b031690565b6000818152604760205260409020546001600160a01b031680611ca75760405163062a39dd60e11b815260040160405180910390fd5b600081815260496020526040902080546001600160a01b0319166001600160a01b038416908117909155819061337a8261330f565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000806133bf8361330f565b9050806001600160a01b0316846001600160a01b031614806133fa5750836001600160a01b03166133ef846132f4565b6001600160a01b0316145b80611b9457506001600160a01b038082166000908152604a6020908152604080832093881683529290522054600114949350505050565b826001600160a01b03166134448261330f565b6001600160a01b03161461346b5760405163c19f17a960e01b815260040160405180910390fd5b6001600160a01b0382166134925760405163d92e233d60e01b815260040160405180910390fd5b603d54600160c01b900460ff1680156134c457506001600160a01b038216600090815260446020526040902054600114155b156134e257604051630b094f2760e31b815260040160405180910390fd5b6134ed8383836145de565b6134f8600082613345565b6001600160a01b038084166000818152604860209081526040808320805460001901905593861680835284832080546001019055858352604790915283822080546001600160a01b03191682179055925184939291600080516020615db583398151915291a4505050565b600080603e544261357491906156eb565b603d549091506001600160401b031681158061358e575080155b1561359d57603f549250505090565b60006135aa6001846156eb565b90506000600284116135bd5760006135c8565b6135c86002856156eb565b90506000676765c793fa10079d601b1b676765c793fa10079d601a1b6135ee868061568d565b6135f89190615907565b61360291906156ba565b90506000676765c793fa10079d601b1b676765c793fa10079d601a1b613628878561568d565b6136329190615907565b61363c91906156ba565b9050600060028361364d878a61568d565b613657919061568d565b61366191906156ba565b9050600060068386613673898c61568d565b61367d919061568d565b613687919061568d565b61369191906156ba565b9050676765c793fa10079d601b1b81836136ab8b8b61568d565b6136c090676765c793fa10079d601b1b615907565b6136ca9190615907565b6136d49190615907565b603f546136e1919061568d565b6136eb91906156ba565b9850505050505050505090565b613700614c45565b6000806000613710888787614625565b925092509250633b9aca00831061373a576040516315fe9b6160e21b815260040160405180910390fd5b6000633b9aca0061374b85826156eb565b6137548a6146b1565b61375e919061568d565b61376891906156ba565b603d54909150600160801b90046001600160401b03168110156137985761379381633b9aca006156eb565b6137b9565b603d546137b990600160801b90046001600160401b0316633b9aca006156eb565b603d54909150600160401b90046001600160401b03166000806137e16002633b9aca00615a30565b603c546137f791906001600160401b031661568d565b83613802868a61568d565b61380c919061568d565b1061399157603c546001600160401b031661382c6002633b9aca00615a30565b613836919061568d565b603c54859061385590600160401b90046001600160401b03168661568d565b61385f919061568d565b61386991906156eb565b603c548590633b9aca0090613887906001600160401b03168961568d565b603c546138a5908b90600160401b90046001600160401b031661568d565b6138af91906156eb565b6138b9919061568d565b6138c3919061568d565b6138cd91906156ba565b60b6549092506138e1633b9aca008261568d565b6138eb858561568d565b6138f59190615907565b613903633b9aca008961568d565b1161398b5761391d676765c793fa10079d601b1b8561568d565b633b9aca008b8f60200151613932919061568d565b61393c919061568d565b61394691906156ba565b613951906001615907565b9250808711156139865783633b9aca0061396b838a6156eb565b613975919061568d565b61397f91906156ba565b915061398b565b600191505b50613a0f565b6038546139a290633b9aca0061568d565b8c518b906139b190879061568d565b6139bb919061568d565b6139c591906156ba565b6139d0906001615907565b915060b554851115613a0b57633b9aca008460b554876139f091906156eb565b6139fa919061568d565b613a0491906156ba565b9050613a0f565b5060015b818852613a1c848b61568d565b603854613a2d633b9aca008561568d565b613a37919061568d565b613a4191906156ba565b602089015260408801525050606085015250608083015250949350505050565b60808054608154604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f602082015290810192909252606082015246918101919091523060a082015260009060c00160405160208183030381529060405280519060200120905090565b6000613ad7613563565b90506000676765c793fa10079d601b1b603f5483613af591906156eb565b604054613b02919061568d565b613b0c91906156ba565b90508060416000828254613b209190615907565b9091555050603f82905542603e8190556040805184815260208101929092527fd1fa8ba00a3bf20274346919dce0de62d2a140af2c71fe7e29fa6472eea3bb9d910160405180910390a15090565b610a18613b7a8461330f565b83836146d9565b600081600003613b9657613b93613acd565b91505b60008481526043602052604081206001015490676765c793fa10079d601b1b613bbf858461568d565b613bc991906156ba565b9050808510613bda57935080613bfd565b83613bf0676765c793fa10079d601b1b8761568d565b613bfa91906156ba565b90505b613c0781836156eb565b91508060406000828254613c1b91906156eb565b90915550508115801590613c505750676765c793fa10079d601b1b60b454613c43919061568d565b613c4d858461568d565b11155b15613c6e5760405163228af07f60e21b815260040160405180910390fd5b60008681526043602052604080822060010184905551600080516020615d7583398151915291613ca1918991859161591a565b60405180910390a15092949350505050565b8515613cd057603454613cd0906001600160a01b0316848861457b565b8415611ce157805115613d4f5760345460355460405163a5d4096b60e01b81526001600160a01b038086169363a5d4096b93613d1c9391831692169089908b908d908990600401615ca3565b600060405180830381600087803b158015613d3657600080fd5b505af1158015613d4a573d6000803e3d6000fd5b505050505b603554604051630d43af8160e21b81526001600160a01b039091169063350ebe0490613d8390889088903390600401615c6b565b600060405180830381600087803b158015613d9d57600080fd5b505af1158015613db1573d6000803e3d6000fd5b50505050505050505050565b826001600160a01b0316826001600160a01b031603613def576040516320c5195360e21b815260040160405180910390fd5b600081613dfd576000613e00565b60015b6001600160a01b038581166000818152604a602090815260408083209489168084529482529182902060ff959095169485905590518615158152939450919290917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b603d54600090600160c01b900460ff168015613ec157506001600160a01b0382166000908152604460205260409020546001141580613ec1575033600090815260446020526040902054600114155b15613edf57604051630b094f2760e31b815260040160405180910390fd5b6001600160a01b038216613f065760405163d92e233d60e01b815260040160405180910390fd5b506045805460010190819055613f1e600083836145de565b6001600160a01b038216600081815260486020908152604080832080546001019055848352604790915280822080546001600160a01b031916841790555183929190600080516020615db5833981519152908290a4613f8f600083836040518060200160405280600081525061474c565b611ca7576040516320149b4360e21b815260040160405180910390fd5b613fb7848484613431565b613fc38484848461474c565b613fe0576040516320149b4360e21b815260040160405180910390fd5b50505050565b600054610100900460ff166140515760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161143e565b7f3f43a9c6bafb5c7aab4e0cfe239dc5d4c15caf0381c6104188191f78a6640bd860825580516020918201206080556040805180820190915260018152603160f81b9101527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6608155565b6140c5826132d7565b6140e25760405163062a39dd60e11b815260040160405180910390fd5b6140ee82826001613b6e565b6000828152604360205260408120805483929061410c908490615907565b9091555050604051600080516020615d9583398151915290611171908490849060019061591a565b600080338561414382826133b3565b6141605760405163c19f17a960e01b815260040160405180910390fd5b600087815260436020908152604080832081518083019092528054825260010154918101919091529080614195838a8a614625565b5091509150633b9aca0082116141be57604051631527804d60e31b815260040160405180910390fd5b8260200151604060008282546141d491906156eb565b909155506141e390508a614852565b603c5460009061420790600160c01b90046001600160401b0316633b9aca006156eb565b614215633b9aca008461568d565b61421f91906156ba565b905061422b82826156eb565b6041600082825461423c9190615907565b90915550509251929a92995091975050505050505050565b338461426082826133b3565b61427d5760405163c19f17a960e01b815260040160405180910390fd5b61428986866000613b6e565b600086815260436020526040812080548792906142a79084906156eb565b9091555050600086815260436020908152604080832081518083019092528054825260010154918101919091526142df908686614625565b50509050633b9aca00811161430757604051631527804d60e31b815260040160405180910390fd5b600080516020615d95833981519152878760006040516143299392919061591a565b60405180910390a150505050505050565b6000338561434882826133b3565b6143655760405163c19f17a960e01b815260040160405180910390fd5b614371878787876148dd565b603c54909650600090633b9aca009061439b908990600160801b90046001600160401b031661568d565b6143a591906156ba565b905080604160008282546143b99190615907565b909155506143c9905081886156eb565b98975050505050505050565b33866143e182826133b3565b6143fe5760405163c19f17a960e01b815260040160405180910390fd5b60408051898152602081018890526001600160a01b038916818301526060810187905290517fddd3b70af631334f7552aadb582ed091018e62e103fa8b150ca66cc700d4dac69181900360800190a1614459888686866148dd565b9450306001600160a01b0388160361447c57614476868685613b81565b50611b50565b603c546040516320d661ad60e21b815260048101889052602481018790526001600160401b03600160801b830481166044830152600160c01b90920490911660648201526001600160a01b0388169063835986b490608401600060405180830381600087803b1580156144ee57600080fd5b505af1158015614502573d6000803e3d6000fd5b505050505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052613fe09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614a71565b610a188363a9059cbb60e01b8484604051602401614544929190615c8a565b6001600160a01b0381166000908152607f60205260409020546145be816001615907565b6001600160a01b039092166000908152607f602052604090209190915590565b6001600160a01b03831615610a18576000818152604360205260408120549061460a90859083906146d9565b6001600160a01b03831615613fe057613fe0838260016146d9565b6000806000676765c793fa10079d601b1b848760200151614646919061568d565b61465091906156ba565b9150603854858760000151614665919061568d565b61466f91906156ba565b9050816000036146835760001992506146a8565b603c54829061469b906001600160401b03168361568d565b6146a591906156ba565b92505b93509350939050565b6000603b6000815481106146c7576146c7615847565b90600052602060002001549050919050565b6034546040516301627e4560e01b81526001600160a01b038581166004830152602482018590528315156044830152909116906301627e4590606401600060405180830381600087803b15801561472f57600080fd5b505af1158015614743573d6000803e3d6000fd5b50505050505050565b6000614760846001600160a01b03166132c8565b1561484a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614797903390899088908890600401615d1b565b6020604051808303816000875af19250505080156147d2575060408051601f3d908101601f191682019092526147cf91810190615cfe565b60015b614830573d808015614800576040519150601f19603f3d011682016040523d82523d6000602084013e614805565b606091505b508051600003614828576040516320149b4360e21b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611b94565b506001611b94565b600061485d8261330f565b905061486b816000846145de565b614876600083613345565b6001600160a01b038116600081815260486020908152604080832080546000190190558583526047825280832080546001600160a01b0319169055604390915280822082815560010182905551849290600080516020615db5833981519152908390a45050565b600080826148f6676765c793fa10079d601b1b8761568d565b61490091906156ba565b6000878152604360205260408120600101549192500361493c5760b454851161493c5760405163228af07f60e21b815260040160405180910390fd5b6000868152604360205260408120600101805483929061495d908490615907565b9250508190555080604060008282546149769190615907565b909155505060395461499490676765c793fa10079d601b1b9061568d565b836040546149a2919061568d565b11156149c1576040516371239a6160e11b815260040160405180910390fd5b600086815260436020908152604080832081518083019092528054825260010154918101919091526149f4908686614625565b50509050633b9aca008111614a1c57604051631527804d60e31b815260040160405180910390fd5b600080516020615d7583398151915287836001604051614a3e9392919061591a565b60405180910390a1676765c793fa10079d601b1b614a5c858461568d565b614a6691906156ba565b979650505050505050565b6000614ac6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b439092919063ffffffff16565b805190915015610a185780806020019051810190614ae491906156ce565b610a185760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161143e565b6060611b94848460008585614b57856132c8565b614ba35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161143e565b600080866001600160a01b03168587604051614bbf9190615d58565b60006040518083038185875af1925050503d8060008114614bfc576040519150601f19603f3d011682016040523d82523d6000602084013e614c01565b606091505b5091509150614a6682828660608315614c1b575081610b03565b825115614c2b5782518084602001fd5b8160405162461bcd60e51b815260040161143e9190614d93565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215614caf579160200282015b82811115614caf578251825591602001919060010190614c94565b50614cbb929150614ce7565b5090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b5b80821115614cbb5760008155600101614ce8565b6001600160a01b0391909116815260200190565b6001600160e01b031981168114610c7f57600080fd5b600060208284031215614d3857600080fd5b8135610b0381614d10565b60005b83811015614d5e578181015183820152602001614d46565b50506000910152565b60008151808452614d7f816020860160208601614d43565b601f01601f19169290920160200192915050565b602081526000610b036020830184614d67565b600060208284031215614db857600080fd5b5035919050565b6001600160a01b0381168114610c7f57600080fd5b60008060408385031215614de757600080fd5b8235614df281614dbf565b946020939093013593505050565b600080600060608486031215614e1557600080fd5b8335614e2081614dbf565b92506020840135614e3081614dbf565b929592945050506040919091013590565b60008060408385031215614e5457600080fd5b823591506020830135614e6681614dbf565b809150509250929050565b60a081016108cc828480518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b600060208284031215614ebc57600080fd5b8135610b0381614dbf565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614f0557614f05614ec7565b604052919050565b600082601f830112614f1e57600080fd5b81356001600160401b03811115614f3757614f37614ec7565b614f4a601f8201601f1916602001614edd565b818152846020838601011115614f5f57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215614f8e57600080fd5b81356001600160401b03811115614fa457600080fd5b611b9484828501614f0d565b80356001600160401b0381168114611ca757600080fd5b60008060408385031215614fda57600080fd5b614df283614fb0565b60006001600160401b03821115614ffc57614ffc614ec7565b5060051b60200190565b600082601f83011261501757600080fd5b8135602061502c61502783614fe3565b614edd565b82815260059290921b8401810191818101908684111561504b57600080fd5b8286015b84811015615066578035835291830191830161504f565b509695505050505050565b60008060006060848603121561508657600080fd5b833561509181614dbf565b925060208401356001600160401b03808211156150ad57600080fd5b6150b987838801615006565b935060408601359150808211156150cf57600080fd5b506150dc86828701615006565b9150509250925092565b60008060008060008060c087890312156150ff57600080fd5b86356001600160401b038082111561511657600080fd5b6151228a838b01615006565b9750602089013591508082111561513857600080fd5b6151448a838b01615006565b96506040890135915061515682614dbf565b90945060608801359061516882614dbf565b90935060808801359061517a82614dbf565b90925060a0880135908082111561519057600080fd5b5061519d89828a01614f0d565b9150509295509295509295565b600080600080608085870312156151c057600080fd5b5050823594602084013594506040840135936060013592509050565b600082601f8301126151ed57600080fd5b813560206151fd61502783614fe3565b82815260059290921b8401810191818101908684111561521c57600080fd5b8286015b84811015615066578035600881106152385760008081fd5b8352918301918301615220565b600082601f83011261525657600080fd5b8135602061526661502783614fe3565b82815260059290921b8401810191818101908684111561528557600080fd5b8286015b848110156150665780356001600160401b038111156152a85760008081fd5b6152b68986838b0101614f0d565b845250918301918301615289565b600080600080608085870312156152da57600080fd5b84356001600160401b03808211156152f157600080fd5b6152fd888389016151dc565b9550602087013591508082111561531357600080fd5b5061532087828801615245565b935050604085013561533181614dbf565b9150606085013561534181614dbf565b939692955090935050565b8015158114610c7f57600080fd5b6000806040838503121561536d57600080fd5b823561537881614dbf565b91506020830135614e668161534c565b6000806000806080858703121561539e57600080fd5b84356153a981614dbf565b935060208501356153b981614dbf565b92506040850135915060608501356001600160401b038111156153db57600080fd5b6153e787828801614f0d565b91505092959194509250565b60008060006060848603121561540857600080fd5b505081359360208301359350604090920135919050565b600080600080600085870361018081121561543957600080fd5b863561544481614dbf565b9550602087013561545481614dbf565b9450604087013561546481614dbf565b9350610100605f198201121561547957600080fd5b506060860191506101608601356001600160401b0381111561549a57600080fd5b6154a688828901614f0d565b9150509295509295909350565b60008060008060008060c087890312156154cc57600080fd5b86356001600160401b03808211156154e357600080fd5b6154ef8a838b016151dc565b9750602089013591508082111561550557600080fd5b6151448a838b01615245565b6000806040838503121561552457600080fd5b823561552f81614dbf565b91506020830135614e6681614dbf565b60ff81168114610c7f57600080fd5b600080600080600080600060e0888a03121561556957600080fd5b873561557481614dbf565b9650602088013561558481614dbf565b955060408801356155948161534c565b94506060880135935060808801356155ab8161553f565b9699959850939692959460a0840135945060c09093013592915050565b600080600080608085870312156155de57600080fd5b84356001600160401b03808211156155f557600080fd5b61560188838901615006565b9550602087013591508082111561561757600080fd5b5061532087828801615006565b600181811c9082168061563857607f821691505b60208210810361565857634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561567057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176108cc576108cc615677565b634e487b7160e01b600052601260045260246000fd5b6000826156c9576156c96156a4565b500490565b6000602082840312156156e057600080fd5b8151610b038161534c565b818103818111156108cc576108cc615677565b601f821115610a1857600081815260208120601f850160051c810160208610156157255750805b601f850160051c820191505b81811015611ce157828155600101615731565b81516001600160401b0381111561575d5761575d614ec7565b6157718161576b8454615624565b846156fe565b602080601f8311600181146157a6576000841561578e5750858301515b600019600386901b1c1916600185901b178555611ce1565b600085815260208120601f198616915b828110156157d5578886015182559484019460019091019084016157b6565b50858210156157f35787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160401b0381811683821601908082111561582357615823615677565b5092915050565b60006020828403121561583c57600080fd5b8151610b0381614dbf565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b8381101561588d57815187529582019590820190600101615871565b509495945050505050565b6040815260006158ab604083018561585d565b8281036020840152611fa2818561585d565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602081526000610b03602083018461585d565b808201808211156108cc576108cc615677565b928352602083019190915260ff16604082015260600190565b60006001820161594557615945615677565b5060010190565b600181815b8085111561598757816000190482111561596d5761596d615677565b8085161561597a57918102915b93841c9390800290615951565b509250929050565b60008261599e575060016108cc565b816159ab575060006108cc565b81600181146159c157600281146159cb576159e7565b60019150506108cc565b60ff8411156159dc576159dc615677565b50506001821b6108cc565b5060208310610133831016604e8410600b8410161715615a0a575081810a6108cc565b615a14838361594c565b8060001904821115615a2857615a28615677565b029392505050565b6000610b0360ff84168361598f565b600082615a4e57615a4e6156a4565b500690565b6000808454615a6181615624565b60018281168015615a795760018114615a8e57615abd565b60ff1984168752821515830287019450615abd565b8860005260208060002060005b85811015615ab45781548a820152908401908201615a9b565b50505082870194505b505050508351615ad1818360208801614d43565b01949350505050565b600060208284031215615aec57600080fd5b8151610b038161553f565b6e020b733b63290283937ba37b1b7b61608d1b815260008251615b2181600f850160208701614d43565b650815985d5b1d60d21b600f939091019283015250601501919050565b60008251615b50818460208701614d43565b650b5d985d5b1d60d21b920191825250600601919050565b600060208284031215615b7a57600080fd5b610b0382614fb0565b600060208284031215615b9557600080fd5b8135610b038161534c565b634e487b7160e01b600052602160045260246000fd5b60008060408385031215615bc957600080fd5b505080516020909101519092909150565b60008060008060008060c08789031215615bf357600080fd5b8651615bfe81614dbf565b6020880151604089015160608a015160808b015160a0909b0151939c929b509099909850965090945092505050565b60008060008060808587031215615c4357600080fd5b845193506020850151615c5581614dbf565b6040860151606090960151949790965092505050565b9283526001600160a01b03918216602084015216604082015260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b038781168252868116602083015285166040820152606081018490526080810183905260c060a082018190526000906143c990830184614d67565b828152604060208201526000611b946040830184614d67565b600060208284031215615d1057600080fd5b8151610b0381614d10565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615d4e90830184614d67565b9695505050505050565b60008251615d6a818460208701614d43565b919091019291505056fe70cf49afe7355562d5b022e594790f22b71ad8cc7eec902fa5feac7c67f71091722cb71fa87c947148cefc06dd890af5802a6a00207c5ddecf1191bf71ce3cd4ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220989782b92d84509719429db7a0d2acce087612fa7a3bed7609b857375005fdab64736f6c63430008110033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 27 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.