ETH Price: $2,373.94 (-1.42%)

Contract

0xCe43220f72A7060F34BC242630D6B96434105Ae4
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040162412232022-12-22 15:45:23633 days ago1671723923IN
 Create: VaultManagerListing
0 ETH0.113424821.47600286

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
VaultManagerListing

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 1 runs

Other Settings:
default evmVersion
File 1 of 35 : VaultManagerListing.sol
// 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);
    }
}

File 2 of 35 : IBorrowStaker.sol
// 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);
}

File 3 of 35 : IAgToken.sol
// 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);
}

File 4 of 35 : ICoreBorrow.sol
// 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);
}

File 5 of 35 : IFlashAngle.sol
// 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;
}

File 6 of 35 : IOracle.sol
// 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);
}

File 7 of 35 : ISwapper.sol
// 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;
}

File 8 of 35 : ITreasury.sol
// 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;
}

File 9 of 35 : IVaultManager.sol
// 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);
}

File 10 of 35 : IERC1271.sol
// 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);
}

File 11 of 35 : IVeBoostProxy.sol
// 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);
}

File 12 of 35 : VaultManager.sol
// 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 {}
}

File 13 of 35 : VaultManagerERC721.sol
// 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 {}
}

File 14 of 35 : VaultManagerPermit.sol
// 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;
}

File 15 of 35 : VaultManagerStorage.sol
// 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 {}
}

File 16 of 35 : AggregatorV3Interface.sol
// 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
    );

}

File 17 of 35 : IERC721Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/extensions/IERC721Metadata.sol";

File 18 of 35 : IERC20.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);
}

File 19 of 35 : IERC20Metadata.sol
// 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);
}

File 20 of 35 : draft-IERC20Permit.sol
// 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);
}

File 21 of 35 : SafeERC20.sol
// 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");
        }
    }
}

File 22 of 35 : IERC721.sol
// 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);
}

File 23 of 35 : IERC721Metadata.sol
// 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);
}

File 24 of 35 : Address.sol
// 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);
            }
        }
    }
}

File 25 of 35 : IERC165.sol
// 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);
}

File 26 of 35 : IERC721MetadataUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "../token/ERC721/extensions/IERC721MetadataUpgradeable.sol";

File 27 of 35 : Initializable.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);
        }
    }
}

File 28 of 35 : ReentrancyGuardUpgradeable.sol
// 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;
}

File 29 of 35 : IERC20Upgradeable.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 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);
}

File 30 of 35 : draft-IERC20PermitUpgradeable.sol
// 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);
}

File 31 of 35 : IERC721ReceiverUpgradeable.sol
// 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);
}

File 32 of 35 : IERC721Upgradeable.sol
// 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);
}

File 33 of 35 : IERC721MetadataUpgradeable.sol
// 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);
}

File 34 of 35 : AddressUpgradeable.sol
// 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);
            }
        }
    }
}

File 35 of 35 : IERC165Upgradeable.sol
// 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);
}

Settings
{
  "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

Contract ABI

[{"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"}]

60806040523480156200001157600080fd5b50600054610100900460ff1615808015620000335750600054600160ff909116105b8062000063575062000050306200013d60201b620032c81760201c565b15801562000063575060005460ff166001145b620000cb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b6000805460ff191660011790558015620000ef576000805461ff0019166101001790555b801562000136576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b506200014c565b6001600160a01b03163b151590565b615e0a806200015c6000396000f3fe608060405234801561001057600080fd5b50600436106103015760003560e01c8063010db1951461030657806301ffc9a71461032f57806306fdde0314610352578063081812fc14610367578063087a60071461037a578063095ea7b3146103915780630e198f22146103a657806313888565146103af57806323b872dd146103b8578063254cf439146103cb578063307439af146103fd57806334ce998a1461041d57806335836f15146104255780633644e5151461043857806339393ac91461044057806339eb4dc6146104535780633ae2325f146104675780633af32abf1461047a5780633c2e941b1461049a57806342842e0e146104a3578063430c2081146104b65780634f7e43df146104c957806355f804b3146104e35780635c975abb146104f657806361d027b31461050a5780636352211e1461051d57806370a082311461053057806374107543146105435780637aacfffa146105565780637adbf973146105925780637c0f59f4146105a55780637c3a00fd146105bf5780637dc0d1d0146105d25780637e53bd97146105e55780637e56d47c146105f85780637ecebe001461060b578063835986b41461063457806389050f1d1461064757806395d89b41146106595780639a3b6f2f146106615780639f48118f146106a7578063a22cb465146106b2578063af2c8c2e146106c5578063b1511cc9146106ce578063b4bd6f46146106e1578063b88d4fde146106f4578063bbcac55714610707578063bfc7ad2e14610710578063c13cacae14610719578063c4ae31681461072c578063c66d8b0114610734578063c87b56dd1461074e578063d8dfeb4514610761578063d9b1cb5b14610774578063de1f776514610787578063de8fc69814610799578063df011c41146107ac578063e182b883146107bf578063e1c84ea4146107d2578063e626648a146107db578063e985e9c5146107f5578063e9cbd82214610808578063f0f442601461081b578063f51cc7dd1461082e578063fad9aba314610841578063fc29b0211461084a578063fd527cf81461085d575b600080fd5b603754610319906001600160a01b031681565b6040516103269190614cfc565b60405180910390f35b61034261033d366004614d26565b610865565b6040519015158152602001610326565b61035a6108d2565b6040516103269190614d93565b610319610375366004614da6565b610960565b610383603f5481565b604051908152602001610326565b6103a461039f366004614dd4565b610991565b005b610383603e5481565b61038360415481565b6103a46103c6366004614e00565b610a1d565b603c546103e590600160401b90046001600160401b031681565b6040516001600160401b039091168152602001610326565b61041061040b366004614e41565b610a58565b6040516103269190614e71565b610383610b0a565b610383610433366004614da6565b610b3c565b610383610b78565b6103a461044e366004614eaa565b610b82565b603d5461034290600160c01b900460ff1681565b610383610475366004614da6565b610c82565b610383610488366004614eaa565b60446020526000908152604090205481565b61038360455481565b6103a46104b1366004614e00565b610ca3565b6103426104c4366004614dd4565b610cbe565b603d546103e590600160801b90046001600160401b031681565b6103a46104f1366004614f7c565b610cca565b603d5461034290600160c81b900460ff1681565b603354610319906001600160a01b031681565b61031961052b366004614da6565b610d68565b61038361053e366004614eaa565b610d73565b6103a4610551366004614fc7565b610db8565b61057d610564366004614da6565b6043602052600090815260409020805460019091015482565b60408051928352602083019190915201610326565b6103a46105a0366004614eaa565b61117d565b603c546103e590600160c01b90046001600160401b031681565b603d546103e5906001600160401b031681565b603654610319906001600160a01b031681565b6103a46105f3366004615071565b6112c4565b6104106106063660046150e6565b6113e9565b610383610619366004614eaa565b6001600160a01b03166000908152607f602052604090205490565b6103a46106423660046151aa565b6119a2565b610383676765c793fa10079d601a1b81565b61035a611b5a565b61067461066f3660046152c4565b611b67565b60405161032691908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b610383633b9aca0081565b6103a46106c036600461535a565b611b9c565b61038360405481565b6103a46106dc366004614da6565b611ba7565b6103836106ef366004614eaa565b611c70565b6103a4610702366004615388565b611cac565b61038360425481565b61038360b65481565b6103a46107273660046153f3565b611ce9565b6103a4611da6565b603d546103e590600160401b90046001600160401b031681565b61035a61075c366004614da6565b611e55565b603454610319906001600160a01b031681565b6103a461078236600461541f565b611fab565b610383676765c793fa10079d601b1b81565b6106746107a73660046154b3565b612547565b603c546103e5906001600160401b031681565b6103836107cd366004614da6565b612de2565b61038360395481565b603c546103e590600160801b90046001600160401b031681565b610342610803366004615511565b612df2565b603554610319906001600160a01b031681565b6103a4610829366004614eaa565b612e20565b6103a461083c36600461554e565b612ec0565b61038360b45481565b6104106108583660046155c8565b61317f565b61057d6131a9565b60006001600160e01b03198216635b5e139f60e01b148061089657506001600160e01b031982166380ac58cd60e01b145b806108b157506001600160e01b0319821663430c208160e01b145b806108cc57506001600160e01b031982166301ffc9a760e01b145b92915050565b607d80546108df90615624565b80601f016020809104026020016040519081016040528092919081815260200182805461090b90615624565b80156109585780601f1061092d57610100808354040283529160200191610958565b820191906000526020600020905b81548152906001019060200180831161093b57829003601f168201915b505050505081565b600061096b826132d7565b6109885760405163062a39dd60e11b815260040160405180910390fd5b6108cc826132f4565b600061099c8261330f565b9050806001600160a01b0316836001600160a01b0316036109d0576040516349fa8bc360e11b815260040160405180910390fd5b336001600160a01b038216148015906109f057506109ee8133612df2565b155b15610a0e5760405163c19f17a960e01b815260040160405180910390fd5b610a188383613345565b505050565b3381610a2982826133b3565b610a465760405163c19f17a960e01b815260040160405180910390fd5b610a51858585613431565b5050505050565b610a60614c45565b60008381526043602090815260409182902082518084018452815481526001909101548183015260365483516315f789a960e21b81529351610b0394929387936001600160a01b03909316926357de26a492600480830193928290030181865afa158015610ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af6919061565e565b610afe613563565b6136f8565b9392505050565b6000676765c793fa10079d601b1b610b20613563565b604054610b2d919061568d565b610b3791906156ba565b905090565b6000676765c793fa10079d601b1b610b52613563565b600084815260436020526040902060010154610b6e919061568d565b6108cc91906156ba565b6000610b37613a61565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b890610bb2903390600401614cfc565b602060405180830381865afa158015610bcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf391906156ce565b610c1057604051633b8d9d7560e21b815260040160405180910390fd5b6001600160a01b03811615610c5f576001600160a01b038116600090815260446020526040902054610c439060016156eb565b6001600160a01b03821660009081526044602052604090205550565b603d805460ff60c01b198116600160c01b9182900460ff16159091021790555b50565b603b8181548110610c9257600080fd5b600091825260209091200154905081565b610a1883838360405180602001604052806000815250611cac565b6000610b0383836133b3565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990610cfa903390600401614cfc565b602060405180830381865afa158015610d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3b91906156ce565b610d5857604051632678482f60e21b815260040160405180910390fd5b6046610d648282615744565b5050565b60006108cc8261330f565b60006001600160a01b038216610d9c5760405163d92e233d60e01b815260040160405180910390fd5b506001600160a01b031660009081526048602052604090205490565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990610de8903390600401614cfc565b602060405180830381865afa158015610e05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2991906156ce565b610e4657604051632678482f60e21b815260040160405180910390fd5b806121a360f11b03610ea957603d546001600160401b03600160401b90910481169083161115610e8957604051637650e96360e11b815260040160405180910390fd5b603c80546001600160401b0319166001600160401b038416179055611136565b80622a242360e91b03610f0d57633b9aca00826001600160401b03161015610ee45760405163da6a17b960e01b815260040160405180910390fd5b603c8054600160401b600160801b031916600160401b6001600160401b03851602179055611136565b8061212360f11b03610f7057633b9aca00826001600160401b03161115610f4757604051637650e96360e11b815260040160405180910390fd5b603c8054600160801b600160c01b031916600160801b6001600160401b03851602179055611136565b8061292360f11b03610fec57603d54633b9aca0090610f9f90600160401b90046001600160401b031684615803565b6001600160401b03161115610fc757604051637650e96360e11b815260040160405180910390fd5b603c80546001600160c01b0316600160c01b6001600160401b03851602179055611136565b806124a960f11b0361102157611000613acd565b50603d80546001600160401b0319166001600160401b038416179055611136565b80614c5360f01b036110b957603c546001600160401b03808416911611806110725750603c54633b9aca009061106790600160c01b90046001600160401b031684615803565b6001600160401b0316115b156110905760405163180d062b60e31b815260040160405180910390fd5b603d8054600160401b600160801b031916600160401b6001600160401b03851602179055611136565b806213531160ea1b0361111d57633b9aca00826001600160401b031611156110f457604051637650e96360e11b815260040160405180910390fd5b603d8054600160801b600160c01b031916600160801b6001600160401b03851602179055611136565b60405163e1daa9cf60e01b815260040160405180910390fd5b604080516001600160401b0384168152602081018390527f13b367dac93b85d1ed9b3d8961d8b48e1a677c9800bb1613b4b0416b2d5b61d091015b60405180910390a15050565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b8906111ad903390600401614cfc565b602060405180830381865afa1580156111ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ee91906156ce565b61120b57604051633b8d9d7560e21b815260040160405180910390fd5b603354604080516361d027b360e01b815290516001600160a01b03928316928416916361d027b39160048083019260209291908290030181865afa158015611257573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127b919061582a565b6001600160a01b0316146112a2576040516302979eb960e31b815260040160405180910390fd5b603680546001600160a01b0319166001600160a01b0392909216919091179055565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de9906112f4903390600401614cfc565b602060405180830381865afa158015611311573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133591906156ce565b61135257604051632678482f60e21b815260040160405180910390fd5b8060008151811061136557611365615847565b602002602001015160000361138d57604051631746545d60e11b815260040160405180910390fd5b80516113a090603b906020840190614c74565b50826001600160a01b03167feb74d4d9fea592587c926aeb35eb6a7893fb28db0c1c8eb2eb3c586e7164b76c83836040516113dc929190615898565b60405180910390a2505050565b6113f1614c45565b603d54600160c81b900460ff161561141c576040516313d0ff5960e31b815260040160405180910390fd5b6002600154036114475760405162461bcd60e51b815260040161143e906158bd565b60405180910390fd5b6002600155865186518114158061145c575080155b1561147a576040516346282e8d60e01b815260040160405180910390fd5b603660009054906101000a90046001600160a01b03166001600160a01b03166357de26a46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f1919061565e565b60608301526114fe613acd565b60808301526040517f965a177723c641ee49150b583a0b9ad4730bb20d3474e00ae5a65e777c00d67b90611533908a906158f4565b60405180910390a160005b81811015611912576000604360008b848151811061155e5761155e615847565b6020026020010151815260200190815260200160002060405180604001604052908160008201548152602001600182015481525050905060006115ab8233876060015188608001516136f8565b905080604001516000141580156115df575080604001518a84815181106115d4576115d4615847565b602002602001015110155b80611606575080600001518a84815181106115fc576115fc615847565b6020026020010151115b1561162f5780600001518a848151811061162257611622615847565b6020026020010181815250505b600085606001518260600151611645919061568d565b603854633b9aca008d878151811061165f5761165f615847565b6020026020010151611671919061568d565b61167b919061568d565b61168591906156ba565b90506116c08c858151811061169c5761169c615847565b602002602001015182856000015111156116b657826116b9565b84515b6000613b6e565b825181106117f6575081516020830151604080546000906116e29084906156eb565b92505081905550604360008d86815181106116ff576116ff615847565b60209081029190910181015182528101919091526040016000908120818155600101819055603d548c51633b9aca0091600160401b90046001600160401b0316908e908890811061175257611752615847565b6020026020010151611764919061568d565b61176e91906156ba565b905082608001518110611782576000611792565b80836080015161179291906156eb565b876040018181516117a39190615907565b905250508b51600080516020615d75833981519152908d90869081106117cb576117cb615847565b6020026020010151846020015160006040516117e99392919061591a565b60405180910390a16118b7565b80604360008e878151811061180d5761180d615847565b60200260200101518152602001908152602001600020600001600082825461183591906156eb565b925050819055506118b58c858151811061185157611851615847565b6020026020010151633b9aca00603d60089054906101000a90046001600160401b03166001600160401b03168e888151811061188f5761188f615847565b60200260200101516118a1919061568d565b6118ab91906156ba565b8860800151613b81565b505b80866020018181516118c99190615907565b9052508a518b90859081106118e0576118e0615847565b6020026020010151866000018181516118f99190615907565b90525061190b92508391506159339050565b905061153e565b50603d54633b9aca009061193690600160401b90046001600160401b0316826156eb565b8351611942919061568d565b61194c91906156ba565b6041600082825461195d9190615907565b9091555050604082015160428054600090611979908490615907565b909155505060208201518251611993919088888888613cb3565b50600180559695505050505050565b603d54600160c81b900460ff16156119cd576040516313d0ff5960e31b815260040160405180910390fd5b6033546040516333b52a9f60e11b81526001600160a01b039091169063676a553e906119fd903390600401614cfc565b602060405180830381865afa158015611a1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3e91906156ce565b611a5b5760405163027f480760e01b815260040160405180910390fd5b603c54600090600160c01b90046001600160401b0316821015611a9957603c54611a96908390600160c01b90046001600160401b03166156eb565b90505b603c54600090600160801b90046001600160401b0316841115611ad657603c54611ad390600160801b90046001600160401b0316856156eb565b90505b6000611ae76002633b9aca00615a30565b611af583633b9aca006156eb565b611b0385633b9aca006156eb565b611b0d908961568d565b611b17919061568d565b611b2191906156ba565b9050611b2d81876156eb565b60416000828254611b3e9190615907565b90915550611b50905087826000613b81565b5050505050505050565b607e80546108df90615624565b611b6f614cbf565b60408051600080825260208201909252611b9191879187918791879190612547565b90505b949350505050565b610d64338383613dbd565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990611bd7903390600401614cfc565b602060405180830381865afa158015611bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1891906156ce565b611c3557604051632678482f60e21b815260040160405180910390fd5b60398190556040518181527fdd63b3dcdbebad734892f7c7a26d0f647fbc7eec973e0775f5229018ac4ab47a9060200160405180910390a150565b603d54600090600160c81b900460ff1615611c9e576040516313d0ff5960e31b815260040160405180910390fd5b6108cc82613e72565b919050565b3382611cb882826133b3565b611cd55760405163c19f17a960e01b815260040160405180910390fd5b611ce186868686613fac565b505050505050565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b890611d19903390600401614cfc565b602060405180830381865afa158015611d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5a91906156ce565b611d7757604051633b8d9d7560e21b815260040160405180910390fd5b81831115611d985760405163180d062b60e31b815260040160405180910390fd5b60b49290925560b65560b555565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990611dd6903390600401614cfc565b602060405180830381865afa158015611df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1791906156ce565b611e3457604051632678482f60e21b815260040160405180910390fd5b603d805460ff60c81b198116600160c81b9182900460ff1615909102179055565b6060611e60826132d7565b611e7d5760405163062a39dd60e11b815260040160405180910390fd5b8160005b8115611ea457611e9081615933565b9050611e9d600a836156ba565b9150611e81565b6000816001600160401b03811115611ebe57611ebe614ec7565b6040519080825280601f01601f191660200182016040528015611ee8576020820181803683370190505b5090505b8415611f5357611efd6001836156eb565b9150611f0a600a86615a3f565b611f15906030615907565b60f81b818381518110611f2a57611f2a615847565b60200101906001600160f81b031916908160001a905350611f4c600a866156ba565b9450611eec565b60468054611f6090615624565b9050600003611f7e5760405180602001604052806000815250611fa2565b604681604051602001611f92929190615a53565b6040516020818303038152906040525b95945050505050565b600054610100900460ff1615808015611fcb5750600054600160ff909116105b80611fec5750611fda306132c8565b158015611fec575060005460ff166001145b61204f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161143e565b6000805460ff191660011790558015612072576000805461ff0019166101001790555b856001600160a01b0316846001600160a01b03166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120de919061582a565b6001600160a01b031614612105576040516302979eb960e31b815260040160405180910390fd5b603380546001600160a01b038089166001600160a01b0319928316179092556034805492881692909116821790556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801561216e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121929190615ada565b61219d90600a615a30565b603881905550856001600160a01b031663e9cbd8226040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612205919061582a565b603580546001600160a01b03199081166001600160a01b039384161790915560368054909116918616919091179055604051600090612248908490602001615af7565b60408051601f198184030181529190529050607d6122668282615744565b5061227081613fe6565b826040516020016122819190615b3e565b604051602081830303815290604052607e908161229e9190615744565b50676765c793fa10079d601b1b603f5542603e556122c260a0850160808601615b68565b6001600160401b03166122db6040860160208701615b68565b6001600160401b0316118061230b5750633b9aca0061230060a0860160808701615b68565b6001600160401b0316115b8061233157506123216060850160408601615b68565b6001600160401b0316633b9aca00115b806123585750633b9aca0061234c60c0860160a08701615b68565b6001600160401b031610155b80612365575060e0840135155b1561238357604051631746545d60e11b815260040160405180910390fd5b83356039556123986040850160208601615b68565b603c80546001600160401b0319166001600160401b03929092169190911790556123c86060850160408601615b68565b603c80546001600160401b0392909216600160401b02600160401b600160801b03199092169190911790556124036080850160608601615b68565b603d80546001600160401b0319166001600160401b039290921691909117905561243360a0850160808601615b68565b603d80546001600160401b0392909216600160401b02600160401b600160801b031990921691909117905561246e60c0850160a08601615b68565b603d80546001600160401b0392909216600160801b02600160801b600160c01b03199092169190911790556124a960e0850160c08601615b83565b603d8054911515600160c01b0260ff60c01b19909216919091179055604080516020810190915260e085013581526124e590603b906001614c74565b5050603d805460ff60c81b1916600160c81b1790558015611ce1576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050505050565b61254f614cbf565b603d54600160c81b900460ff161561257a576040516313d0ff5960e31b815260040160405180910390fd5b60026001540361259c5760405162461bcd60e51b815260040161143e906158bd565b6002600155855187511415806125b157508651155b156125cf576040516346282e8d60e01b815260040160405180910390fd5b6000806000806000805b8c51811015612b4b5760008d82815181106125f6576125f6615847565b602002602001015190506000600781111561261357612613615ba0565b81600781111561262557612625615ba0565b036126655761265f8d838151811061263f5761263f615847565b602002602001015180602001905181019061265a919061582a565b613e72565b50612b3a565b600281600781111561267957612679615ba0565b036126e2578c828151811061269057612690615847565b60200260200101518060200190518101906126ab9190615bb6565b9550925060008390036126be5760455492505b6126c883866140bc565b84886060018181516126da9190615907565b905250612b3a565b60078160078111156126f6576126f6615ba0565b036127ce5760008060008f858151811061271257612712615847565b602002602001015180602001905181019061272d9190615bda565b60345460405163d505accf60e01b81526001600160a01b038089166004830152306024830152604482018890526064820187905260ff8616608483015260a4820185905260c48201849052969f50939d50939b509497509550929350169063d505accf9060e401600060405180830381600087803b1580156127ae57600080fd5b505af11580156127c2573d6000803e3d6000fd5b50505050505050612b3a565b866000036127e1576127de613acd565b96505b60048160078111156127f5576127f5615ba0565b036128c4578c828151811061280c5761280c615847565b60200260200101518060200190518101906128279190615bb6565b94509250600083900361283a5760455492505b612845838589613b81565b603c5490945060009061286c90600160c01b90046001600160401b0316633b9aca006156eb565b61287a633b9aca008761568d565b61288491906156ba565b905061289085826156eb565b604160008282546128a19190615907565b9250508190555080896020018181516128ba9190615907565b905250612b3a9050565b8560000361294657603660009054906101000a90046001600160a01b03166001600160a01b03166357de26a46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561291f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612943919061565e565b95505b600181600781111561295a5761295a615ba0565b036129d8578c828151811061297157612971615847565b602002602001015180602001905181019061298c919061565e565b92508260000361299c5760455492505b6129a7838789614134565b809650819550505084886040018181516129c19190615907565b9052506020880180518591906126da908390615907565b60038160078111156129ec576129ec615ba0565b03612a4f578c8281518110612a0357612a03615847565b6020026020010151806020019051810190612a1e9190615bb6565b955092506000839003612a315760455492505b612a3d8386888a614254565b84886040018181516126da9190615907565b6005816007811115612a6357612a63615ba0565b03612ac8578c8281518110612a7a57612a7a615847565b6020026020010151806020019051810190612a959190615bb6565b945092506000839003612aa85760455492505b612ab48385888a61433a565b935083886000018181516126da9190615907565b6006816007811115612adc57612adc615ba0565b03612b3a576000808e8481518110612af657612af6615847565b6020026020010151806020019051810190612b119190615c2d565b9850919650925090506000859003612b295760455494505b612b37858383898c8e6143d5565b50505b50612b4481615933565b90506125d9565b508551602087015110612c425785516020870151600091612b6b916156eb565b90508660600151876040015110612ba257612b9d87606001518860400151612b9391906156eb565b828d8d8d8d613cb3565b612c3c565b8015612c0f57603554604051630d43af8160e21b81526001600160a01b039091169063350ebe0490612bdc9084908f903390600401615c6b565b600060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050505b612c3c333089604001518a60600151612c2891906156eb565b6034546001600160a01b0316929190614510565b50612dce565b60208601518651600091612c55916156eb565b6035546040516340c10f1960e01b81529192506001600160a01b0316906340c10f1990612c88908d908590600401615c8a565b600060405180830381600087803b158015612ca257600080fd5b505af1158015612cb6573d6000803e3d6000fd5b50505050866060015187604001511115612cfa57612cf58a88606001518960400151612ce291906156eb565b6034546001600160a01b0316919061457b565b612dcc565b600087604001518860600151612d1091906156eb565b90508015612dca57885115612db257896001600160a01b031663a5d4096b603560009054906101000a90046001600160a01b0316603460009054906101000a90046001600160a01b03163385878f6040518763ffffffff1660e01b8152600401612d7f96959493929190615ca3565b600060405180830381600087803b158015612d9957600080fd5b505af1158015612dad573d6000803e3d6000fd5b505050505b603454612dca906001600160a01b0316333084614510565b505b505b505060018055509198975050505050505050565b603a8181548110610c9257600080fd5b6001600160a01b039182166000908152604a6020908152604080832093909416825291909152205460011490565b6033546001600160a01b03163314612e4b5760405163b90cdbb160e01b815260040160405180910390fd5b603380546001600160a01b0319166001600160a01b0383811691909117909155603654604051630787a21360e51b815291169063f0f4426090612e92908490600401614cfc565b600060405180830381600087803b158015612eac57600080fd5b505af1158015610a51573d6000803e3d6000fd5b83421115612ee15760405163f87d927160e01b815260040160405180910390fd5b6fa2a8918ca85bafe22016d0b997e4df60600160ff1b03811180612f1857508260ff16601b14158015612f1857508260ff16601c14155b15612f3657604051638baa579f60e01b815260040160405180910390fd5b6000612f40613a61565b608254898989612f4f8d61459a565b6040805160208101969096526001600160a01b03948516908601529290911660608401521515608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001612fc692919061190160f01b81526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050612ff0886001600160a01b03166132c8565b156130cc57604080516020810185905280820184905260f886901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b038a1691631626ba7e91613056918591606501615ce5565b602060405180830381865afa158015613073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130979190615cfe565b6001600160e01b031916631626ba7e60e01b146130c757604051638baa579f60e01b815260040160405180910390fd5b613174565b6040805160008082526020820180845284905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613120573d6000803e3d6000fd5b505050602060405103519050886001600160a01b0316816001600160a01b031614158061315457506001600160a01b038116155b1561317257604051638baa579f60e01b815260040160405180910390fd5b505b611b50888888613dbd565b613187614c45565b60408051600080825260208201909252611b91918791879187918791906113e9565b60335460009081906001600160a01b031633146131d95760405163b90cdbb160e01b815260040160405180910390fd5b6131e1613acd565b50506041805460428054600093849055929055915080821061327a5761320781836156eb565b6035546033546040516340c10f1960e01b8152929450600093506001600160a01b03918216926340c10f19926132439216908690600401615c8a565b600060405180830381600087803b15801561325d57600080fd5b505af1158015613271573d6000803e3d6000fd5b5050505061328b565b61328482826156eb565b9050600091505b60408051838152602081018390527ffeb12225c131aab793a00c5239afb778932d170fa28ce6e9d23703e4bd892121910160405180910390a19091565b6001600160a01b03163b151590565b6000908152604760205260409020546001600160a01b0316151590565b6000908152604960205260409020546001600160a01b031690565b6000818152604760205260409020546001600160a01b031680611ca75760405163062a39dd60e11b815260040160405180910390fd5b600081815260496020526040902080546001600160a01b0319166001600160a01b038416908117909155819061337a8261330f565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000806133bf8361330f565b9050806001600160a01b0316846001600160a01b031614806133fa5750836001600160a01b03166133ef846132f4565b6001600160a01b0316145b80611b9457506001600160a01b038082166000908152604a6020908152604080832093881683529290522054600114949350505050565b826001600160a01b03166134448261330f565b6001600160a01b03161461346b5760405163c19f17a960e01b815260040160405180910390fd5b6001600160a01b0382166134925760405163d92e233d60e01b815260040160405180910390fd5b603d54600160c01b900460ff1680156134c457506001600160a01b038216600090815260446020526040902054600114155b156134e257604051630b094f2760e31b815260040160405180910390fd5b6134ed8383836145de565b6134f8600082613345565b6001600160a01b038084166000818152604860209081526040808320805460001901905593861680835284832080546001019055858352604790915283822080546001600160a01b03191682179055925184939291600080516020615db583398151915291a4505050565b600080603e544261357491906156eb565b603d549091506001600160401b031681158061358e575080155b1561359d57603f549250505090565b60006135aa6001846156eb565b90506000600284116135bd5760006135c8565b6135c86002856156eb565b90506000676765c793fa10079d601b1b676765c793fa10079d601a1b6135ee868061568d565b6135f89190615907565b61360291906156ba565b90506000676765c793fa10079d601b1b676765c793fa10079d601a1b613628878561568d565b6136329190615907565b61363c91906156ba565b9050600060028361364d878a61568d565b613657919061568d565b61366191906156ba565b9050600060068386613673898c61568d565b61367d919061568d565b613687919061568d565b61369191906156ba565b9050676765c793fa10079d601b1b81836136ab8b8b61568d565b6136c090676765c793fa10079d601b1b615907565b6136ca9190615907565b6136d49190615907565b603f546136e1919061568d565b6136eb91906156ba565b9850505050505050505090565b613700614c45565b6000806000613710888787614625565b925092509250633b9aca00831061373a576040516315fe9b6160e21b815260040160405180910390fd5b6000633b9aca0061374b85826156eb565b6137548a6146b1565b61375e919061568d565b61376891906156ba565b603d54909150600160801b90046001600160401b03168110156137985761379381633b9aca006156eb565b6137b9565b603d546137b990600160801b90046001600160401b0316633b9aca006156eb565b603d54909150600160401b90046001600160401b03166000806137e16002633b9aca00615a30565b603c546137f791906001600160401b031661568d565b83613802868a61568d565b61380c919061568d565b1061399157603c546001600160401b031661382c6002633b9aca00615a30565b613836919061568d565b603c54859061385590600160401b90046001600160401b03168661568d565b61385f919061568d565b61386991906156eb565b603c548590633b9aca0090613887906001600160401b03168961568d565b603c546138a5908b90600160401b90046001600160401b031661568d565b6138af91906156eb565b6138b9919061568d565b6138c3919061568d565b6138cd91906156ba565b60b6549092506138e1633b9aca008261568d565b6138eb858561568d565b6138f59190615907565b613903633b9aca008961568d565b1161398b5761391d676765c793fa10079d601b1b8561568d565b633b9aca008b8f60200151613932919061568d565b61393c919061568d565b61394691906156ba565b613951906001615907565b9250808711156139865783633b9aca0061396b838a6156eb565b613975919061568d565b61397f91906156ba565b915061398b565b600191505b50613a0f565b6038546139a290633b9aca0061568d565b8c518b906139b190879061568d565b6139bb919061568d565b6139c591906156ba565b6139d0906001615907565b915060b554851115613a0b57633b9aca008460b554876139f091906156eb565b6139fa919061568d565b613a0491906156ba565b9050613a0f565b5060015b818852613a1c848b61568d565b603854613a2d633b9aca008561568d565b613a37919061568d565b613a4191906156ba565b602089015260408801525050606085015250608083015250949350505050565b60808054608154604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f602082015290810192909252606082015246918101919091523060a082015260009060c00160405160208183030381529060405280519060200120905090565b6000613ad7613563565b90506000676765c793fa10079d601b1b603f5483613af591906156eb565b604054613b02919061568d565b613b0c91906156ba565b90508060416000828254613b209190615907565b9091555050603f82905542603e8190556040805184815260208101929092527fd1fa8ba00a3bf20274346919dce0de62d2a140af2c71fe7e29fa6472eea3bb9d910160405180910390a15090565b610a18613b7a8461330f565b83836146d9565b600081600003613b9657613b93613acd565b91505b60008481526043602052604081206001015490676765c793fa10079d601b1b613bbf858461568d565b613bc991906156ba565b9050808510613bda57935080613bfd565b83613bf0676765c793fa10079d601b1b8761568d565b613bfa91906156ba565b90505b613c0781836156eb565b91508060406000828254613c1b91906156eb565b90915550508115801590613c505750676765c793fa10079d601b1b60b454613c43919061568d565b613c4d858461568d565b11155b15613c6e5760405163228af07f60e21b815260040160405180910390fd5b60008681526043602052604080822060010184905551600080516020615d7583398151915291613ca1918991859161591a565b60405180910390a15092949350505050565b8515613cd057603454613cd0906001600160a01b0316848861457b565b8415611ce157805115613d4f5760345460355460405163a5d4096b60e01b81526001600160a01b038086169363a5d4096b93613d1c9391831692169089908b908d908990600401615ca3565b600060405180830381600087803b158015613d3657600080fd5b505af1158015613d4a573d6000803e3d6000fd5b505050505b603554604051630d43af8160e21b81526001600160a01b039091169063350ebe0490613d8390889088903390600401615c6b565b600060405180830381600087803b158015613d9d57600080fd5b505af1158015613db1573d6000803e3d6000fd5b50505050505050505050565b826001600160a01b0316826001600160a01b031603613def576040516320c5195360e21b815260040160405180910390fd5b600081613dfd576000613e00565b60015b6001600160a01b038581166000818152604a602090815260408083209489168084529482529182902060ff959095169485905590518615158152939450919290917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b603d54600090600160c01b900460ff168015613ec157506001600160a01b0382166000908152604460205260409020546001141580613ec1575033600090815260446020526040902054600114155b15613edf57604051630b094f2760e31b815260040160405180910390fd5b6001600160a01b038216613f065760405163d92e233d60e01b815260040160405180910390fd5b506045805460010190819055613f1e600083836145de565b6001600160a01b038216600081815260486020908152604080832080546001019055848352604790915280822080546001600160a01b031916841790555183929190600080516020615db5833981519152908290a4613f8f600083836040518060200160405280600081525061474c565b611ca7576040516320149b4360e21b815260040160405180910390fd5b613fb7848484613431565b613fc38484848461474c565b613fe0576040516320149b4360e21b815260040160405180910390fd5b50505050565b600054610100900460ff166140515760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161143e565b7f3f43a9c6bafb5c7aab4e0cfe239dc5d4c15caf0381c6104188191f78a6640bd860825580516020918201206080556040805180820190915260018152603160f81b9101527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6608155565b6140c5826132d7565b6140e25760405163062a39dd60e11b815260040160405180910390fd5b6140ee82826001613b6e565b6000828152604360205260408120805483929061410c908490615907565b9091555050604051600080516020615d9583398151915290611171908490849060019061591a565b600080338561414382826133b3565b6141605760405163c19f17a960e01b815260040160405180910390fd5b600087815260436020908152604080832081518083019092528054825260010154918101919091529080614195838a8a614625565b5091509150633b9aca0082116141be57604051631527804d60e31b815260040160405180910390fd5b8260200151604060008282546141d491906156eb565b909155506141e390508a614852565b603c5460009061420790600160c01b90046001600160401b0316633b9aca006156eb565b614215633b9aca008461568d565b61421f91906156ba565b905061422b82826156eb565b6041600082825461423c9190615907565b90915550509251929a92995091975050505050505050565b338461426082826133b3565b61427d5760405163c19f17a960e01b815260040160405180910390fd5b61428986866000613b6e565b600086815260436020526040812080548792906142a79084906156eb565b9091555050600086815260436020908152604080832081518083019092528054825260010154918101919091526142df908686614625565b50509050633b9aca00811161430757604051631527804d60e31b815260040160405180910390fd5b600080516020615d95833981519152878760006040516143299392919061591a565b60405180910390a150505050505050565b6000338561434882826133b3565b6143655760405163c19f17a960e01b815260040160405180910390fd5b614371878787876148dd565b603c54909650600090633b9aca009061439b908990600160801b90046001600160401b031661568d565b6143a591906156ba565b905080604160008282546143b99190615907565b909155506143c9905081886156eb565b98975050505050505050565b33866143e182826133b3565b6143fe5760405163c19f17a960e01b815260040160405180910390fd5b60408051898152602081018890526001600160a01b038916818301526060810187905290517fddd3b70af631334f7552aadb582ed091018e62e103fa8b150ca66cc700d4dac69181900360800190a1614459888686866148dd565b9450306001600160a01b0388160361447c57614476868685613b81565b50611b50565b603c546040516320d661ad60e21b815260048101889052602481018790526001600160401b03600160801b830481166044830152600160c01b90920490911660648201526001600160a01b0388169063835986b490608401600060405180830381600087803b1580156144ee57600080fd5b505af1158015614502573d6000803e3d6000fd5b505050505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052613fe09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614a71565b610a188363a9059cbb60e01b8484604051602401614544929190615c8a565b6001600160a01b0381166000908152607f60205260409020546145be816001615907565b6001600160a01b039092166000908152607f602052604090209190915590565b6001600160a01b03831615610a18576000818152604360205260408120549061460a90859083906146d9565b6001600160a01b03831615613fe057613fe0838260016146d9565b6000806000676765c793fa10079d601b1b848760200151614646919061568d565b61465091906156ba565b9150603854858760000151614665919061568d565b61466f91906156ba565b9050816000036146835760001992506146a8565b603c54829061469b906001600160401b03168361568d565b6146a591906156ba565b92505b93509350939050565b6000603b6000815481106146c7576146c7615847565b90600052602060002001549050919050565b6034546040516301627e4560e01b81526001600160a01b038581166004830152602482018590528315156044830152909116906301627e4590606401600060405180830381600087803b15801561472f57600080fd5b505af1158015614743573d6000803e3d6000fd5b50505050505050565b6000614760846001600160a01b03166132c8565b1561484a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614797903390899088908890600401615d1b565b6020604051808303816000875af19250505080156147d2575060408051601f3d908101601f191682019092526147cf91810190615cfe565b60015b614830573d808015614800576040519150601f19603f3d011682016040523d82523d6000602084013e614805565b606091505b508051600003614828576040516320149b4360e21b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611b94565b506001611b94565b600061485d8261330f565b905061486b816000846145de565b614876600083613345565b6001600160a01b038116600081815260486020908152604080832080546000190190558583526047825280832080546001600160a01b0319169055604390915280822082815560010182905551849290600080516020615db5833981519152908390a45050565b600080826148f6676765c793fa10079d601b1b8761568d565b61490091906156ba565b6000878152604360205260408120600101549192500361493c5760b454851161493c5760405163228af07f60e21b815260040160405180910390fd5b6000868152604360205260408120600101805483929061495d908490615907565b9250508190555080604060008282546149769190615907565b909155505060395461499490676765c793fa10079d601b1b9061568d565b836040546149a2919061568d565b11156149c1576040516371239a6160e11b815260040160405180910390fd5b600086815260436020908152604080832081518083019092528054825260010154918101919091526149f4908686614625565b50509050633b9aca008111614a1c57604051631527804d60e31b815260040160405180910390fd5b600080516020615d7583398151915287836001604051614a3e9392919061591a565b60405180910390a1676765c793fa10079d601b1b614a5c858461568d565b614a6691906156ba565b979650505050505050565b6000614ac6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b439092919063ffffffff16565b805190915015610a185780806020019051810190614ae491906156ce565b610a185760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161143e565b6060611b94848460008585614b57856132c8565b614ba35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161143e565b600080866001600160a01b03168587604051614bbf9190615d58565b60006040518083038185875af1925050503d8060008114614bfc576040519150601f19603f3d011682016040523d82523d6000602084013e614c01565b606091505b5091509150614a6682828660608315614c1b575081610b03565b825115614c2b5782518084602001fd5b8160405162461bcd60e51b815260040161143e9190614d93565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215614caf579160200282015b82811115614caf578251825591602001919060010190614c94565b50614cbb929150614ce7565b5090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b5b80821115614cbb5760008155600101614ce8565b6001600160a01b0391909116815260200190565b6001600160e01b031981168114610c7f57600080fd5b600060208284031215614d3857600080fd5b8135610b0381614d10565b60005b83811015614d5e578181015183820152602001614d46565b50506000910152565b60008151808452614d7f816020860160208601614d43565b601f01601f19169290920160200192915050565b602081526000610b036020830184614d67565b600060208284031215614db857600080fd5b5035919050565b6001600160a01b0381168114610c7f57600080fd5b60008060408385031215614de757600080fd5b8235614df281614dbf565b946020939093013593505050565b600080600060608486031215614e1557600080fd5b8335614e2081614dbf565b92506020840135614e3081614dbf565b929592945050506040919091013590565b60008060408385031215614e5457600080fd5b823591506020830135614e6681614dbf565b809150509250929050565b60a081016108cc828480518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b600060208284031215614ebc57600080fd5b8135610b0381614dbf565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614f0557614f05614ec7565b604052919050565b600082601f830112614f1e57600080fd5b81356001600160401b03811115614f3757614f37614ec7565b614f4a601f8201601f1916602001614edd565b818152846020838601011115614f5f57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215614f8e57600080fd5b81356001600160401b03811115614fa457600080fd5b611b9484828501614f0d565b80356001600160401b0381168114611ca757600080fd5b60008060408385031215614fda57600080fd5b614df283614fb0565b60006001600160401b03821115614ffc57614ffc614ec7565b5060051b60200190565b600082601f83011261501757600080fd5b8135602061502c61502783614fe3565b614edd565b82815260059290921b8401810191818101908684111561504b57600080fd5b8286015b84811015615066578035835291830191830161504f565b509695505050505050565b60008060006060848603121561508657600080fd5b833561509181614dbf565b925060208401356001600160401b03808211156150ad57600080fd5b6150b987838801615006565b935060408601359150808211156150cf57600080fd5b506150dc86828701615006565b9150509250925092565b60008060008060008060c087890312156150ff57600080fd5b86356001600160401b038082111561511657600080fd5b6151228a838b01615006565b9750602089013591508082111561513857600080fd5b6151448a838b01615006565b96506040890135915061515682614dbf565b90945060608801359061516882614dbf565b90935060808801359061517a82614dbf565b90925060a0880135908082111561519057600080fd5b5061519d89828a01614f0d565b9150509295509295509295565b600080600080608085870312156151c057600080fd5b5050823594602084013594506040840135936060013592509050565b600082601f8301126151ed57600080fd5b813560206151fd61502783614fe3565b82815260059290921b8401810191818101908684111561521c57600080fd5b8286015b84811015615066578035600881106152385760008081fd5b8352918301918301615220565b600082601f83011261525657600080fd5b8135602061526661502783614fe3565b82815260059290921b8401810191818101908684111561528557600080fd5b8286015b848110156150665780356001600160401b038111156152a85760008081fd5b6152b68986838b0101614f0d565b845250918301918301615289565b600080600080608085870312156152da57600080fd5b84356001600160401b03808211156152f157600080fd5b6152fd888389016151dc565b9550602087013591508082111561531357600080fd5b5061532087828801615245565b935050604085013561533181614dbf565b9150606085013561534181614dbf565b939692955090935050565b8015158114610c7f57600080fd5b6000806040838503121561536d57600080fd5b823561537881614dbf565b91506020830135614e668161534c565b6000806000806080858703121561539e57600080fd5b84356153a981614dbf565b935060208501356153b981614dbf565b92506040850135915060608501356001600160401b038111156153db57600080fd5b6153e787828801614f0d565b91505092959194509250565b60008060006060848603121561540857600080fd5b505081359360208301359350604090920135919050565b600080600080600085870361018081121561543957600080fd5b863561544481614dbf565b9550602087013561545481614dbf565b9450604087013561546481614dbf565b9350610100605f198201121561547957600080fd5b506060860191506101608601356001600160401b0381111561549a57600080fd5b6154a688828901614f0d565b9150509295509295909350565b60008060008060008060c087890312156154cc57600080fd5b86356001600160401b03808211156154e357600080fd5b6154ef8a838b016151dc565b9750602089013591508082111561550557600080fd5b6151448a838b01615245565b6000806040838503121561552457600080fd5b823561552f81614dbf565b91506020830135614e6681614dbf565b60ff81168114610c7f57600080fd5b600080600080600080600060e0888a03121561556957600080fd5b873561557481614dbf565b9650602088013561558481614dbf565b955060408801356155948161534c565b94506060880135935060808801356155ab8161553f565b9699959850939692959460a0840135945060c09093013592915050565b600080600080608085870312156155de57600080fd5b84356001600160401b03808211156155f557600080fd5b61560188838901615006565b9550602087013591508082111561561757600080fd5b5061532087828801615006565b600181811c9082168061563857607f821691505b60208210810361565857634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561567057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176108cc576108cc615677565b634e487b7160e01b600052601260045260246000fd5b6000826156c9576156c96156a4565b500490565b6000602082840312156156e057600080fd5b8151610b038161534c565b818103818111156108cc576108cc615677565b601f821115610a1857600081815260208120601f850160051c810160208610156157255750805b601f850160051c820191505b81811015611ce157828155600101615731565b81516001600160401b0381111561575d5761575d614ec7565b6157718161576b8454615624565b846156fe565b602080601f8311600181146157a6576000841561578e5750858301515b600019600386901b1c1916600185901b178555611ce1565b600085815260208120601f198616915b828110156157d5578886015182559484019460019091019084016157b6565b50858210156157f35787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160401b0381811683821601908082111561582357615823615677565b5092915050565b60006020828403121561583c57600080fd5b8151610b0381614dbf565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b8381101561588d57815187529582019590820190600101615871565b509495945050505050565b6040815260006158ab604083018561585d565b8281036020840152611fa2818561585d565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602081526000610b03602083018461585d565b808201808211156108cc576108cc615677565b928352602083019190915260ff16604082015260600190565b60006001820161594557615945615677565b5060010190565b600181815b8085111561598757816000190482111561596d5761596d615677565b8085161561597a57918102915b93841c9390800290615951565b509250929050565b60008261599e575060016108cc565b816159ab575060006108cc565b81600181146159c157600281146159cb576159e7565b60019150506108cc565b60ff8411156159dc576159dc615677565b50506001821b6108cc565b5060208310610133831016604e8410600b8410161715615a0a575081810a6108cc565b615a14838361594c565b8060001904821115615a2857615a28615677565b029392505050565b6000610b0360ff84168361598f565b600082615a4e57615a4e6156a4565b500690565b6000808454615a6181615624565b60018281168015615a795760018114615a8e57615abd565b60ff1984168752821515830287019450615abd565b8860005260208060002060005b85811015615ab45781548a820152908401908201615a9b565b50505082870194505b505050508351615ad1818360208801614d43565b01949350505050565b600060208284031215615aec57600080fd5b8151610b038161553f565b6e020b733b63290283937ba37b1b7b61608d1b815260008251615b2181600f850160208701614d43565b650815985d5b1d60d21b600f939091019283015250601501919050565b60008251615b50818460208701614d43565b650b5d985d5b1d60d21b920191825250600601919050565b600060208284031215615b7a57600080fd5b610b0382614fb0565b600060208284031215615b9557600080fd5b8135610b038161534c565b634e487b7160e01b600052602160045260246000fd5b60008060408385031215615bc957600080fd5b505080516020909101519092909150565b60008060008060008060c08789031215615bf357600080fd5b8651615bfe81614dbf565b6020880151604089015160608a015160808b015160a0909b0151939c929b509099909850965090945092505050565b60008060008060808587031215615c4357600080fd5b845193506020850151615c5581614dbf565b6040860151606090960151949790965092505050565b9283526001600160a01b03918216602084015216604082015260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b038781168252868116602083015285166040820152606081018490526080810183905260c060a082018190526000906143c990830184614d67565b828152604060208201526000611b946040830184614d67565b600060208284031215615d1057600080fd5b8151610b0381614d10565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615d4e90830184614d67565b9695505050505050565b60008251615d6a818460208701614d43565b919091019291505056fe70cf49afe7355562d5b022e594790f22b71ad8cc7eec902fa5feac7c67f71091722cb71fa87c947148cefc06dd890af5802a6a00207c5ddecf1191bf71ce3cd4ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220989782b92d84509719429db7a0d2acce087612fa7a3bed7609b857375005fdab64736f6c63430008110033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106103015760003560e01c8063010db1951461030657806301ffc9a71461032f57806306fdde0314610352578063081812fc14610367578063087a60071461037a578063095ea7b3146103915780630e198f22146103a657806313888565146103af57806323b872dd146103b8578063254cf439146103cb578063307439af146103fd57806334ce998a1461041d57806335836f15146104255780633644e5151461043857806339393ac91461044057806339eb4dc6146104535780633ae2325f146104675780633af32abf1461047a5780633c2e941b1461049a57806342842e0e146104a3578063430c2081146104b65780634f7e43df146104c957806355f804b3146104e35780635c975abb146104f657806361d027b31461050a5780636352211e1461051d57806370a082311461053057806374107543146105435780637aacfffa146105565780637adbf973146105925780637c0f59f4146105a55780637c3a00fd146105bf5780637dc0d1d0146105d25780637e53bd97146105e55780637e56d47c146105f85780637ecebe001461060b578063835986b41461063457806389050f1d1461064757806395d89b41146106595780639a3b6f2f146106615780639f48118f146106a7578063a22cb465146106b2578063af2c8c2e146106c5578063b1511cc9146106ce578063b4bd6f46146106e1578063b88d4fde146106f4578063bbcac55714610707578063bfc7ad2e14610710578063c13cacae14610719578063c4ae31681461072c578063c66d8b0114610734578063c87b56dd1461074e578063d8dfeb4514610761578063d9b1cb5b14610774578063de1f776514610787578063de8fc69814610799578063df011c41146107ac578063e182b883146107bf578063e1c84ea4146107d2578063e626648a146107db578063e985e9c5146107f5578063e9cbd82214610808578063f0f442601461081b578063f51cc7dd1461082e578063fad9aba314610841578063fc29b0211461084a578063fd527cf81461085d575b600080fd5b603754610319906001600160a01b031681565b6040516103269190614cfc565b60405180910390f35b61034261033d366004614d26565b610865565b6040519015158152602001610326565b61035a6108d2565b6040516103269190614d93565b610319610375366004614da6565b610960565b610383603f5481565b604051908152602001610326565b6103a461039f366004614dd4565b610991565b005b610383603e5481565b61038360415481565b6103a46103c6366004614e00565b610a1d565b603c546103e590600160401b90046001600160401b031681565b6040516001600160401b039091168152602001610326565b61041061040b366004614e41565b610a58565b6040516103269190614e71565b610383610b0a565b610383610433366004614da6565b610b3c565b610383610b78565b6103a461044e366004614eaa565b610b82565b603d5461034290600160c01b900460ff1681565b610383610475366004614da6565b610c82565b610383610488366004614eaa565b60446020526000908152604090205481565b61038360455481565b6103a46104b1366004614e00565b610ca3565b6103426104c4366004614dd4565b610cbe565b603d546103e590600160801b90046001600160401b031681565b6103a46104f1366004614f7c565b610cca565b603d5461034290600160c81b900460ff1681565b603354610319906001600160a01b031681565b61031961052b366004614da6565b610d68565b61038361053e366004614eaa565b610d73565b6103a4610551366004614fc7565b610db8565b61057d610564366004614da6565b6043602052600090815260409020805460019091015482565b60408051928352602083019190915201610326565b6103a46105a0366004614eaa565b61117d565b603c546103e590600160c01b90046001600160401b031681565b603d546103e5906001600160401b031681565b603654610319906001600160a01b031681565b6103a46105f3366004615071565b6112c4565b6104106106063660046150e6565b6113e9565b610383610619366004614eaa565b6001600160a01b03166000908152607f602052604090205490565b6103a46106423660046151aa565b6119a2565b610383676765c793fa10079d601a1b81565b61035a611b5a565b61067461066f3660046152c4565b611b67565b60405161032691908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b610383633b9aca0081565b6103a46106c036600461535a565b611b9c565b61038360405481565b6103a46106dc366004614da6565b611ba7565b6103836106ef366004614eaa565b611c70565b6103a4610702366004615388565b611cac565b61038360425481565b61038360b65481565b6103a46107273660046153f3565b611ce9565b6103a4611da6565b603d546103e590600160401b90046001600160401b031681565b61035a61075c366004614da6565b611e55565b603454610319906001600160a01b031681565b6103a461078236600461541f565b611fab565b610383676765c793fa10079d601b1b81565b6106746107a73660046154b3565b612547565b603c546103e5906001600160401b031681565b6103836107cd366004614da6565b612de2565b61038360395481565b603c546103e590600160801b90046001600160401b031681565b610342610803366004615511565b612df2565b603554610319906001600160a01b031681565b6103a4610829366004614eaa565b612e20565b6103a461083c36600461554e565b612ec0565b61038360b45481565b6104106108583660046155c8565b61317f565b61057d6131a9565b60006001600160e01b03198216635b5e139f60e01b148061089657506001600160e01b031982166380ac58cd60e01b145b806108b157506001600160e01b0319821663430c208160e01b145b806108cc57506001600160e01b031982166301ffc9a760e01b145b92915050565b607d80546108df90615624565b80601f016020809104026020016040519081016040528092919081815260200182805461090b90615624565b80156109585780601f1061092d57610100808354040283529160200191610958565b820191906000526020600020905b81548152906001019060200180831161093b57829003601f168201915b505050505081565b600061096b826132d7565b6109885760405163062a39dd60e11b815260040160405180910390fd5b6108cc826132f4565b600061099c8261330f565b9050806001600160a01b0316836001600160a01b0316036109d0576040516349fa8bc360e11b815260040160405180910390fd5b336001600160a01b038216148015906109f057506109ee8133612df2565b155b15610a0e5760405163c19f17a960e01b815260040160405180910390fd5b610a188383613345565b505050565b3381610a2982826133b3565b610a465760405163c19f17a960e01b815260040160405180910390fd5b610a51858585613431565b5050505050565b610a60614c45565b60008381526043602090815260409182902082518084018452815481526001909101548183015260365483516315f789a960e21b81529351610b0394929387936001600160a01b03909316926357de26a492600480830193928290030181865afa158015610ad2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610af6919061565e565b610afe613563565b6136f8565b9392505050565b6000676765c793fa10079d601b1b610b20613563565b604054610b2d919061568d565b610b3791906156ba565b905090565b6000676765c793fa10079d601b1b610b52613563565b600084815260436020526040902060010154610b6e919061568d565b6108cc91906156ba565b6000610b37613a61565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b890610bb2903390600401614cfc565b602060405180830381865afa158015610bcf573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bf391906156ce565b610c1057604051633b8d9d7560e21b815260040160405180910390fd5b6001600160a01b03811615610c5f576001600160a01b038116600090815260446020526040902054610c439060016156eb565b6001600160a01b03821660009081526044602052604090205550565b603d805460ff60c01b198116600160c01b9182900460ff16159091021790555b50565b603b8181548110610c9257600080fd5b600091825260209091200154905081565b610a1883838360405180602001604052806000815250611cac565b6000610b0383836133b3565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990610cfa903390600401614cfc565b602060405180830381865afa158015610d17573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d3b91906156ce565b610d5857604051632678482f60e21b815260040160405180910390fd5b6046610d648282615744565b5050565b60006108cc8261330f565b60006001600160a01b038216610d9c5760405163d92e233d60e01b815260040160405180910390fd5b506001600160a01b031660009081526048602052604090205490565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990610de8903390600401614cfc565b602060405180830381865afa158015610e05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e2991906156ce565b610e4657604051632678482f60e21b815260040160405180910390fd5b806121a360f11b03610ea957603d546001600160401b03600160401b90910481169083161115610e8957604051637650e96360e11b815260040160405180910390fd5b603c80546001600160401b0319166001600160401b038416179055611136565b80622a242360e91b03610f0d57633b9aca00826001600160401b03161015610ee45760405163da6a17b960e01b815260040160405180910390fd5b603c8054600160401b600160801b031916600160401b6001600160401b03851602179055611136565b8061212360f11b03610f7057633b9aca00826001600160401b03161115610f4757604051637650e96360e11b815260040160405180910390fd5b603c8054600160801b600160c01b031916600160801b6001600160401b03851602179055611136565b8061292360f11b03610fec57603d54633b9aca0090610f9f90600160401b90046001600160401b031684615803565b6001600160401b03161115610fc757604051637650e96360e11b815260040160405180910390fd5b603c80546001600160c01b0316600160c01b6001600160401b03851602179055611136565b806124a960f11b0361102157611000613acd565b50603d80546001600160401b0319166001600160401b038416179055611136565b80614c5360f01b036110b957603c546001600160401b03808416911611806110725750603c54633b9aca009061106790600160c01b90046001600160401b031684615803565b6001600160401b0316115b156110905760405163180d062b60e31b815260040160405180910390fd5b603d8054600160401b600160801b031916600160401b6001600160401b03851602179055611136565b806213531160ea1b0361111d57633b9aca00826001600160401b031611156110f457604051637650e96360e11b815260040160405180910390fd5b603d8054600160801b600160c01b031916600160801b6001600160401b03851602179055611136565b60405163e1daa9cf60e01b815260040160405180910390fd5b604080516001600160401b0384168152602081018390527f13b367dac93b85d1ed9b3d8961d8b48e1a677c9800bb1613b4b0416b2d5b61d091015b60405180910390a15050565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b8906111ad903390600401614cfc565b602060405180830381865afa1580156111ca573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111ee91906156ce565b61120b57604051633b8d9d7560e21b815260040160405180910390fd5b603354604080516361d027b360e01b815290516001600160a01b03928316928416916361d027b39160048083019260209291908290030181865afa158015611257573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061127b919061582a565b6001600160a01b0316146112a2576040516302979eb960e31b815260040160405180910390fd5b603680546001600160a01b0319166001600160a01b0392909216919091179055565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de9906112f4903390600401614cfc565b602060405180830381865afa158015611311573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061133591906156ce565b61135257604051632678482f60e21b815260040160405180910390fd5b8060008151811061136557611365615847565b602002602001015160000361138d57604051631746545d60e11b815260040160405180910390fd5b80516113a090603b906020840190614c74565b50826001600160a01b03167feb74d4d9fea592587c926aeb35eb6a7893fb28db0c1c8eb2eb3c586e7164b76c83836040516113dc929190615898565b60405180910390a2505050565b6113f1614c45565b603d54600160c81b900460ff161561141c576040516313d0ff5960e31b815260040160405180910390fd5b6002600154036114475760405162461bcd60e51b815260040161143e906158bd565b60405180910390fd5b6002600155865186518114158061145c575080155b1561147a576040516346282e8d60e01b815260040160405180910390fd5b603660009054906101000a90046001600160a01b03166001600160a01b03166357de26a46040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114cd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114f1919061565e565b60608301526114fe613acd565b60808301526040517f965a177723c641ee49150b583a0b9ad4730bb20d3474e00ae5a65e777c00d67b90611533908a906158f4565b60405180910390a160005b81811015611912576000604360008b848151811061155e5761155e615847565b6020026020010151815260200190815260200160002060405180604001604052908160008201548152602001600182015481525050905060006115ab8233876060015188608001516136f8565b905080604001516000141580156115df575080604001518a84815181106115d4576115d4615847565b602002602001015110155b80611606575080600001518a84815181106115fc576115fc615847565b6020026020010151115b1561162f5780600001518a848151811061162257611622615847565b6020026020010181815250505b600085606001518260600151611645919061568d565b603854633b9aca008d878151811061165f5761165f615847565b6020026020010151611671919061568d565b61167b919061568d565b61168591906156ba565b90506116c08c858151811061169c5761169c615847565b602002602001015182856000015111156116b657826116b9565b84515b6000613b6e565b825181106117f6575081516020830151604080546000906116e29084906156eb565b92505081905550604360008d86815181106116ff576116ff615847565b60209081029190910181015182528101919091526040016000908120818155600101819055603d548c51633b9aca0091600160401b90046001600160401b0316908e908890811061175257611752615847565b6020026020010151611764919061568d565b61176e91906156ba565b905082608001518110611782576000611792565b80836080015161179291906156eb565b876040018181516117a39190615907565b905250508b51600080516020615d75833981519152908d90869081106117cb576117cb615847565b6020026020010151846020015160006040516117e99392919061591a565b60405180910390a16118b7565b80604360008e878151811061180d5761180d615847565b60200260200101518152602001908152602001600020600001600082825461183591906156eb565b925050819055506118b58c858151811061185157611851615847565b6020026020010151633b9aca00603d60089054906101000a90046001600160401b03166001600160401b03168e888151811061188f5761188f615847565b60200260200101516118a1919061568d565b6118ab91906156ba565b8860800151613b81565b505b80866020018181516118c99190615907565b9052508a518b90859081106118e0576118e0615847565b6020026020010151866000018181516118f99190615907565b90525061190b92508391506159339050565b905061153e565b50603d54633b9aca009061193690600160401b90046001600160401b0316826156eb565b8351611942919061568d565b61194c91906156ba565b6041600082825461195d9190615907565b9091555050604082015160428054600090611979908490615907565b909155505060208201518251611993919088888888613cb3565b50600180559695505050505050565b603d54600160c81b900460ff16156119cd576040516313d0ff5960e31b815260040160405180910390fd5b6033546040516333b52a9f60e11b81526001600160a01b039091169063676a553e906119fd903390600401614cfc565b602060405180830381865afa158015611a1a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3e91906156ce565b611a5b5760405163027f480760e01b815260040160405180910390fd5b603c54600090600160c01b90046001600160401b0316821015611a9957603c54611a96908390600160c01b90046001600160401b03166156eb565b90505b603c54600090600160801b90046001600160401b0316841115611ad657603c54611ad390600160801b90046001600160401b0316856156eb565b90505b6000611ae76002633b9aca00615a30565b611af583633b9aca006156eb565b611b0385633b9aca006156eb565b611b0d908961568d565b611b17919061568d565b611b2191906156ba565b9050611b2d81876156eb565b60416000828254611b3e9190615907565b90915550611b50905087826000613b81565b5050505050505050565b607e80546108df90615624565b611b6f614cbf565b60408051600080825260208201909252611b9191879187918791879190612547565b90505b949350505050565b610d64338383613dbd565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990611bd7903390600401614cfc565b602060405180830381865afa158015611bf4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c1891906156ce565b611c3557604051632678482f60e21b815260040160405180910390fd5b60398190556040518181527fdd63b3dcdbebad734892f7c7a26d0f647fbc7eec973e0775f5229018ac4ab47a9060200160405180910390a150565b603d54600090600160c81b900460ff1615611c9e576040516313d0ff5960e31b815260040160405180910390fd5b6108cc82613e72565b919050565b3382611cb882826133b3565b611cd55760405163c19f17a960e01b815260040160405180910390fd5b611ce186868686613fac565b505050505050565b603354604051631c86b03760e31b81526001600160a01b039091169063e43581b890611d19903390600401614cfc565b602060405180830381865afa158015611d36573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611d5a91906156ce565b611d7757604051633b8d9d7560e21b815260040160405180910390fd5b81831115611d985760405163180d062b60e31b815260040160405180910390fd5b60b49290925560b65560b555565b60335460405163521d4de960e01b81526001600160a01b039091169063521d4de990611dd6903390600401614cfc565b602060405180830381865afa158015611df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e1791906156ce565b611e3457604051632678482f60e21b815260040160405180910390fd5b603d805460ff60c81b198116600160c81b9182900460ff1615909102179055565b6060611e60826132d7565b611e7d5760405163062a39dd60e11b815260040160405180910390fd5b8160005b8115611ea457611e9081615933565b9050611e9d600a836156ba565b9150611e81565b6000816001600160401b03811115611ebe57611ebe614ec7565b6040519080825280601f01601f191660200182016040528015611ee8576020820181803683370190505b5090505b8415611f5357611efd6001836156eb565b9150611f0a600a86615a3f565b611f15906030615907565b60f81b818381518110611f2a57611f2a615847565b60200101906001600160f81b031916908160001a905350611f4c600a866156ba565b9450611eec565b60468054611f6090615624565b9050600003611f7e5760405180602001604052806000815250611fa2565b604681604051602001611f92929190615a53565b6040516020818303038152906040525b95945050505050565b600054610100900460ff1615808015611fcb5750600054600160ff909116105b80611fec5750611fda306132c8565b158015611fec575060005460ff166001145b61204f5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161143e565b6000805460ff191660011790558015612072576000805461ff0019166101001790555b856001600160a01b0316846001600160a01b03166361d027b36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120de919061582a565b6001600160a01b031614612105576040516302979eb960e31b815260040160405180910390fd5b603380546001600160a01b038089166001600160a01b0319928316179092556034805492881692909116821790556040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801561216e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121929190615ada565b61219d90600a615a30565b603881905550856001600160a01b031663e9cbd8226040518163ffffffff1660e01b8152600401602060405180830381865afa1580156121e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612205919061582a565b603580546001600160a01b03199081166001600160a01b039384161790915560368054909116918616919091179055604051600090612248908490602001615af7565b60408051601f198184030181529190529050607d6122668282615744565b5061227081613fe6565b826040516020016122819190615b3e565b604051602081830303815290604052607e908161229e9190615744565b50676765c793fa10079d601b1b603f5542603e556122c260a0850160808601615b68565b6001600160401b03166122db6040860160208701615b68565b6001600160401b0316118061230b5750633b9aca0061230060a0860160808701615b68565b6001600160401b0316115b8061233157506123216060850160408601615b68565b6001600160401b0316633b9aca00115b806123585750633b9aca0061234c60c0860160a08701615b68565b6001600160401b031610155b80612365575060e0840135155b1561238357604051631746545d60e11b815260040160405180910390fd5b83356039556123986040850160208601615b68565b603c80546001600160401b0319166001600160401b03929092169190911790556123c86060850160408601615b68565b603c80546001600160401b0392909216600160401b02600160401b600160801b03199092169190911790556124036080850160608601615b68565b603d80546001600160401b0319166001600160401b039290921691909117905561243360a0850160808601615b68565b603d80546001600160401b0392909216600160401b02600160401b600160801b031990921691909117905561246e60c0850160a08601615b68565b603d80546001600160401b0392909216600160801b02600160801b600160c01b03199092169190911790556124a960e0850160c08601615b83565b603d8054911515600160c01b0260ff60c01b19909216919091179055604080516020810190915260e085013581526124e590603b906001614c74565b5050603d805460ff60c81b1916600160c81b1790558015611ce1576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050505050565b61254f614cbf565b603d54600160c81b900460ff161561257a576040516313d0ff5960e31b815260040160405180910390fd5b60026001540361259c5760405162461bcd60e51b815260040161143e906158bd565b6002600155855187511415806125b157508651155b156125cf576040516346282e8d60e01b815260040160405180910390fd5b6000806000806000805b8c51811015612b4b5760008d82815181106125f6576125f6615847565b602002602001015190506000600781111561261357612613615ba0565b81600781111561262557612625615ba0565b036126655761265f8d838151811061263f5761263f615847565b602002602001015180602001905181019061265a919061582a565b613e72565b50612b3a565b600281600781111561267957612679615ba0565b036126e2578c828151811061269057612690615847565b60200260200101518060200190518101906126ab9190615bb6565b9550925060008390036126be5760455492505b6126c883866140bc565b84886060018181516126da9190615907565b905250612b3a565b60078160078111156126f6576126f6615ba0565b036127ce5760008060008f858151811061271257612712615847565b602002602001015180602001905181019061272d9190615bda565b60345460405163d505accf60e01b81526001600160a01b038089166004830152306024830152604482018890526064820187905260ff8616608483015260a4820185905260c48201849052969f50939d50939b509497509550929350169063d505accf9060e401600060405180830381600087803b1580156127ae57600080fd5b505af11580156127c2573d6000803e3d6000fd5b50505050505050612b3a565b866000036127e1576127de613acd565b96505b60048160078111156127f5576127f5615ba0565b036128c4578c828151811061280c5761280c615847565b60200260200101518060200190518101906128279190615bb6565b94509250600083900361283a5760455492505b612845838589613b81565b603c5490945060009061286c90600160c01b90046001600160401b0316633b9aca006156eb565b61287a633b9aca008761568d565b61288491906156ba565b905061289085826156eb565b604160008282546128a19190615907565b9250508190555080896020018181516128ba9190615907565b905250612b3a9050565b8560000361294657603660009054906101000a90046001600160a01b03166001600160a01b03166357de26a46040518163ffffffff1660e01b8152600401602060405180830381865afa15801561291f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612943919061565e565b95505b600181600781111561295a5761295a615ba0565b036129d8578c828151811061297157612971615847565b602002602001015180602001905181019061298c919061565e565b92508260000361299c5760455492505b6129a7838789614134565b809650819550505084886040018181516129c19190615907565b9052506020880180518591906126da908390615907565b60038160078111156129ec576129ec615ba0565b03612a4f578c8281518110612a0357612a03615847565b6020026020010151806020019051810190612a1e9190615bb6565b955092506000839003612a315760455492505b612a3d8386888a614254565b84886040018181516126da9190615907565b6005816007811115612a6357612a63615ba0565b03612ac8578c8281518110612a7a57612a7a615847565b6020026020010151806020019051810190612a959190615bb6565b945092506000839003612aa85760455492505b612ab48385888a61433a565b935083886000018181516126da9190615907565b6006816007811115612adc57612adc615ba0565b03612b3a576000808e8481518110612af657612af6615847565b6020026020010151806020019051810190612b119190615c2d565b9850919650925090506000859003612b295760455494505b612b37858383898c8e6143d5565b50505b50612b4481615933565b90506125d9565b508551602087015110612c425785516020870151600091612b6b916156eb565b90508660600151876040015110612ba257612b9d87606001518860400151612b9391906156eb565b828d8d8d8d613cb3565b612c3c565b8015612c0f57603554604051630d43af8160e21b81526001600160a01b039091169063350ebe0490612bdc9084908f903390600401615c6b565b600060405180830381600087803b158015612bf657600080fd5b505af1158015612c0a573d6000803e3d6000fd5b505050505b612c3c333089604001518a60600151612c2891906156eb565b6034546001600160a01b0316929190614510565b50612dce565b60208601518651600091612c55916156eb565b6035546040516340c10f1960e01b81529192506001600160a01b0316906340c10f1990612c88908d908590600401615c8a565b600060405180830381600087803b158015612ca257600080fd5b505af1158015612cb6573d6000803e3d6000fd5b50505050866060015187604001511115612cfa57612cf58a88606001518960400151612ce291906156eb565b6034546001600160a01b0316919061457b565b612dcc565b600087604001518860600151612d1091906156eb565b90508015612dca57885115612db257896001600160a01b031663a5d4096b603560009054906101000a90046001600160a01b0316603460009054906101000a90046001600160a01b03163385878f6040518763ffffffff1660e01b8152600401612d7f96959493929190615ca3565b600060405180830381600087803b158015612d9957600080fd5b505af1158015612dad573d6000803e3d6000fd5b505050505b603454612dca906001600160a01b0316333084614510565b505b505b505060018055509198975050505050505050565b603a8181548110610c9257600080fd5b6001600160a01b039182166000908152604a6020908152604080832093909416825291909152205460011490565b6033546001600160a01b03163314612e4b5760405163b90cdbb160e01b815260040160405180910390fd5b603380546001600160a01b0319166001600160a01b0383811691909117909155603654604051630787a21360e51b815291169063f0f4426090612e92908490600401614cfc565b600060405180830381600087803b158015612eac57600080fd5b505af1158015610a51573d6000803e3d6000fd5b83421115612ee15760405163f87d927160e01b815260040160405180910390fd5b6fa2a8918ca85bafe22016d0b997e4df60600160ff1b03811180612f1857508260ff16601b14158015612f1857508260ff16601c14155b15612f3657604051638baa579f60e01b815260040160405180910390fd5b6000612f40613a61565b608254898989612f4f8d61459a565b6040805160208101969096526001600160a01b03948516908601529290911660608401521515608083015260a082015260c0810187905260e00160405160208183030381529060405280519060200120604051602001612fc692919061190160f01b81526002810192909252602282015260420190565b604051602081830303815290604052805190602001209050612ff0886001600160a01b03166132c8565b156130cc57604080516020810185905280820184905260f886901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b038a1691631626ba7e91613056918591606501615ce5565b602060405180830381865afa158015613073573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130979190615cfe565b6001600160e01b031916631626ba7e60e01b146130c757604051638baa579f60e01b815260040160405180910390fd5b613174565b6040805160008082526020820180845284905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa158015613120573d6000803e3d6000fd5b505050602060405103519050886001600160a01b0316816001600160a01b031614158061315457506001600160a01b038116155b1561317257604051638baa579f60e01b815260040160405180910390fd5b505b611b50888888613dbd565b613187614c45565b60408051600080825260208201909252611b91918791879187918791906113e9565b60335460009081906001600160a01b031633146131d95760405163b90cdbb160e01b815260040160405180910390fd5b6131e1613acd565b50506041805460428054600093849055929055915080821061327a5761320781836156eb565b6035546033546040516340c10f1960e01b8152929450600093506001600160a01b03918216926340c10f19926132439216908690600401615c8a565b600060405180830381600087803b15801561325d57600080fd5b505af1158015613271573d6000803e3d6000fd5b5050505061328b565b61328482826156eb565b9050600091505b60408051838152602081018390527ffeb12225c131aab793a00c5239afb778932d170fa28ce6e9d23703e4bd892121910160405180910390a19091565b6001600160a01b03163b151590565b6000908152604760205260409020546001600160a01b0316151590565b6000908152604960205260409020546001600160a01b031690565b6000818152604760205260409020546001600160a01b031680611ca75760405163062a39dd60e11b815260040160405180910390fd5b600081815260496020526040902080546001600160a01b0319166001600160a01b038416908117909155819061337a8261330f565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000806133bf8361330f565b9050806001600160a01b0316846001600160a01b031614806133fa5750836001600160a01b03166133ef846132f4565b6001600160a01b0316145b80611b9457506001600160a01b038082166000908152604a6020908152604080832093881683529290522054600114949350505050565b826001600160a01b03166134448261330f565b6001600160a01b03161461346b5760405163c19f17a960e01b815260040160405180910390fd5b6001600160a01b0382166134925760405163d92e233d60e01b815260040160405180910390fd5b603d54600160c01b900460ff1680156134c457506001600160a01b038216600090815260446020526040902054600114155b156134e257604051630b094f2760e31b815260040160405180910390fd5b6134ed8383836145de565b6134f8600082613345565b6001600160a01b038084166000818152604860209081526040808320805460001901905593861680835284832080546001019055858352604790915283822080546001600160a01b03191682179055925184939291600080516020615db583398151915291a4505050565b600080603e544261357491906156eb565b603d549091506001600160401b031681158061358e575080155b1561359d57603f549250505090565b60006135aa6001846156eb565b90506000600284116135bd5760006135c8565b6135c86002856156eb565b90506000676765c793fa10079d601b1b676765c793fa10079d601a1b6135ee868061568d565b6135f89190615907565b61360291906156ba565b90506000676765c793fa10079d601b1b676765c793fa10079d601a1b613628878561568d565b6136329190615907565b61363c91906156ba565b9050600060028361364d878a61568d565b613657919061568d565b61366191906156ba565b9050600060068386613673898c61568d565b61367d919061568d565b613687919061568d565b61369191906156ba565b9050676765c793fa10079d601b1b81836136ab8b8b61568d565b6136c090676765c793fa10079d601b1b615907565b6136ca9190615907565b6136d49190615907565b603f546136e1919061568d565b6136eb91906156ba565b9850505050505050505090565b613700614c45565b6000806000613710888787614625565b925092509250633b9aca00831061373a576040516315fe9b6160e21b815260040160405180910390fd5b6000633b9aca0061374b85826156eb565b6137548a6146b1565b61375e919061568d565b61376891906156ba565b603d54909150600160801b90046001600160401b03168110156137985761379381633b9aca006156eb565b6137b9565b603d546137b990600160801b90046001600160401b0316633b9aca006156eb565b603d54909150600160401b90046001600160401b03166000806137e16002633b9aca00615a30565b603c546137f791906001600160401b031661568d565b83613802868a61568d565b61380c919061568d565b1061399157603c546001600160401b031661382c6002633b9aca00615a30565b613836919061568d565b603c54859061385590600160401b90046001600160401b03168661568d565b61385f919061568d565b61386991906156eb565b603c548590633b9aca0090613887906001600160401b03168961568d565b603c546138a5908b90600160401b90046001600160401b031661568d565b6138af91906156eb565b6138b9919061568d565b6138c3919061568d565b6138cd91906156ba565b60b6549092506138e1633b9aca008261568d565b6138eb858561568d565b6138f59190615907565b613903633b9aca008961568d565b1161398b5761391d676765c793fa10079d601b1b8561568d565b633b9aca008b8f60200151613932919061568d565b61393c919061568d565b61394691906156ba565b613951906001615907565b9250808711156139865783633b9aca0061396b838a6156eb565b613975919061568d565b61397f91906156ba565b915061398b565b600191505b50613a0f565b6038546139a290633b9aca0061568d565b8c518b906139b190879061568d565b6139bb919061568d565b6139c591906156ba565b6139d0906001615907565b915060b554851115613a0b57633b9aca008460b554876139f091906156eb565b6139fa919061568d565b613a0491906156ba565b9050613a0f565b5060015b818852613a1c848b61568d565b603854613a2d633b9aca008561568d565b613a37919061568d565b613a4191906156ba565b602089015260408801525050606085015250608083015250949350505050565b60808054608154604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f602082015290810192909252606082015246918101919091523060a082015260009060c00160405160208183030381529060405280519060200120905090565b6000613ad7613563565b90506000676765c793fa10079d601b1b603f5483613af591906156eb565b604054613b02919061568d565b613b0c91906156ba565b90508060416000828254613b209190615907565b9091555050603f82905542603e8190556040805184815260208101929092527fd1fa8ba00a3bf20274346919dce0de62d2a140af2c71fe7e29fa6472eea3bb9d910160405180910390a15090565b610a18613b7a8461330f565b83836146d9565b600081600003613b9657613b93613acd565b91505b60008481526043602052604081206001015490676765c793fa10079d601b1b613bbf858461568d565b613bc991906156ba565b9050808510613bda57935080613bfd565b83613bf0676765c793fa10079d601b1b8761568d565b613bfa91906156ba565b90505b613c0781836156eb565b91508060406000828254613c1b91906156eb565b90915550508115801590613c505750676765c793fa10079d601b1b60b454613c43919061568d565b613c4d858461568d565b11155b15613c6e5760405163228af07f60e21b815260040160405180910390fd5b60008681526043602052604080822060010184905551600080516020615d7583398151915291613ca1918991859161591a565b60405180910390a15092949350505050565b8515613cd057603454613cd0906001600160a01b0316848861457b565b8415611ce157805115613d4f5760345460355460405163a5d4096b60e01b81526001600160a01b038086169363a5d4096b93613d1c9391831692169089908b908d908990600401615ca3565b600060405180830381600087803b158015613d3657600080fd5b505af1158015613d4a573d6000803e3d6000fd5b505050505b603554604051630d43af8160e21b81526001600160a01b039091169063350ebe0490613d8390889088903390600401615c6b565b600060405180830381600087803b158015613d9d57600080fd5b505af1158015613db1573d6000803e3d6000fd5b50505050505050505050565b826001600160a01b0316826001600160a01b031603613def576040516320c5195360e21b815260040160405180910390fd5b600081613dfd576000613e00565b60015b6001600160a01b038581166000818152604a602090815260408083209489168084529482529182902060ff959095169485905590518615158152939450919290917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a350505050565b603d54600090600160c01b900460ff168015613ec157506001600160a01b0382166000908152604460205260409020546001141580613ec1575033600090815260446020526040902054600114155b15613edf57604051630b094f2760e31b815260040160405180910390fd5b6001600160a01b038216613f065760405163d92e233d60e01b815260040160405180910390fd5b506045805460010190819055613f1e600083836145de565b6001600160a01b038216600081815260486020908152604080832080546001019055848352604790915280822080546001600160a01b031916841790555183929190600080516020615db5833981519152908290a4613f8f600083836040518060200160405280600081525061474c565b611ca7576040516320149b4360e21b815260040160405180910390fd5b613fb7848484613431565b613fc38484848461474c565b613fe0576040516320149b4360e21b815260040160405180910390fd5b50505050565b600054610100900460ff166140515760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840161143e565b7f3f43a9c6bafb5c7aab4e0cfe239dc5d4c15caf0381c6104188191f78a6640bd860825580516020918201206080556040805180820190915260018152603160f81b9101527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6608155565b6140c5826132d7565b6140e25760405163062a39dd60e11b815260040160405180910390fd5b6140ee82826001613b6e565b6000828152604360205260408120805483929061410c908490615907565b9091555050604051600080516020615d9583398151915290611171908490849060019061591a565b600080338561414382826133b3565b6141605760405163c19f17a960e01b815260040160405180910390fd5b600087815260436020908152604080832081518083019092528054825260010154918101919091529080614195838a8a614625565b5091509150633b9aca0082116141be57604051631527804d60e31b815260040160405180910390fd5b8260200151604060008282546141d491906156eb565b909155506141e390508a614852565b603c5460009061420790600160c01b90046001600160401b0316633b9aca006156eb565b614215633b9aca008461568d565b61421f91906156ba565b905061422b82826156eb565b6041600082825461423c9190615907565b90915550509251929a92995091975050505050505050565b338461426082826133b3565b61427d5760405163c19f17a960e01b815260040160405180910390fd5b61428986866000613b6e565b600086815260436020526040812080548792906142a79084906156eb565b9091555050600086815260436020908152604080832081518083019092528054825260010154918101919091526142df908686614625565b50509050633b9aca00811161430757604051631527804d60e31b815260040160405180910390fd5b600080516020615d95833981519152878760006040516143299392919061591a565b60405180910390a150505050505050565b6000338561434882826133b3565b6143655760405163c19f17a960e01b815260040160405180910390fd5b614371878787876148dd565b603c54909650600090633b9aca009061439b908990600160801b90046001600160401b031661568d565b6143a591906156ba565b905080604160008282546143b99190615907565b909155506143c9905081886156eb565b98975050505050505050565b33866143e182826133b3565b6143fe5760405163c19f17a960e01b815260040160405180910390fd5b60408051898152602081018890526001600160a01b038916818301526060810187905290517fddd3b70af631334f7552aadb582ed091018e62e103fa8b150ca66cc700d4dac69181900360800190a1614459888686866148dd565b9450306001600160a01b0388160361447c57614476868685613b81565b50611b50565b603c546040516320d661ad60e21b815260048101889052602481018790526001600160401b03600160801b830481166044830152600160c01b90920490911660648201526001600160a01b0388169063835986b490608401600060405180830381600087803b1580156144ee57600080fd5b505af1158015614502573d6000803e3d6000fd5b505050505050505050505050565b6040516001600160a01b0380851660248301528316604482015260648101829052613fe09085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152614a71565b610a188363a9059cbb60e01b8484604051602401614544929190615c8a565b6001600160a01b0381166000908152607f60205260409020546145be816001615907565b6001600160a01b039092166000908152607f602052604090209190915590565b6001600160a01b03831615610a18576000818152604360205260408120549061460a90859083906146d9565b6001600160a01b03831615613fe057613fe0838260016146d9565b6000806000676765c793fa10079d601b1b848760200151614646919061568d565b61465091906156ba565b9150603854858760000151614665919061568d565b61466f91906156ba565b9050816000036146835760001992506146a8565b603c54829061469b906001600160401b03168361568d565b6146a591906156ba565b92505b93509350939050565b6000603b6000815481106146c7576146c7615847565b90600052602060002001549050919050565b6034546040516301627e4560e01b81526001600160a01b038581166004830152602482018590528315156044830152909116906301627e4590606401600060405180830381600087803b15801561472f57600080fd5b505af1158015614743573d6000803e3d6000fd5b50505050505050565b6000614760846001600160a01b03166132c8565b1561484a57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290614797903390899088908890600401615d1b565b6020604051808303816000875af19250505080156147d2575060408051601f3d908101601f191682019092526147cf91810190615cfe565b60015b614830573d808015614800576040519150601f19603f3d011682016040523d82523d6000602084013e614805565b606091505b508051600003614828576040516320149b4360e21b815260040160405180910390fd5b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611b94565b506001611b94565b600061485d8261330f565b905061486b816000846145de565b614876600083613345565b6001600160a01b038116600081815260486020908152604080832080546000190190558583526047825280832080546001600160a01b0319169055604390915280822082815560010182905551849290600080516020615db5833981519152908390a45050565b600080826148f6676765c793fa10079d601b1b8761568d565b61490091906156ba565b6000878152604360205260408120600101549192500361493c5760b454851161493c5760405163228af07f60e21b815260040160405180910390fd5b6000868152604360205260408120600101805483929061495d908490615907565b9250508190555080604060008282546149769190615907565b909155505060395461499490676765c793fa10079d601b1b9061568d565b836040546149a2919061568d565b11156149c1576040516371239a6160e11b815260040160405180910390fd5b600086815260436020908152604080832081518083019092528054825260010154918101919091526149f4908686614625565b50509050633b9aca008111614a1c57604051631527804d60e31b815260040160405180910390fd5b600080516020615d7583398151915287836001604051614a3e9392919061591a565b60405180910390a1676765c793fa10079d601b1b614a5c858461568d565b614a6691906156ba565b979650505050505050565b6000614ac6826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316614b439092919063ffffffff16565b805190915015610a185780806020019051810190614ae491906156ce565b610a185760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840161143e565b6060611b94848460008585614b57856132c8565b614ba35760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161143e565b600080866001600160a01b03168587604051614bbf9190615d58565b60006040518083038185875af1925050503d8060008114614bfc576040519150601f19603f3d011682016040523d82523d6000602084013e614c01565b606091505b5091509150614a6682828660608315614c1b575081610b03565b825115614c2b5782518084602001fd5b8160405162461bcd60e51b815260040161143e9190614d93565b6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b828054828255906000526020600020908101928215614caf579160200282015b82811115614caf578251825591602001919060010190614c94565b50614cbb929150614ce7565b5090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b5b80821115614cbb5760008155600101614ce8565b6001600160a01b0391909116815260200190565b6001600160e01b031981168114610c7f57600080fd5b600060208284031215614d3857600080fd5b8135610b0381614d10565b60005b83811015614d5e578181015183820152602001614d46565b50506000910152565b60008151808452614d7f816020860160208601614d43565b601f01601f19169290920160200192915050565b602081526000610b036020830184614d67565b600060208284031215614db857600080fd5b5035919050565b6001600160a01b0381168114610c7f57600080fd5b60008060408385031215614de757600080fd5b8235614df281614dbf565b946020939093013593505050565b600080600060608486031215614e1557600080fd5b8335614e2081614dbf565b92506020840135614e3081614dbf565b929592945050506040919091013590565b60008060408385031215614e5457600080fd5b823591506020830135614e6681614dbf565b809150509250929050565b60a081016108cc828480518252602081015160208301526040810151604083015260608101516060830152608081015160808301525050565b600060208284031215614ebc57600080fd5b8135610b0381614dbf565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b0381118282101715614f0557614f05614ec7565b604052919050565b600082601f830112614f1e57600080fd5b81356001600160401b03811115614f3757614f37614ec7565b614f4a601f8201601f1916602001614edd565b818152846020838601011115614f5f57600080fd5b816020850160208301376000918101602001919091529392505050565b600060208284031215614f8e57600080fd5b81356001600160401b03811115614fa457600080fd5b611b9484828501614f0d565b80356001600160401b0381168114611ca757600080fd5b60008060408385031215614fda57600080fd5b614df283614fb0565b60006001600160401b03821115614ffc57614ffc614ec7565b5060051b60200190565b600082601f83011261501757600080fd5b8135602061502c61502783614fe3565b614edd565b82815260059290921b8401810191818101908684111561504b57600080fd5b8286015b84811015615066578035835291830191830161504f565b509695505050505050565b60008060006060848603121561508657600080fd5b833561509181614dbf565b925060208401356001600160401b03808211156150ad57600080fd5b6150b987838801615006565b935060408601359150808211156150cf57600080fd5b506150dc86828701615006565b9150509250925092565b60008060008060008060c087890312156150ff57600080fd5b86356001600160401b038082111561511657600080fd5b6151228a838b01615006565b9750602089013591508082111561513857600080fd5b6151448a838b01615006565b96506040890135915061515682614dbf565b90945060608801359061516882614dbf565b90935060808801359061517a82614dbf565b90925060a0880135908082111561519057600080fd5b5061519d89828a01614f0d565b9150509295509295509295565b600080600080608085870312156151c057600080fd5b5050823594602084013594506040840135936060013592509050565b600082601f8301126151ed57600080fd5b813560206151fd61502783614fe3565b82815260059290921b8401810191818101908684111561521c57600080fd5b8286015b84811015615066578035600881106152385760008081fd5b8352918301918301615220565b600082601f83011261525657600080fd5b8135602061526661502783614fe3565b82815260059290921b8401810191818101908684111561528557600080fd5b8286015b848110156150665780356001600160401b038111156152a85760008081fd5b6152b68986838b0101614f0d565b845250918301918301615289565b600080600080608085870312156152da57600080fd5b84356001600160401b03808211156152f157600080fd5b6152fd888389016151dc565b9550602087013591508082111561531357600080fd5b5061532087828801615245565b935050604085013561533181614dbf565b9150606085013561534181614dbf565b939692955090935050565b8015158114610c7f57600080fd5b6000806040838503121561536d57600080fd5b823561537881614dbf565b91506020830135614e668161534c565b6000806000806080858703121561539e57600080fd5b84356153a981614dbf565b935060208501356153b981614dbf565b92506040850135915060608501356001600160401b038111156153db57600080fd5b6153e787828801614f0d565b91505092959194509250565b60008060006060848603121561540857600080fd5b505081359360208301359350604090920135919050565b600080600080600085870361018081121561543957600080fd5b863561544481614dbf565b9550602087013561545481614dbf565b9450604087013561546481614dbf565b9350610100605f198201121561547957600080fd5b506060860191506101608601356001600160401b0381111561549a57600080fd5b6154a688828901614f0d565b9150509295509295909350565b60008060008060008060c087890312156154cc57600080fd5b86356001600160401b03808211156154e357600080fd5b6154ef8a838b016151dc565b9750602089013591508082111561550557600080fd5b6151448a838b01615245565b6000806040838503121561552457600080fd5b823561552f81614dbf565b91506020830135614e6681614dbf565b60ff81168114610c7f57600080fd5b600080600080600080600060e0888a03121561556957600080fd5b873561557481614dbf565b9650602088013561558481614dbf565b955060408801356155948161534c565b94506060880135935060808801356155ab8161553f565b9699959850939692959460a0840135945060c09093013592915050565b600080600080608085870312156155de57600080fd5b84356001600160401b03808211156155f557600080fd5b61560188838901615006565b9550602087013591508082111561561757600080fd5b5061532087828801615006565b600181811c9082168061563857607f821691505b60208210810361565857634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561567057600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b80820281158282048414176108cc576108cc615677565b634e487b7160e01b600052601260045260246000fd5b6000826156c9576156c96156a4565b500490565b6000602082840312156156e057600080fd5b8151610b038161534c565b818103818111156108cc576108cc615677565b601f821115610a1857600081815260208120601f850160051c810160208610156157255750805b601f850160051c820191505b81811015611ce157828155600101615731565b81516001600160401b0381111561575d5761575d614ec7565b6157718161576b8454615624565b846156fe565b602080601f8311600181146157a6576000841561578e5750858301515b600019600386901b1c1916600185901b178555611ce1565b600085815260208120601f198616915b828110156157d5578886015182559484019460019091019084016157b6565b50858210156157f35787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6001600160401b0381811683821601908082111561582357615823615677565b5092915050565b60006020828403121561583c57600080fd5b8151610b0381614dbf565b634e487b7160e01b600052603260045260246000fd5b600081518084526020808501945080840160005b8381101561588d57815187529582019590820190600101615871565b509495945050505050565b6040815260006158ab604083018561585d565b8281036020840152611fa2818561585d565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b602081526000610b03602083018461585d565b808201808211156108cc576108cc615677565b928352602083019190915260ff16604082015260600190565b60006001820161594557615945615677565b5060010190565b600181815b8085111561598757816000190482111561596d5761596d615677565b8085161561597a57918102915b93841c9390800290615951565b509250929050565b60008261599e575060016108cc565b816159ab575060006108cc565b81600181146159c157600281146159cb576159e7565b60019150506108cc565b60ff8411156159dc576159dc615677565b50506001821b6108cc565b5060208310610133831016604e8410600b8410161715615a0a575081810a6108cc565b615a14838361594c565b8060001904821115615a2857615a28615677565b029392505050565b6000610b0360ff84168361598f565b600082615a4e57615a4e6156a4565b500690565b6000808454615a6181615624565b60018281168015615a795760018114615a8e57615abd565b60ff1984168752821515830287019450615abd565b8860005260208060002060005b85811015615ab45781548a820152908401908201615a9b565b50505082870194505b505050508351615ad1818360208801614d43565b01949350505050565b600060208284031215615aec57600080fd5b8151610b038161553f565b6e020b733b63290283937ba37b1b7b61608d1b815260008251615b2181600f850160208701614d43565b650815985d5b1d60d21b600f939091019283015250601501919050565b60008251615b50818460208701614d43565b650b5d985d5b1d60d21b920191825250600601919050565b600060208284031215615b7a57600080fd5b610b0382614fb0565b600060208284031215615b9557600080fd5b8135610b038161534c565b634e487b7160e01b600052602160045260246000fd5b60008060408385031215615bc957600080fd5b505080516020909101519092909150565b60008060008060008060c08789031215615bf357600080fd5b8651615bfe81614dbf565b6020880151604089015160608a015160808b015160a0909b0151939c929b509099909850965090945092505050565b60008060008060808587031215615c4357600080fd5b845193506020850151615c5581614dbf565b6040860151606090960151949790965092505050565b9283526001600160a01b03918216602084015216604082015260600190565b6001600160a01b03929092168252602082015260400190565b6001600160a01b038781168252868116602083015285166040820152606081018490526080810183905260c060a082018190526000906143c990830184614d67565b828152604060208201526000611b946040830184614d67565b600060208284031215615d1057600080fd5b8151610b0381614d10565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090615d4e90830184614d67565b9695505050505050565b60008251615d6a818460208701614d43565b919091019291505056fe70cf49afe7355562d5b022e594790f22b71ad8cc7eec902fa5feac7c67f71091722cb71fa87c947148cefc06dd890af5802a6a00207c5ddecf1191bf71ce3cd4ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220989782b92d84509719429db7a0d2acce087612fa7a3bed7609b857375005fdab64736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.