ETH Price: $3,421.46 (+0.89%)
Gas: 49 Gwei

Contract

0x3Ae354d7E49039CcD582f1F3c9e65034fFd17baD
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
0x60806040168295862023-03-14 23:55:23353 days ago1678838123IN
 Create: Vault
0 ETH0.1181176532.65105915

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vault

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 4 of 12 : Vault.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.13;

import { IERC721 } from "./interfaces/IERC721.sol";
import { IERC1155 } from "./interfaces/IERC1155.sol";
import { IMainRegistry } from "./interfaces/IMainRegistry.sol";
import { ITrustedCreditor } from "./interfaces/ITrustedCreditor.sol";
import { IActionBase, ActionData } from "./interfaces/IActionBase.sol";
import { IFactory } from "./interfaces/IFactory.sol";
import { IVault } from "./interfaces/IVault.sol";
import { IOraclesHub } from "./PricingModules/interfaces/IOraclesHub.sol";
import { ActionData } from "./actions/utils/ActionData.sol";
import { ERC20, SafeTransferLib } from "../lib/solmate/src/utils/SafeTransferLib.sol";

/**
 * @title Acadia Vaults.
 * @author Pragma Labs
 * @notice Arcadia Vaults are smart contracts that act as onchain, decentralized and composable margin accounts.
 * They provide individuals, DAOs, and other protocols with a simple and flexible way to deposit and manage multiple assets as collateral.
 * The total combination of assets can be used as margin to back liabilities issued by any financial protocol (lending, leverage, futures...).
 * @dev Users can use this vault to deposit assets (ERC20, ERC721, ERC1155, ...).
 * The vault will denominate all the pooled assets into one baseCurrency (one unit of account, like usd or eth).
 * An increase of value of one asset will offset a decrease in value of another asset.
 * Users can use the single denominated value of all their assets to take margin (take credit line, financing for leverage...).
 * Ensure your total value denomination remains above the liquidation threshold, or risk being liquidated!
 * @dev Integrating this vault as means of margin/collateral management for your own protocol that requires collateral is encouraged.
 * Arcadia's vault functions will guarantee you a certain value of the vault.
 * For allowlists or liquidation strategies specific to your protocol, contact pragmalabs.dev
 */
contract Vault is IVault {
    using SafeTransferLib for ERC20;

    /* //////////////////////////////////////////////////////////////
                                STORAGE
    ////////////////////////////////////////////////////////////// */

    // Storage slot with the address of the current implementation.
    // This is the hardcoded keccak-256 hash of: "eip1967.proxy.implementation" subtracted by 1.
    bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
    // The maximum amount of different assets that can be used as collateral within an Arcadia Vault.
    uint256 public constant ASSET_LIMIT = 15;
    // Flag that indicates if a trusted creditor is set.
    bool public isTrustedCreditorSet;
    // The current Vault Version.
    uint16 public vaultVersion;
    // The contract address of the liquidator, address 0 if no trusted creditor is set.
    address public liquidator;
    // The estimated maximum cost to liquidate a Vault, will count as Used Margin when a trusted creditor is set.
    uint96 public fixedLiquidationCost;
    // The owner of the Vault.
    address public owner;
    // The contract address of the MainRegistry.
    address public registry;
    // The trusted creditor, address 0 if no trusted creditor is set.
    address public trustedCreditor;
    // The baseCurrency of the Vault in which all assets and liabilities are denominated.
    address public baseCurrency;

    // Array with all the contract address of ERC20 tokens in the vault.
    address[] public erc20Stored;
    // Array with all the contract address of ERC721 tokens in the vault.
    address[] public erc721Stored;
    // Array with all the contract address of ERC1155 tokens in the vault.
    address[] public erc1155Stored;
    // Array with all the corresponding id's for each ERC721 token in the vault.
    uint256[] public erc721TokenIds;
    // Array with all the corresponding id's for each ERC1155 token in the vault.
    uint256[] public erc1155TokenIds;

    // Map asset => balance.
    mapping(address => uint256) public erc20Balances;
    // Map asset => id => balance.
    mapping(address => mapping(uint256 => uint256)) public erc1155Balances;
    // Map owner => assetManager => flag.
    mapping(address => mapping(address => bool)) public isAssetManager;

    // Storage slot for the Vault logic, a struct to avoid storage conflict when dealing with upgradeable contracts.
    struct AddressSlot {
        address value;
    }

    /* //////////////////////////////////////////////////////////////
                                EVENTS
    ////////////////////////////////////////////////////////////// */

    event BaseCurrencySet(address baseCurrency);
    event TrustedMarginAccountChanged(address indexed protocol, address indexed liquidator);
    event AssetManagerSet(address indexed owner, address indexed assetManager, bool value);

    /* //////////////////////////////////////////////////////////////
                                MODIFIERS
    ////////////////////////////////////////////////////////////// */

    /**
     * @dev Throws if called by any account other than the factory address.
     */
    modifier onlyFactory() {
        require(msg.sender == IMainRegistry(registry).factory(), "V: Only Factory");
        _;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(msg.sender == owner, "V: Only Owner");
        _;
    }

    /**
     * @dev Throws if called by any account other than an asset manager or the owner.
     */
    modifier onlyAssetManager() {
        require(
            msg.sender == owner || msg.sender == trustedCreditor || isAssetManager[owner][msg.sender],
            "V: Only Asset Manager"
        );
        _;
    }

    /* //////////////////////////////////////////////////////////////
                                CONSTRUCTOR
    ////////////////////////////////////////////////////////////// */

    constructor() {
        // This will only be the owner of the vault logic implementation.
        // and will not affect any subsequent proxy implementation using this vault logic.
        owner = msg.sender;
    }

    /* ///////////////////////////////////////////////////////////////
                          VAULT MANAGEMENT
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Initiates the variables of the vault.
     * @dev A proxy will be used to interact with the vault logic.
     * Therefore everything is initialised through an init function.
     * This function will only be called (once) in the same transaction as the proxy vault creation through the factory.
     * @param owner_ The sender of the 'createVault' on the factory
     * @param registry_ The 'beacon' contract with the external logic.
     * @param vaultVersion_ The version of the vault logic.
     * @param baseCurrency_ The Base-currency in which the vault is denominated.
     */
    function initialize(address owner_, address registry_, uint16 vaultVersion_, address baseCurrency_) external {
        require(vaultVersion == 0 && owner == address(0), "V_I: Already initialized!");
        require(vaultVersion_ != 0, "V_I: Invalid vault version");
        owner = owner_;
        registry = registry_;
        vaultVersion = vaultVersion_;
        baseCurrency = baseCurrency_;

        emit BaseCurrencySet(baseCurrency_);
    }

    /**
     * @notice Updates the vault version and stores a new address in the EIP1967 implementation slot.
     * @param newImplementation The contract with the new vault logic.
     * @param newRegistry The MainRegistry for this specific implementation (might be identical as the old registry).
     * @param data Arbitrary data, can contain instructions to execute when updating Vault to new logic.
     * @param newVersion The new version of the vault logic.
     */
    function upgradeVault(address newImplementation, address newRegistry, uint16 newVersion, bytes calldata data)
        external
        onlyFactory
    {
        if (isTrustedCreditorSet) {
            //If a trustedCreditor is set, new version should be compatible.
            //openMarginAccount() is a view function, cannot modify state.
            (bool success,,,) = ITrustedCreditor(trustedCreditor).openMarginAccount(newVersion);
            require(success, "V_UV: Invalid vault version");
        }

        //Cache old parameters
        address oldImplementation = _getAddressSlot(_IMPLEMENTATION_SLOT).value;
        address oldRegistry = registry;
        uint16 oldVersion = vaultVersion;
        _getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
        registry = newRegistry;
        vaultVersion = newVersion;

        //Hook on the new logic to finalize upgrade.
        //Used to eg. Remove exposure from old Registry and Add exposure to the new Registry.
        //Extra data can be added by the factory for complex instructions.
        this.upgradeHook(oldImplementation, oldRegistry, oldVersion, data);

        //Event emitted by Factory.
    }

    /**
     * @notice Returns an `AddressSlot` with member `value` located at `slot`.
     * @param slot The slot where the address of the Logic contract is stored.
     * @return r The address stored in slot.
     */
    function _getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        assembly {
            r.slot := slot
        }
    }

    /**
     * @notice Finalizes the Upgrade to a new vault version on the new Logic Contract.
     * @param oldImplementation The contract with the new old logic.
     * @param oldRegistry The MainRegistry of the old version (might be identical as the new registry)
     * @param oldVersion The old version of the vault logic.
     * @param data Arbitrary data, can contain instructions to execute in this function.
     * @dev If upgradeHook() is implemented, it MUST verify that msg.sender == address(this).
     */
    function upgradeHook(address oldImplementation, address oldRegistry, uint16 oldVersion, bytes calldata data)
        external
    { }

    /* ///////////////////////////////////////////////////////////////
                        OWNERSHIP MANAGEMENT
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Transfers ownership of the contract to a new account.
     * @param newOwner The new owner of the Vault.
     * @dev Can only be called by the current owner via the factory.
     * A transfer of ownership of the vault is triggered by a transfer
     * of ownership of the accompanying ERC721 Vault NFT, issued by the factory.
     * Owner of Vault NFT = owner of vault
     */
    function transferOwnership(address newOwner) external onlyFactory {
        if (newOwner == address(0)) {
            revert("V_TO: INVALID_RECIPIENT");
        }
        _transferOwnership(newOwner);
    }

    /**
     * @notice Transfers ownership of the contract to a new account (`newOwner`).
     * @param newOwner The new owner of the Vault.
     */
    function _transferOwnership(address newOwner) internal {
        owner = newOwner;

        //Event emitted by Factory.
    }

    /* ///////////////////////////////////////////////////////////////
                        BASE CURRENCY LOGIC
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Sets the baseCurrency of a vault.
     * @param baseCurrency_ the new baseCurrency for the vault.
     * @dev First checks if there is no trusted creditor set,
     * if there is none set, then a new baseCurrency is set.
     */
    function setBaseCurrency(address baseCurrency_) external onlyOwner {
        require(!isTrustedCreditorSet, "V_SBC: Trusted Creditor Set");
        _setBaseCurrency(baseCurrency_);
    }

    /**
     * @notice Internal function: sets baseCurrency.
     * @param baseCurrency_ the new baseCurrency for the vault.
     */
    function _setBaseCurrency(address baseCurrency_) internal {
        require(IMainRegistry(registry).isBaseCurrency(baseCurrency_), "V_SBC: baseCurrency not found");
        baseCurrency = baseCurrency_;

        emit BaseCurrencySet(baseCurrency_);
    }

    /* ///////////////////////////////////////////////////////////////
                    MARGIN ACCOUNT SETTINGS
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Opens a margin account on the vault for a trusted Creditor.
     * @param creditor The contract address of the trusted Creditor.
     * @dev Currently only one trusted Creditor can be set
     * (we are working towards a single account for multiple creditors tho!).
     * @dev Only open margin accounts for protocols you trust!
     * The Creditor should be trusted by the Vault Owner, but not by any of the Arcadia-vault smart contracts.
     * TrustedProtocol and Liquidator will never be called from an Arcadia Contract with a function that can modify state.
     * @dev The creditor has significant authorisation: use margin, trigger liquidation, and manage assets.
     */
    function openTrustedMarginAccount(address creditor) external onlyOwner {
        require(!isTrustedCreditorSet, "V_OTMA: ALREADY SET");

        //openMarginAccount() is a view function, cannot modify state.
        (bool success, address baseCurrency_, address liquidator_, uint256 fixedLiquidationCost_) =
            ITrustedCreditor(creditor).openMarginAccount(vaultVersion);
        require(success, "V_OTMA: Invalid Version");

        liquidator = liquidator_;
        trustedCreditor = creditor;
        fixedLiquidationCost = uint96(fixedLiquidationCost_);
        if (baseCurrency != baseCurrency_) {
            _setBaseCurrency(baseCurrency_);
        }
        isTrustedCreditorSet = true;

        emit TrustedMarginAccountChanged(creditor, liquidator_);
    }

    /**
     * @notice Closes the margin account on the vault of the trusted application..
     * @dev Currently only one trusted creditor can be set.
     */
    function closeTrustedMarginAccount() external onlyOwner {
        require(isTrustedCreditorSet, "V_CTMA: NOT SET");
        //getOpenPosition() is a view function, cannot modify state.
        require(ITrustedCreditor(trustedCreditor).getOpenPosition(address(this)) == 0, "V_CTMA: NON-ZERO OPEN POSITION");

        isTrustedCreditorSet = false;
        trustedCreditor = address(0);
        liquidator = address(0);
        fixedLiquidationCost = 0;

        emit TrustedMarginAccountChanged(address(0), address(0));
    }

    /* ///////////////////////////////////////////////////////////////
                          MARGIN REQUIREMENTS
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Checks if the Vault is healthy and still has free margin.
     * @param debtIncrease The amount with which the debt is increased.
     * @param totalOpenDebt The total open Debt against the Vault.
     * @return success Boolean indicating if there is sufficient margin to back a certain amount of Debt.
     * @return trustedCreditor_ The contract address of the trusted creditor.
     * @return vaultVersion_ The vault version.
     * @dev A Vault is healthy if the Collateral value is bigger than or equal to the Used Margin.
     * @dev Only one of the values can be non-zero, or we check on a certain increase of debt, or we check on a total amount of debt.
     * @dev If both values are zero, we check if the vault is currently healthy.
     */
    function isVaultHealthy(uint256 debtIncrease, uint256 totalOpenDebt)
        external
        view
        returns (bool success, address trustedCreditor_, uint256 vaultVersion_)
    {
        if (totalOpenDebt > 0) {
            //Check if vault is healthy for a given amount of openDebt.
            //The total Used margin equals the sum of the given amount of openDebt and the gas cost to liquidate.
            success = getCollateralValue() >= totalOpenDebt + fixedLiquidationCost;
        } else {
            //Check if vault is still healthy after an increase of debt.
            //The gas cost to liquidate is already taken into account in getUsedMargin().
            success = getCollateralValue() >= getUsedMargin() + debtIncrease;
        }

        return (success, trustedCreditor, vaultVersion);
    }

    /**
     * @notice Checks if the Vault can be liquidated.
     * @return success Boolean indicating if the Vault can be liquidated.
     */
    function isVaultLiquidatable() external view returns (bool success) {
        //If usedMargin is equal to fixedLiquidationCost, the open liabilities are 0 and the Vault is never liquidatable.
        uint256 usedMargin = getUsedMargin();
        if (usedMargin > fixedLiquidationCost) {
            //A Vault can be liquidated if the Liquidation value is smaller than the Used Margin.
            success = getLiquidationValue() < usedMargin;
        }
    }

    /**
     * @notice Returns the total value (mark to market) of the vault in a specific baseCurrency
     * @param baseCurrency_ The baseCurrency to return the value in.
     * @return vaultValue Total value stored in the vault, denominated in baseCurrency.
     * @dev Fetches all stored assets with their amounts.
     * Using a specified baseCurrency, fetches the value of all assets in said baseCurrency.
     */
    function getVaultValue(address baseCurrency_) external view returns (uint256 vaultValue) {
        (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
            generateAssetData();
        vaultValue = IMainRegistry(registry).getTotalValue(assetAddresses, assetIds, assetAmounts, baseCurrency_);
    }

    /**
     * @notice Calculates the total collateral value (MTM discounted with a haircut) of the vault.
     * @return collateralValue The collateral value, returned in the decimals of the base currency.
     * @dev Returns the value denominated in the baseCurrency of the Vault.
     * @dev The collateral value of the vault is equal to the spot value of the underlying assets,
     * discounted by a haircut (the collateral factor). Since the value of
     * collateralised assets can fluctuate, the haircut guarantees that the vault
     * remains over-collateralised with a high confidence level (99,9%+). The size of the
     * haircut depends on the underlying risk of the assets in the vault, the bigger the volatility
     * or the smaller the on-chain liquidity, the bigger the haircut will be.
     */
    function getCollateralValue() public view returns (uint256 collateralValue) {
        (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
            generateAssetData();
        collateralValue =
            IMainRegistry(registry).getCollateralValue(assetAddresses, assetIds, assetAmounts, baseCurrency);
    }

    /**
     * @notice Calculates the total liquidation value (MTM discounted with a factor to account for slippage) of the vault.
     * @return liquidationValue The liquidation value, returned in the decimals of the base currency.
     * @dev Returns the value denominated in the baseCurrency of the Vault.
     * @dev The liquidation value of the vault is equal to the spot value of the underlying assets,
     * discounted by a haircut (the liquidation factor).
     * The liquidation value takes into account that not the full value of the assets can go towards
     * repaying the debt: a fraction of the value is lost due to:
     * slippage while liquidating the assets, fees for the auction initiator and a penalty to the protocol.
     */
    function getLiquidationValue() public view returns (uint256 liquidationValue) {
        (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
            generateAssetData();
        liquidationValue =
            IMainRegistry(registry).getLiquidationValue(assetAddresses, assetIds, assetAmounts, baseCurrency);
    }

    /**
     * @notice Returns the used margin of the Vault.
     * @return usedMargin The total amount of Margin that is currently in use to back liabilities.
     * @dev Used Margin is the value of the assets that is currently 'locked' to back:
     *  - All the liabilities issued against the Vault.
     *  - An additional fixed buffer to cover gas fees in case of a liquidation.
     * @dev The used margin is denominated in the baseCurrency.
     * @dev Currently only one trusted application (Arcadia Lending) can open a margin account.
     * The open liability is fetched at the contract of the application -> only allow trusted audited creditors!!!
     */
    function getUsedMargin() public view returns (uint256 usedMargin) {
        if (!isTrustedCreditorSet) return 0;

        //getOpenPosition() is a view function, cannot modify state.
        usedMargin = ITrustedCreditor(trustedCreditor).getOpenPosition(address(this)) + fixedLiquidationCost;
    }

    /**
     * @notice Calculates the remaining margin the owner of the Vault can use.
     * @return freeMargin The remaining amount of margin a user can take.
     * @dev Free Margin is the value of the assets that is still free to back additional liabilities.
     * @dev The free margin is denominated in the baseCurrency.
     */
    function getFreeMargin() public view returns (uint256 freeMargin) {
        uint256 collateralValue = getCollateralValue();
        uint256 usedMargin = getUsedMargin();

        //gas: explicit check is done to prevent underflow.
        unchecked {
            freeMargin = collateralValue > usedMargin ? collateralValue - usedMargin : 0;
        }
    }

    /* ///////////////////////////////////////////////////////////////
                          LIQUIDATION LOGIC
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Function called by Liquidator to start liquidation of the Vault.
     * @param openDebt The open debt taken by `originalOwner` at moment of liquidation at trustedCreditor
     * @return originalOwner The original owner of this vault.
     * @return baseCurrency_ The baseCurrency in which the vault is denominated.
     * @return trustedCreditor_ The account or contract that is owed the debt.
     * @dev Requires a liquidation value below Used margin.
     * @dev Transfers ownership of the Vault to the liquidator!
     */
    function liquidateVault(uint256 openDebt)
        external
        returns (address originalOwner, address baseCurrency_, address trustedCreditor_)
    {
        require(msg.sender == liquidator, "V_LV: Only Liquidator");

        //Cache trustedCreditor.
        trustedCreditor_ = trustedCreditor;

        //Close margin account.
        isTrustedCreditorSet = false;
        trustedCreditor = address(0);
        liquidator = address(0);

        //If getLiquidationValue (total value discounted with liquidation factor to account for slippage)
        //is smaller than the Used Margin: sum of the liabilities of the Vault (openDebt)
        //and the max gas cost to liquidate the vault (fixedLiquidationCost),
        //then the Vault can be successfully liquidated.
        //Liquidations are triggered by the trustedCreditor (via Liquidator), the openDebt is
        //passed as input to avoid the need of another contract call back to trustedCreditor.
        require(getLiquidationValue() < openDebt + fixedLiquidationCost, "V_LV: liqValue above usedMargin");

        //Set fixedLiquidationCost to 0 since margin account is closed.
        fixedLiquidationCost = 0;

        //Transfer ownership of the ERC721 in Factory of the Vault to the Liquidator.
        IFactory(IMainRegistry(registry).factory()).liquidate(msg.sender);

        //Transfer ownership of the Vault itself to the Liquidator.
        originalOwner = owner;
        _transferOwnership(msg.sender);

        emit TrustedMarginAccountChanged(address(0), address(0));

        return (originalOwner, baseCurrency, trustedCreditor_);
    }

    /*///////////////////////////////////////////////////////////////
                    ASSET MANAGEMENT LOGIC
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Add or remove an Asset Manager.
     * @param assetManager the address of the Asset Manager
     * @param value A boolean giving permissions to or taking permissions from an Asset manager
     * @dev Only set trusted addresses as Asset manager, Asset managers can potentially steal assets (as long as the vault position remains healthy).
     * @dev No need to set the Owner as Asset manager, owner will automatically have all permissions of an asset manager.
     * @dev Potential use-cases of the asset manager might be to:
     * - Automate actions by keeper networks,
     * - Chain interactions with the Trusted Creditor together with vault actions (eg. borrow deposit and trade in one transaction).
     */
    function setAssetManager(address assetManager, bool value) external onlyOwner {
        isAssetManager[msg.sender][assetManager] = value;

        emit AssetManagerSet(msg.sender, assetManager, value);
    }

    /**
     * @notice Calls external action handler to execute and interact with external logic.
     * @param actionHandler The address of the action handler.
     * @param actionData A bytes object containing two actionAssetData structs, an address array and a bytes array.
     * @return trustedCreditor_ The contract address of the trusted creditor.
     * @return vaultVersion_ The vault version.
     * @dev Similar to flash loans, this function optimistically calls external logic and checks for the vault state at the very end.
     * @dev vaultManagementAction can interact with and chain together any DeFi protocol to swap, stake, claim...
     * The only requirements are that the recipient tokens of the interactions are allowlisted, deposited back into the vault and
     * that the Vault is in a healthy state at the end of the transaction.
     */
    function vaultManagementAction(address actionHandler, bytes calldata actionData)
        external
        onlyAssetManager
        returns (address, uint256)
    {
        require(IMainRegistry(registry).isActionAllowed(actionHandler), "V_VMA: Action not allowed");

        (ActionData memory outgoing,,,) = abi.decode(actionData, (ActionData, ActionData, address[], bytes[]));

        // Withdraw assets to actionHandler.
        _withdraw(outgoing.assets, outgoing.assetIds, outgoing.assetAmounts, actionHandler);

        // Execute Action(s).
        ActionData memory incoming = IActionBase(actionHandler).executeAction(actionData);

        // Deposit assets from actionHandler into vault.
        _deposit(incoming.assets, incoming.assetIds, incoming.assetAmounts, actionHandler);

        //If usedMargin is equal to fixedLiquidationCost, the open liabilities are 0 and the Vault is always in a healthy state.
        uint256 usedMargin = getUsedMargin();
        if (usedMargin > fixedLiquidationCost) {
            //Vault must be healthy after actions are executed.
            require(getCollateralValue() >= usedMargin, "V_VMA: Vault Unhealthy");
        }

        return (trustedCreditor, vaultVersion);
    }

    /* ///////////////////////////////////////////////////////////////
                    ASSET DEPOSIT/WITHDRAWN LOGIC
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Deposits assets into the Vault.
     * @param assetAddresses Array of the contract addresses of the assets.
     * One address for each asset to be deposited, even if multiple assets of the same contract address are deposited.
     * @param assetIds Array of the IDs of the assets.
     * When depositing an ERC20 token, this will be disregarded, HOWEVER a value (eg. 0) must be set in the array!
     * @param assetAmounts Array with the amounts of the assets.
     * When depositing an ERC721 token, this will be disregarded, HOWEVER a value (eg. 1) must be set in the array!
     * @dev All arrays should be of same length, each index in each array corresponding
     * to the same asset that will get deposited. If multiple asset IDs of the same contract address
     * are deposited, the assetAddress must be repeated in assetAddresses.
     * Example inputs:
     * [wETH, DAI, BAYC, Interleave], [0, 0, 15, 2], [10**18, 10**18, 1, 100], [0, 0, 1, 2]
     * [Interleave, Interleave, BAYC, BAYC, wETH], [3, 5, 16, 17, 0], [123, 456, 1, 1, 10**18], [2, 2, 1, 1, 0]
     */
    function deposit(address[] calldata assetAddresses, uint256[] calldata assetIds, uint256[] calldata assetAmounts)
        external
        onlyOwner
    {
        //No need to check that all arrays have equal length, this check is already done in the MainRegistry.
        _deposit(assetAddresses, assetIds, assetAmounts, msg.sender);
    }

    /**
     * @notice Deposits assets into the Vault.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @param assetIds Array of the IDs of the assets.
     * @param assetAmounts Array with the amounts of the assets.
     * @param from The address to withdraw the assets from.
     */
    function _deposit(
        address[] memory assetAddresses,
        uint256[] memory assetIds,
        uint256[] memory assetAmounts,
        address from
    ) internal {
        //Reverts in mainRegistry if input is invalid.
        uint256[] memory assetTypes =
            IMainRegistry(registry).batchProcessDeposit(assetAddresses, assetIds, assetAmounts);

        uint256 assetAddressesLength = assetAddresses.length;
        for (uint256 i; i < assetAddressesLength;) {
            if (assetAmounts[i] == 0) {
                //Skip if amount is 0 to prevent storing addresses that have 0 balance.
                unchecked {
                    ++i;
                }
                continue;
            }

            if (assetTypes[i] == 0) {
                _depositERC20(from, assetAddresses[i], assetAmounts[i]);
            } else if (assetTypes[i] == 1) {
                _depositERC721(from, assetAddresses[i], assetIds[i]);
            } else if (assetTypes[i] == 2) {
                _depositERC1155(from, assetAddresses[i], assetIds[i], assetAmounts[i]);
            } else {
                revert("V_D: Unknown asset type");
            }
            unchecked {
                ++i;
            }
        }

        require(erc20Stored.length + erc721Stored.length + erc1155Stored.length <= ASSET_LIMIT, "V_D: Too many assets");
    }

    /**
     * @notice Withdrawals assets from the Vault to the owner.
     * @param assetAddresses Array of the contract addresses of the assets.
     * One address for each asset to be withdrawn, even if multiple assets of the same contract address are withdrawn.
     * @param assetIds Array of the IDs of the assets.
     * When withdrawing an ERC20 token, this will be disregarded, HOWEVER a value (eg. 0) must be set in the array!
     * @param assetAmounts Array with the amounts of the assets.
     * When withdrawing an ERC721 token, this will be disregarded, HOWEVER a value (eg. 1) must be set in the array!
     * @dev All arrays should be of same length, each index in each array corresponding
     * to the same asset that will get withdrawn. If multiple asset IDs of the same contract address
     * are to be withdrawn, the assetAddress must be repeated in assetAddresses.
     * Example inputs:
     * [wETH, DAI, BAYC, Interleave], [0, 0, 15, 2], [10**18, 10**18, 1, 100], [0, 0, 1, 2]
     * [Interleave, Interleave, BAYC, BAYC, wETH], [3, 5, 16, 17, 0], [123, 456, 1, 1, 10**18], [2, 2, 1, 1, 0]
     * @dev Will fail if the value is in an unhealthy state after withdrawal (collateral value is smaller than the Used Margin).
     * If no debt is taken yet on this Vault, users are free to withdraw any asset at any time.
     */
    function withdraw(address[] calldata assetAddresses, uint256[] calldata assetIds, uint256[] calldata assetAmounts)
        external
        onlyOwner
    {
        //No need to check that all arrays have equal length, this check is already done in the MainRegistry.
        _withdraw(assetAddresses, assetIds, assetAmounts, msg.sender);

        uint256 usedMargin = getUsedMargin();
        //If usedMargin is equal to fixedLiquidationCost, the open liabilities are 0 and all assets can be withdrawn.
        if (usedMargin > fixedLiquidationCost) {
            //Vault must be healthy after assets are withdrawn.
            require(getCollateralValue() >= usedMargin, "V_W: Vault Unhealthy");
        }
    }

    /**
     * @notice Withdrawals assets from the Vault to the owner.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @param assetIds Array of the IDs of the assets.
     * @param assetAmounts Array with the amounts of the assets.
     * @param to The address to withdraw to.
     */

    function _withdraw(
        address[] memory assetAddresses,
        uint256[] memory assetIds,
        uint256[] memory assetAmounts,
        address to
    ) internal {
        //Reverts in mainRegistry if input is invalid.
        uint256[] memory assetTypes =
            IMainRegistry(registry).batchProcessWithdrawal(assetAddresses, assetIds, assetAmounts); //reverts in mainregistry if invalid input

        uint256 assetAddressesLength = assetAddresses.length;
        for (uint256 i; i < assetAddressesLength;) {
            if (assetAmounts[i] == 0) {
                //Skip if amount is 0 to prevent transferring 0 balances.
                unchecked {
                    ++i;
                }
                continue;
            }

            if (assetTypes[i] == 0) {
                _withdrawERC20(to, assetAddresses[i], assetAmounts[i]);
            } else if (assetTypes[i] == 1) {
                _withdrawERC721(to, assetAddresses[i], assetIds[i]);
            } else if (assetTypes[i] == 2) {
                _withdrawERC1155(to, assetAddresses[i], assetIds[i], assetAmounts[i]);
            } else {
                require(false, "V_W: Unknown asset type");
            }
            unchecked {
                ++i;
            }
        }
    }

    /**
     * @notice Internal function to deposit ERC20 tokens.
     * @param from Address the tokens should be transferred from. This address must have approved the Vault.
     * @param ERC20Address The contract address of the asset.
     * @param amount The amount of ERC20 tokens.
     * @dev Used for all tokens type == 0.
     * @dev If the token has not yet been deposited, the ERC20 token address is stored.
     */
    function _depositERC20(address from, address ERC20Address, uint256 amount) internal {
        ERC20(ERC20Address).safeTransferFrom(from, address(this), amount);

        uint256 currentBalance = erc20Balances[ERC20Address];

        if (currentBalance == 0) {
            erc20Stored.push(ERC20Address);
        }

        unchecked {
            erc20Balances[ERC20Address] += amount;
        }
    }

    /**
     * @notice Internal function to deposit ERC721 tokens.
     * @param from Address the tokens should be transferred from. This address must have approved the Vault.
     * @param ERC721Address The contract address of the asset.
     * @param id The ID of the ERC721 token.
     * @dev Used for all tokens type == 1.
     * @dev After successful transfer, the function pushes the ERC721 address to the stored token and stored ID array.
     * This may cause duplicates in the ERC721 stored addresses array, but this is intended.
     */
    function _depositERC721(address from, address ERC721Address, uint256 id) internal {
        IERC721(ERC721Address).safeTransferFrom(from, address(this), id);

        erc721Stored.push(ERC721Address);
        erc721TokenIds.push(id);
    }

    /**
     * @notice Internal function to deposit ERC1155 tokens.
     * @param from The Address the tokens should be transferred from. This address must have approved the Vault.
     * @param ERC1155Address The contract address of the asset.
     * @param id The ID of the ERC1155 tokens.
     * @param amount The amount of ERC1155 tokens.
     * @dev Used for all tokens type == 2.
     * @dev After successful transfer, the function checks whether the combination of address & ID has already been stored.
     * If not, the function pushes the new address and ID to the stored arrays.
     * This may cause duplicates in the ERC1155 stored addresses array, this is intended.
     */
    function _depositERC1155(address from, address ERC1155Address, uint256 id, uint256 amount) internal {
        IERC1155(ERC1155Address).safeTransferFrom(from, address(this), id, amount, "");

        uint256 currentBalance = erc1155Balances[ERC1155Address][id];

        if (currentBalance == 0) {
            erc1155Stored.push(ERC1155Address);
            erc1155TokenIds.push(id);
        }

        unchecked {
            erc1155Balances[ERC1155Address][id] += amount;
        }
    }

    /**
     * @notice Internal function to withdraw ERC20 tokens.
     * @param to Address the tokens should be sent to.
     * @param ERC20Address The contract address of the asset.
     * @param amount The amount of ERC20 tokens.
     * @dev Used for all tokens type == 0.
     * @dev The function checks whether the Vault has any leftover balance of said asset.
     * If not, it will pop() the ERC20 asset address from the stored addresses array.
     * Note: this shifts the order of erc20Stored!
     * @dev This check is done using a loop:
     * gas usage of writing it in a mapping vs extra loops is in favor of extra loops in this case.
     */
    function _withdrawERC20(address to, address ERC20Address, uint256 amount) internal {
        erc20Balances[ERC20Address] -= amount;

        if (erc20Balances[ERC20Address] == 0) {
            uint256 erc20StoredLength = erc20Stored.length;

            if (erc20StoredLength == 1) {
                // There was only one ERC20 stored on the contract, safe to remove from array.
                erc20Stored.pop();
            } else {
                for (uint256 i; i < erc20StoredLength;) {
                    if (erc20Stored[i] == ERC20Address) {
                        erc20Stored[i] = erc20Stored[erc20StoredLength - 1];
                        erc20Stored.pop();
                        break;
                    }
                    unchecked {
                        ++i;
                    }
                }
            }
        }

        ERC20(ERC20Address).safeTransfer(to, amount);
    }

    /**
     * @notice Internal function to withdraw ERC721 tokens.
     * @param to Address the tokens should be sent to.
     * @param ERC721Address The contract address of the asset.
     * @param id The ID of the ERC721 token.
     * @dev Used for all tokens type == 1.
     * @dev The function checks whether any other ERC721 is deposited in the Vault.
     * If not, it pops the stored addresses and stored IDs (pop() of two arrays is 180 gas cheaper than deleting).
     * If there are, it loops through the stored arrays and searches the ID that's withdrawn,
     * then replaces it with the last index, followed by a pop().
     * @dev Sensitive to ReEntrance attacks! SafeTransferFrom therefore done at the end of the function.
     */
    function _withdrawERC721(address to, address ERC721Address, uint256 id) internal {
        uint256 tokenIdLength = erc721TokenIds.length;

        uint256 i;
        if (tokenIdLength == 1) {
            //There was only one ERC721 stored on the contract, safe to remove both lists.
            require(erc721TokenIds[0] == id && erc721Stored[0] == ERC721Address, "V_W721: Unknown asset");
            erc721TokenIds.pop();
            erc721Stored.pop();
        } else {
            for (i; i < tokenIdLength;) {
                if (erc721TokenIds[i] == id && erc721Stored[i] == ERC721Address) {
                    erc721TokenIds[i] = erc721TokenIds[tokenIdLength - 1];
                    erc721TokenIds.pop();
                    erc721Stored[i] = erc721Stored[tokenIdLength - 1];
                    erc721Stored.pop();
                    break;
                }
                unchecked {
                    ++i;
                }
            }
            //For loop should break, otherwise we never went into the if-branch, meaning the token being withdrawn
            //is unknown and not properly deposited.
            require(i < tokenIdLength, "V_W721: Unknown asset");
        }

        IERC721(ERC721Address).safeTransferFrom(address(this), to, id);
    }

    /**
     * @notice Internal function to withdraw ERC1155 tokens.
     * @param to Address the tokens should be sent to.
     * @param ERC1155Address The contract address of the asset.
     * @param id The ID of the ERC1155 tokens.
     * @param amount The amount of ERC1155 tokens.
     * @dev Used for all tokens types = 2.
     * @dev After successful transfer, the function checks whether there is any balance left for that ERC1155.
     * If there is, it simply transfers the tokens.
     * If not, it checks whether it can pop() (used for gas savings vs delete) the stored arrays.
     * If there are still other ERC1155's on the contract, it looks for the ID and token address to be withdrawn
     * and then replaces it with the last index, followed by a pop().
     * @dev Sensitive to ReEntrance attacks! SafeTransferFrom therefore done at the end of the function.
     */
    function _withdrawERC1155(address to, address ERC1155Address, uint256 id, uint256 amount) internal {
        uint256 tokenIdLength = erc1155TokenIds.length;

        erc1155Balances[ERC1155Address][id] -= amount;

        if (erc1155Balances[ERC1155Address][id] == 0) {
            if (tokenIdLength == 1) {
                erc1155TokenIds.pop();
                erc1155Stored.pop();
            } else {
                for (uint256 i; i < tokenIdLength;) {
                    if (erc1155TokenIds[i] == id) {
                        if (erc1155Stored[i] == ERC1155Address) {
                            erc1155TokenIds[i] = erc1155TokenIds[tokenIdLength - 1];
                            erc1155TokenIds.pop();
                            erc1155Stored[i] = erc1155Stored[tokenIdLength - 1];
                            erc1155Stored.pop();
                            break;
                        }
                    }
                    unchecked {
                        ++i;
                    }
                }
            }
        }

        IERC1155(ERC1155Address).safeTransferFrom(address(this), to, id, amount, "");
    }

    /**
     * @notice Skims non-deposited assets from the Vault.
     * @param token The contract address of the asset.
     * @param id The ID of the asset.
     * @param type_ The asset type of the asset.
     * @dev Function can retrieve assets that were transferred to the Vault but not deposited.
     * or can be used to claim yield for rebasing tokens.
     */
    function skim(address token, uint256 id, uint256 type_) public {
        require(msg.sender == owner, "V_S: Only owner can skim");

        if (token == address(0)) {
            payable(owner).transfer(address(this).balance);
            return;
        }

        if (type_ == 0) {
            uint256 balance = ERC20(token).balanceOf(address(this));
            uint256 balanceStored = erc20Balances[token];
            if (balance > balanceStored) {
                ERC20(token).safeTransfer(owner, balance - balanceStored);
            }
        } else if (type_ == 1) {
            bool isStored;
            uint256 erc721StoredLength = erc721Stored.length;
            for (uint256 i; i < erc721StoredLength;) {
                if (erc721Stored[i] == token && erc721TokenIds[i] == id) {
                    isStored = true;
                    break;
                }
                unchecked {
                    ++i;
                }
            }

            if (!isStored) {
                IERC721(token).safeTransferFrom(address(this), owner, id);
            }
        } else if (type_ == 2) {
            uint256 balance = IERC1155(token).balanceOf(address(this), id);
            uint256 balanceStored = erc1155Balances[token][id];

            if (balance > balanceStored) {
                IERC1155(token).safeTransferFrom(address(this), owner, id, balance - balanceStored, "");
            }
        }
    }

    /* ///////////////////////////////////////////////////////////////
                        HELPER FUNCTIONS
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Generates three arrays of all the stored assets in the Vault.
     * @return assetAddresses Array of the contract addresses of the assets.
     * @return assetIds Array of the IDs of the assets.
     * @return assetAmounts Array with the amounts of the assets.
     * @dev Balances are stored on the contract to prevent working around the deposit limits.
     * @dev Loops through the stored asset addresses and fills the arrays.
     * @dev There is no importance of the order in the arrays, but all indexes of the arrays correspond to the same asset.
     */
    function generateAssetData()
        public
        view
        returns (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts)
    {
        uint256 totalLength;
        unchecked {
            totalLength = erc20Stored.length + erc721Stored.length + erc1155Stored.length;
        } //Cannot realistically overflow. No max(uint256) contracts deployed.
        assetAddresses = new address[](totalLength);
        assetIds = new uint256[](totalLength);
        assetAmounts = new uint256[](totalLength);

        uint256 i;
        uint256 erc20StoredLength = erc20Stored.length;
        address cacheAddr;
        for (; i < erc20StoredLength;) {
            cacheAddr = erc20Stored[i];
            assetAddresses[i] = cacheAddr;
            //assetIds[i] = 0; //gas: no need to store 0, index will continue anyway.
            assetAmounts[i] = erc20Balances[cacheAddr];
            unchecked {
                ++i;
            }
        }

        uint256 j;
        uint256 erc721StoredLength = erc721Stored.length;
        for (; j < erc721StoredLength;) {
            cacheAddr = erc721Stored[j];
            assetAddresses[i] = cacheAddr;
            assetIds[i] = erc721TokenIds[j];
            assetAmounts[i] = 1;
            unchecked {
                ++i;
            }
            unchecked {
                ++j;
            }
        }

        uint256 k;
        uint256 erc1155StoredLength = erc1155Stored.length;
        uint256 cacheId;
        for (; k < erc1155StoredLength;) {
            cacheAddr = erc1155Stored[k];
            cacheId = erc1155TokenIds[k];
            assetAddresses[i] = cacheAddr;
            assetIds[i] = cacheId;
            assetAmounts[i] = erc1155Balances[cacheAddr][cacheId];
            unchecked {
                ++i;
            }
            unchecked {
                ++k;
            }
        }
    }

    function onERC721Received(address, address, uint256, bytes calldata) public pure returns (bytes4) {
        return this.onERC721Received.selector;
    }

    function onERC1155Received(address, address, uint256, uint256, bytes calldata) public pure returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    fallback() external {
        revert();
    }
}

File 2 of 12 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 3 of 12 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

    /*//////////////////////////////////////////////////////////////
                            ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool success;

        /// @solidity memory-safe-assembly
        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}

File 4 of 12 : IOraclesHub.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.13;

interface IOraclesHub {
    /**
     * @notice Verifies whether a sequence of oracles complies with a predetermined set of criteria.
     * @param oracles Array of contract addresses of oracles.
     * @param asset The contract address of the base-asset.
     */
    function checkOracleSequence(address[] memory oracles, address asset) external view;

    /**
     * @notice Returns the state of an oracle.
     * @param oracle The contract address of the oracle to be checked.
     * @return boolean indicating if the oracle is active or not.
     */
    function isActive(address oracle) external view returns (bool);

    /**
     * @notice Returns the rate of a certain asset, denominated in USD or in another BaseCurrency.
     * @param oracles Array of contract addresses of oracles.
     * @param baseCurrency The BaseCurrency in which the rate is ideally expressed.
     * @return rateInUsd The rate of the asset denominated in USD, with 18 Decimals precision.
     * @return rateInBaseCurrency The rate of the asset denominated in a BaseCurrency different from USD, with 18 Decimals precision.
     */
    function getRate(address[] memory oracles, uint256 baseCurrency) external view returns (uint256, uint256);
}

File 5 of 12 : ActionData.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.13;

// Struct with information to pass to and from actionHandlers.
struct ActionData {
    address[] assets; // Array of the contract addresses of the assets.
    uint256[] assetIds; // Array of the IDs of the assets.
    uint256[] assetAmounts; // Array with the amounts of the assets.
    uint256[] assetTypes; // Array with the types of the assets.
    uint256[] actionBalances; // Array with the balances of the actionHandler.
}

File 6 of 12 : IActionBase.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.13;

import "../actions/utils/ActionData.sol";

interface IActionBase {
    /**
     * @notice Calls a series of addresses with arbitrary calldata.
     * @param actionData A bytes object containing two actionAssetData structs, an address array and a bytes array.
     * @return resultData An actionAssetData struct with the balances of this ActionMultiCall address.
     */
    function executeAction(bytes calldata actionData) external returns (ActionData memory);
}

File 7 of 12 : IERC1155.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IERC1155 {
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;

    function balanceOf(address account, uint256 id) external view returns (uint256);
}

File 8 of 12 : IERC721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

interface IERC721 {
    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    function transferFrom(address from, address to, uint256 id) external;
}

File 9 of 12 : IFactory.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.13;

interface IFactory {
    /**
     * @notice Checks if a contract is a Vault.
     * @param vault The contract address of the Vault.
     * @return bool indicating if the address is a vault or not.
     */
    function isVault(address vault) external view returns (bool);

    /**
     * @notice Function used to transfer a vault between users.
     * @param from The sender.
     * @param to The target.
     * @param vault The address of the vault that is transferred.
     */
    function safeTransferFrom(address from, address to, address vault) external;

    /**
     * @notice Function called by a Vault at the start of a liquidation to transfer ownership to the Liquidator contract.
     * @param liquidator The contract address of the liquidator.
     */
    function liquidate(address liquidator) external;
}

File 10 of 12 : IMainRegistry.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.13;

interface IMainRegistry {
    /**
     * @notice Returns the number of baseCurrencies.
     * @return Counter for the number of baseCurrencies in use.
     */
    function baseCurrencyCounter() external view returns (uint256);

    /**
     * @notice Returns the Factory address.
     * @return factory The contract address of the Factory.
     */
    function factory() external view returns (address);

    /**
     * @notice Returns the contract address of a baseCurrency.
     * @param index The index of the baseCurrency in the array baseCurrencies.
     * @return baseCurrency The contract address of a baseCurrency.
     */
    function baseCurrencies(uint256 index) external view returns (address);

    /**
     * @notice Checks if a contract is a baseCurrency.
     * @param baseCurrency The contract address of the baseCurrency.
     * @return boolean.
     */
    function isBaseCurrency(address baseCurrency) external view returns (bool);

    /**
     * @notice Checks if an action is allowed.
     * @param action The contract address of the action.
     * @return boolean.
     */
    function isActionAllowed(address action) external view returns (bool);

    /**
     * @notice Batch deposit multiple assets.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @param assetIds Array of the IDs of the assets.
     * @param amounts Array with the amounts of the assets.
     * @return assetTypes Array with the types of the assets.
     * 0 = ERC20.
     * 1 = ERC721.
     * 2 = ERC1155.
     */
    function batchProcessDeposit(
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata amounts
    ) external returns (uint256[] memory);

    /**
     * @notice Batch withdraw multiple assets.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @param assetIds Array of the IDs of the assets.
     * @param amounts Array with the amounts of the assets.
     * @return assetTypes Array with the types of the assets.
     * 0 = ERC20.
     * 1 = ERC721.
     * 2 = ERC1155.
     */
    function batchProcessWithdrawal(
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata amounts
    ) external returns (uint256[] memory);

    /**
     * @notice Calculates the combined value of a combination of assets, denominated in a given BaseCurrency.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @param assetIds Array of the IDs of the assets.
     * @param assetAmounts Array with the amounts of the assets.
     * @param baseCurrency The contract address of the BaseCurrency.
     * @return valueInBaseCurrency The combined value of the assets, denominated in BaseCurrency.
     */
    function getTotalValue(
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata assetAmounts,
        address baseCurrency
    ) external view returns (uint256);

    /**
     * @notice Calculates the collateralValue of a combination of assets, denominated in a given BaseCurrency.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @param assetIds Array of the IDs of the assets.
     * @param assetAmounts Array with the amounts of the assets.
     * @param baseCurrency The contract address of the BaseCurrency.
     * @return collateralValue The collateral value of the assets, denominated in BaseCurrency.
     */
    function getCollateralValue(
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata assetAmounts,
        address baseCurrency
    ) external view returns (uint256);

    /**
     * @notice Calculates the getLiquidationValue of a combination of assets, denominated in a given BaseCurrency.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @param assetIds Array of the IDs of the assets.
     * @param assetAmounts Array with the amounts of the assets.
     * @param baseCurrency The contract address of the BaseCurrency.
     * @return liquidationValue The liquidation value of the assets, denominated in BaseCurrency.
     */
    function getLiquidationValue(
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata assetAmounts,
        address baseCurrency
    ) external view returns (uint256);
}

File 11 of 12 : ITrustedCreditor.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.13;

interface ITrustedCreditor {
    /**
     * @notice Checks if vault fulfills all requirements and returns application settings.
     * @param vaultVersion The current version of the vault.
     * @return success Bool indicating if all requirements are met.
     * @return baseCurrency The base currency of the application.
     * @return liquidator The liquidator of the application.
     * @return fixedLiquidationCost Estimated fixed costs (independent of size of debt) to liquidate a position.
     */
    function openMarginAccount(uint256 vaultVersion) external view returns (bool, address, address, uint256);

    /**
     * @notice Returns the open position of the vault.
     * @param vault The vault address.
     * @return openPosition The open position of the vault.
     */
    function getOpenPosition(address vault) external view returns (uint256);
}

File 12 of 12 : IVault.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.13;

interface IVault {
    /**
     * @notice Returns the Vault version.
     * @return version The Vault version.
     */
    function vaultVersion() external view returns (uint16);

    /**
     * @notice Initiates the variables of the vault.
     * @param owner The sender of the 'createVault' on the factory
     * @param registry The 'beacon' contract with the external logic.
     * @param vaultVersion The version of the vault logic.
     * @param baseCurrency The Base-currency in which the vault is denominated.
     */
    function initialize(address owner, address registry, uint16 vaultVersion, address baseCurrency) external;

    /**
     * @notice Updates the vault version and stores a new address in the EIP1967 implementation slot.
     * @param newImplementation The contract with the new vault logic.
     * @param newRegistry The MainRegistry for this specific implementation (might be identical as the old registry).
     * @param data Arbitrary data, can contain instructions to execute when updating Vault to new logic.
     * @param newVersion The new version of the vault logic.
     */
    function upgradeVault(address newImplementation, address newRegistry, uint16 newVersion, bytes calldata data)
        external;

    /**
     * @notice Transfers ownership of the contract to a new account.
     * @param newOwner The new owner of the Vault.
     */
    function transferOwnership(address newOwner) external;

    /**
     * @notice Function called by Liquidator to start liquidation of the Vault.
     * @param openDebt The open debt taken by `originalOwner` at moment of liquidation at trustedCreditor
     * @return originalOwner The original owner of this vault.
     * @return baseCurrency The baseCurrency in which the vault is denominated.
     * @return trustedCreditor The account or contract that is owed the debt.
     */
    function liquidateVault(uint256 openDebt) external returns (address, address, address);
}

Settings
{
  "remappings": [
    "arcadia-lending/=lib/arcadia-lending/",
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "solmate/=lib/solmate/src/",
    "v2-periphery/=lib/v2-periphery/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"assetManager","type":"address"},{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"AssetManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"baseCurrency","type":"address"}],"name":"BaseCurrencySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"protocol","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"}],"name":"TrustedMarginAccountChanged","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"ASSET_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseCurrency","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"closeTrustedMarginAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"erc1155Balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"erc1155Stored","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"erc1155TokenIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"erc20Balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"erc20Stored","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"erc721Stored","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"erc721TokenIds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fixedLiquidationCost","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"generateAssetData","outputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollateralValue","outputs":[{"internalType":"uint256","name":"collateralValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFreeMargin","outputs":[{"internalType":"uint256","name":"freeMargin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidationValue","outputs":[{"internalType":"uint256","name":"liquidationValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsedMargin","outputs":[{"internalType":"uint256","name":"usedMargin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"baseCurrency_","type":"address"}],"name":"getVaultValue","outputs":[{"internalType":"uint256","name":"vaultValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"registry_","type":"address"},{"internalType":"uint16","name":"vaultVersion_","type":"uint16"},{"internalType":"address","name":"baseCurrency_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isAssetManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isTrustedCreditorSet","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"debtIncrease","type":"uint256"},{"internalType":"uint256","name":"totalOpenDebt","type":"uint256"}],"name":"isVaultHealthy","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"address","name":"trustedCreditor_","type":"address"},{"internalType":"uint256","name":"vaultVersion_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isVaultLiquidatable","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"openDebt","type":"uint256"}],"name":"liquidateVault","outputs":[{"internalType":"address","name":"originalOwner","type":"address"},{"internalType":"address","name":"baseCurrency_","type":"address"},{"internalType":"address","name":"trustedCreditor_","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"creditor","type":"address"}],"name":"openTrustedMarginAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetManager","type":"address"},{"internalType":"bool","name":"value","type":"bool"}],"name":"setAssetManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"baseCurrency_","type":"address"}],"name":"setBaseCurrency","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"type_","type":"uint256"}],"name":"skim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedCreditor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oldImplementation","type":"address"},{"internalType":"address","name":"oldRegistry","type":"address"},{"internalType":"uint16","name":"oldVersion","type":"uint16"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"address","name":"newRegistry","type":"address"},{"internalType":"uint16","name":"newVersion","type":"uint16"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"actionHandler","type":"address"},{"internalType":"bytes","name":"actionData","type":"bytes"}],"name":"vaultManagementAction","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"vaultVersion","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50600180546001600160601b0316336c0100000000000000000000000002179055614017806100406000396000f3fe608060405234801561001057600080fd5b50600436106102485760003560e01c80638e33f5ee1161013b578063b8eabab0116100b8578063ebbbbc831161007c578063ebbbbc83146105fc578063edfe1faf14610604578063f23a6e611461060c578063f2fde38b1461062c578063f60935301461063f57600080fd5b8063b8eabab0146105b3578063bfed6b03146105c6578063c047e563146105ce578063cfdb12db146105d6578063d80359f1146105e957600080fd5b80639ea1a85d116100ff5780639ea1a85d1461053c578063adb33ab61461054f578063b1ffa5821461057a578063b478f4161461058d578063b6f70e76146105a057600080fd5b80638e33f5ee146104a6578063907a9429146104d157806390cf0bba146104d957806392a85fde146105165780639566433a1461052957600080fd5b80635a9c6128116101c957806371a84c711161018d57806371a84c711461042e5780637a1d16951461045c5780637b103999146104715780638d3a47ca146104845780638da5cb5b1461048c57600080fd5b80635a9c6128146103c5578063610600c8146103d85780636208e930146103eb578063656e8729146104085780636b8798d71461041b57600080fd5b806342c8d7151161021057806342c8d7151461033d57806345a50a80146103455780634a1ec375146103585780634d401a361461036d5780634e7adf2c1461039f57600080fd5b806308f1bdaa1461024d578063150b7a021461026d57806317e62b67146102a557806321ab889d146102dd5780634046ebae1461030b575b600080fd5b610255610652565b6040516102649392919061334d565b60405180910390f35b61028c61027b3660046133ed565b630a85bd0160e11b95945050505050565b6040516001600160e01b03199091168152602001610264565b6102b86102b336600461345f565b6109ea565b6040805193151584526001600160a01b03909216602084015290820152606001610264565b6102fd6102eb366004613481565b600a6020526000908152604090205481565b604051908152602001610264565b60005461032590630100000090046001600160a01b031681565b6040516001600160a01b039091168152602001610264565b6102fd610a60565b600354610325906001600160a01b031681565b61036b6103663660046134a5565b610afb565b005b61038061037b3660046134da565b610e7f565b604080516001600160a01b039093168352602083019190915201610264565b6000546103b290610100900461ffff1681565b60405161ffff9091168152602001610264565b6103256103d336600461352e565b611138565b61036b6103e636600461355e565b611162565b6000546103f89060ff1681565b6040519015158152602001610264565b61036b6104163660046135fe565b6113d9565b61032561042936600461352e565b6114b2565b6103f861043c366004613697565b600c60209081526000928352604080842090915290825290205460ff1681565b61036b61046a36600461355e565b5050505050565b600254610325906001600160a01b031681565b61036b6114c2565b60015461032590600160601b90046001600160a01b031681565b6001546104b9906001600160601b031681565b6040516001600160601b039091168152602001610264565b6102fd61164e565b6104ec6104e736600461352e565b6116e8565b604080516001600160a01b0394851681529284166020840152921691810191909152606001610264565b600454610325906001600160a01b031681565b6102fd610537366004613481565b61192e565b6102fd61054a36600461352e565b6119c2565b6102fd61055d3660046136d0565b600b60209081526000928352604080842090915290825290205481565b61036b6105883660046136fc565b6119e3565b61036b61059b366004613481565b611b49565b6102fd6105ae36600461352e565b611bd9565b61036b6105c13660046135fe565b611be9565b6102fd600f81565b6102fd611d31565b6103256105e436600461352e565b611d82565b61036b6105f7366004613764565b611d92565b6103f8611e2f565b6102fd611e61565b61028c61061a366004613792565b63f23a6e6160e01b9695505050505050565b61036b61063a366004613481565b611e93565b61036b61064d366004613481565b611fd5565b600754600654600554606092839283920101806001600160401b0381111561067c5761067c6137fb565b6040519080825280602002602001820160405280156106a5578160200160208202803683370190505b509350806001600160401b038111156106c0576106c06137fb565b6040519080825280602002602001820160405280156106e9578160200160208202803683370190505b509250806001600160401b03811115610704576107046137fb565b60405190808252806020026020018201604052801561072d578160200160208202803683370190505b50600554909250600090815b818310156107f6576005838154811061075457610754613811565b9060005260206000200160009054906101000a90046001600160a01b031690508087848151811061078757610787613811565b60200260200101906001600160a01b031690816001600160a01b031681525050600a6000826001600160a01b03166001600160a01b03168152602001908152602001600020548584815181106107df576107df613811565b602002602001018181525050826001019250610739565b6006546000905b808210156108d8576006828154811061081857610818613811565b9060005260206000200160009054906101000a90046001600160a01b031692508289868151811061084b5761084b613811565b60200260200101906001600160a01b031690816001600160a01b0316815250506008828154811061087e5761087e613811565b906000526020600020015488868151811061089b5761089b613811565b60200260200101818152505060018786815181106108bb576108bb613811565b6020026020010181815250508460010194508160010191506107fd565b600754600090815b818310156109dc57600783815481106108fb576108fb613811565b600091825260209091200154600980546001600160a01b039092169750908490811061092957610929613811565b90600052602060002001549050858c898151811061094957610949613811565b60200260200101906001600160a01b031690816001600160a01b031681525050808b898151811061097c5761097c613811565b6020908102919091018101919091526001600160a01b0387166000908152600b8252604080822084835290925220548a518b908a9081106109bf576109bf613811565b6020026020010181815250508760010197508260010192506108e0565b505050505050505050909192565b600080808315610a1b57600154610a0a906001600160601b03168561383d565b610a12611d31565b10159250610a3b565b84610a24610a60565b610a2e919061383d565b610a36611d31565b101592505b50506003546000546001600160a01b039091169061ffff610100909104169250925092565b6000805460ff16610a715750600090565b60015460035460405163c11002df60e01b81523060048201526001600160601b03909216916001600160a01b039091169063c11002df90602401602060405180830381865afa158015610ac8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aec9190613856565b610af6919061383d565b905090565b600154600160601b90046001600160a01b03163314610b615760405162461bcd60e51b815260206004820152601860248201527f565f533a204f6e6c79206f776e65722063616e20736b696d000000000000000060448201526064015b60405180910390fd5b6001600160a01b038316610bb7576001546040516001600160a01b03600160601b90920491909116904780156108fc02916000818181858888f19350505050158015610bb1573d6000803e3d6000fd5b50505050565b80600003610c80576040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015610c06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2a9190613856565b6001600160a01b0385166000908152600a60205260409020549091508082111561046a5760015461046a90600160601b90046001600160a01b0316610c6f838561386f565b6001600160a01b03881691906121db565b80600103610d8a57600654600090815b81811015610d0957856001600160a01b031660068281548110610cb557610cb5613811565b6000918252602090912001546001600160a01b0316148015610cf357508460088281548110610ce657610ce6613811565b9060005260206000200154145b15610d015760019250610d09565b600101610c90565b508161046a57600154604051632142170760e11b8152306004820152600160601b9091046001600160a01b039081166024830152604482018690528616906342842e0e906064015b600060405180830381600087803b158015610d6b57600080fd5b505af1158015610d7f573d6000803e3d6000fd5b505050505050505050565b80600203610e7a57604051627eeac760e11b8152306004820152602481018390526000906001600160a01b0385169062fdd58e90604401602060405180830381865afa158015610dde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e029190613856565b6001600160a01b0385166000908152600b602090815260408083208784529091529020549091508082111561046a576001546001600160a01b038087169163f242432a913091600160601b90041687610e5b868861386f565b6040518563ffffffff1660e01b8152600401610d519493929190613882565b505050565b6001546000908190600160601b90046001600160a01b0316331480610eae57506003546001600160a01b031633145b80610ee55750600154600160601b90046001600160a01b03166000908152600c6020908152604080832033845290915290205460ff165b610f295760405162461bcd60e51b81526020600482015260156024820152742b1d1027b7363c9020b9b9b2ba1026b0b730b3b2b960591b6044820152606401610b58565b600254604051634fab6c4360e11b81526001600160a01b03878116600483015290911690639f56d88690602401602060405180830381865afa158015610f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9791906138ba565b610fe35760405162461bcd60e51b815260206004820152601960248201527f565f564d413a20416374696f6e206e6f7420616c6c6f776564000000000000006044820152606401610b58565b6000610ff184860186613afe565b505050905061100e81600001518260200151836040015189612253565b60405163a129568d60e01b81526000906001600160a01b0388169063a129568d9061103f9089908990600401613c94565b6000604051808303816000875af115801561105e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526110869190810190613d6f565b90506110a08160000151826020015183604001518a612491565b60006110aa610a60565b6001549091506001600160601b031681111561111057806110c9611d31565b10156111105760405162461bcd60e51b8152602060048201526016602482015275565f564d413a205661756c7420556e6865616c74687960501b6044820152606401610b58565b50506003546000546001600160a01b039091169761010090910461ffff169650945050505050565b6007818154811061114857600080fd5b6000918252602090912001546001600160a01b0316905081565b600260009054906101000a90046001600160a01b03166001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d99190613e6b565b6001600160a01b0316336001600160a01b03161461122b5760405162461bcd60e51b815260206004820152600f60248201526e563a204f6e6c7920466163746f727960881b6044820152606401610b58565b60005460ff16156112fb57600354604051638c48052360e01b815261ffff851660048201526000916001600160a01b031690638c48052390602401608060405180830381865afa158015611283573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a79190613e88565b5050509050806112f95760405162461bcd60e51b815260206004820152601b60248201527f565f55563a20496e76616c6964207661756c742076657273696f6e00000000006044820152606401610b58565b505b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc805460028054600080546001600160a01b03198086166001600160a01b038d81169190911790975583168a87161790935562ffff0019831661010061ffff8a8116820292909217909255604051637a1d169560e01b81529486169590921693920416903090637a1d16959061139d908690869086908b908b90600401613edb565b600060405180830381600087803b1580156113b757600080fd5b505af11580156113cb573d6000803e3d6000fd5b505050505050505050505050565b600154600160601b90046001600160a01b0316331461140a5760405162461bcd60e51b8152600401610b5890613f1d565b6114aa86868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808a0282810182019093528982529093508992508891829185019084908082843760009201919091525050604080516020808902828101820190935288825290935088925087918291850190849080828437600092019190915250339250612491915050565b505050505050565b6006818154811061114857600080fd5b600154600160601b90046001600160a01b031633146114f35760405162461bcd60e51b8152600401610b5890613f1d565b60005460ff166115375760405162461bcd60e51b815260206004820152600f60248201526e1597d0d513504e881393d50814d155608a1b6044820152606401610b58565b60035460405163c11002df60e01b81523060048201526001600160a01b039091169063c11002df90602401602060405180830381865afa15801561157f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a39190613856565b156115f05760405162461bcd60e51b815260206004820152601e60248201527f565f43544d413a204e4f4e2d5a45524f204f50454e20504f534954494f4e00006044820152606401610b58565b60008054600380546001600160a01b031916905562ffff01600160b81b0319168155600180546001600160601b031916905560405181907f7c870bf97bf11b7ae68424ab51803b0f4c489bbecd0cddd19f7747f16211f781908290a3565b60008060008061165c610652565b6002546004805460405163540629df60e01b81529598509396509194506001600160a01b039081169363540629df9361169f938993899389939091169101613f44565b602060405180830381865afa1580156116bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e09190613856565b935050505090565b6000805481908190630100000090046001600160a01b031633146117465760405162461bcd60e51b81526020600482015260156024820152742b2fa62b1d1027b7363c902634b8bab4b230ba37b960591b6044820152606401610b58565b5060038054600080546001600160a01b0319831690935562ffff01600160b81b03199092169091556001546001600160a01b0390911690611790906001600160601b03168561383d565b61179861164e565b106117e55760405162461bcd60e51b815260206004820152601f60248201527f565f4c563a206c697156616c75652061626f766520757365644d617267696e006044820152606401610b58565b600180546001600160601b03191690556002546040805163c45a015560e01b815290516001600160a01b039092169163c45a0155916004808201926020929091908290030181865afa15801561183f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118639190613e6b565b6040516305f0caad60e31b81523360048201526001600160a01b039190911690632f86556890602401600060405180830381600087803b1580156118a657600080fd5b505af11580156118ba573d6000803e3d6000fd5b505060018054600160601b3381026001600160601b038316179092556001600160a01b039190041694506118ec915050565b60405160009081907f7c870bf97bf11b7ae68424ab51803b0f4c489bbecd0cddd19f7747f16211f781908290a36004546001600160a01b031691509193909250565b60008060008061193c610652565b600254604051632933cbe360e21b815293965091945092506001600160a01b03169063a4cf2f8c90611978908690869086908b90600401613f44565b602060405180830381865afa158015611995573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b99190613856565b95945050505050565b600881815481106119d257600080fd5b600091825260209091200154905081565b600054610100900461ffff16158015611a0c5750600154600160601b90046001600160a01b0316155b611a585760405162461bcd60e51b815260206004820152601960248201527f565f493a20416c726561647920696e697469616c697a656421000000000000006044820152606401610b58565b8161ffff16600003611aac5760405162461bcd60e51b815260206004820152601a60248201527f565f493a20496e76616c6964207661756c742076657273696f6e0000000000006044820152606401610b58565b600180546001600160601b0316600160601b6001600160a01b038781169190910291909117909155600280546001600160a01b0319908116868416179091556000805462ffff00191661010061ffff8716021790556004805490911691831691821790556040519081527f90c340510e0f42fbc86b8f279720f8a069bce148554c0b17f61a01db70757d7e9060200160405180910390a150505050565b600154600160601b90046001600160a01b03163314611b7a5760405162461bcd60e51b8152600401610b5890613f1d565b60005460ff1615611bcd5760405162461bcd60e51b815260206004820152601b60248201527f565f5342433a2054727573746564204372656469746f722053657400000000006044820152606401610b58565b611bd681612735565b50565b600981815481106119d257600080fd5b600154600160601b90046001600160a01b03163314611c1a5760405162461bcd60e51b8152600401610b5890613f1d565b611cba86868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808a0282810182019093528982529093508992508891829185019084908082843760009201919091525050604080516020808902828101820190935288825290935088925087918291850190849080828437600092019190915250339250612253915050565b6000611cc4610a60565b6001549091506001600160601b0316811115611d285780611ce3611d31565b1015611d285760405162461bcd60e51b8152602060048201526014602482015273565f573a205661756c7420556e6865616c74687960601b6044820152606401610b58565b50505050505050565b600080600080611d3f610652565b60025460048054604051631527047960e11b81529598509396509194506001600160a01b0390811693632a4e08f29361169f938993899389939091169101613f44565b6005818154811061114857600080fd5b600154600160601b90046001600160a01b03163314611dc35760405162461bcd60e51b8152600401610b5890613f1d565b336000818152600c602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c1910160405180910390a35050565b600080611e3a610a60565b6001549091506001600160601b0316811115611e5d5780611e5961164e565b1091505b5090565b600080611e6c611d31565b90506000611e78610a60565b9050808211611e88576000611e8c565b8082035b9250505090565b600260009054906101000a90046001600160a01b03166001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ee6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0a9190613e6b565b6001600160a01b0316336001600160a01b031614611f5c5760405162461bcd60e51b815260206004820152600f60248201526e563a204f6e6c7920466163746f727960881b6044820152606401610b58565b6001600160a01b038116611fb25760405162461bcd60e51b815260206004820152601760248201527f565f544f3a20494e56414c49445f524543495049454e540000000000000000006044820152606401610b58565b600180546001600160601b0316600160601b6001600160a01b0384160217905550565b600154600160601b90046001600160a01b031633146120065760405162461bcd60e51b8152600401610b5890613f1d565b60005460ff161561204f5760405162461bcd60e51b81526020600482015260136024820152721597d3d513504e88105314915051164814d155606a1b6044820152606401610b58565b60008054604051638c48052360e01b815261010090910461ffff1660048201528190819081906001600160a01b03861690638c48052390602401608060405180830381865afa1580156120a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ca9190613e88565b93509350935093508361211f5760405162461bcd60e51b815260206004820152601760248201527f565f4f544d413a20496e76616c69642056657273696f6e0000000000000000006044820152606401610b58565b600080546301000000600160b81b03191663010000006001600160a01b038581169190910291909117909155600380546001600160a01b031916878316179055600180546001600160601b0319166001600160601b0384161790556004548482169116146121905761219083612735565b6000805460ff191660011781556040516001600160a01b0380851692908816917f7c870bf97bf11b7ae68424ab51803b0f4c489bbecd0cddd19f7747f16211f7819190a35050505050565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610bb15760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610b58565b6002546040516301c24e0560e21b81526000916001600160a01b0316906307093814906122889088908890889060040161334d565b6000604051808303816000875af11580156122a7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122cf9190810190613f97565b855190915060005b81811015611d28578481815181106122f1576122f1613811565b6020026020010151600003612308576001016122d7565b82818151811061231a5761231a613811565b602002602001015160000361236b576123668488838151811061233f5761233f613811565b602002602001015187848151811061235957612359613811565b6020026020010151612843565b612489565b82818151811061237d5761237d613811565b60200260200101516001036123c957612366848883815181106123a2576123a2613811565b60200260200101518884815181106123bc576123bc613811565b60200260200101516129e4565b8281815181106123db576123db613811565b6020026020010151600203612441576123668488838151811061240057612400613811565b602002602001015188848151811061241a5761241a613811565b602002602001015188858151811061243457612434613811565b6020026020010151612d03565b60405162461bcd60e51b815260206004820152601760248201527f565f573a20556e6b6e6f776e20617373657420747970650000000000000000006044820152606401610b58565b6001016122d7565b600254604051632303480f60e01b81526000916001600160a01b031690632303480f906124c69088908890889060040161334d565b6000604051808303816000875af11580156124e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261250d9190810190613f97565b855190915060005b818110156126cf5784818151811061252f5761252f613811565b602002602001015160000361254657600101612515565b82818151811061255857612558613811565b60200260200101516000036125a9576125a48488838151811061257d5761257d613811565b602002602001015187848151811061259757612597613811565b6020026020010151612f90565b6126c7565b8281815181106125bb576125bb613811565b6020026020010151600103612607576125a4848883815181106125e0576125e0613811565b60200260200101518884815181106125fa576125fa613811565b6020026020010151613036565b82818151811061261957612619613811565b602002602001015160020361267f576125a48488838151811061263e5761263e613811565b602002602001015188848151811061265857612658613811565b602002602001015188858151811061267257612672613811565b602002602001015161311f565b60405162461bcd60e51b815260206004820152601760248201527f565f443a20556e6b6e6f776e20617373657420747970650000000000000000006044820152606401610b58565b600101612515565b50600754600654600554600f92916126e69161383d565b6126f0919061383d565b11156114aa5760405162461bcd60e51b8152602060048201526014602482015273565f443a20546f6f206d616e792061737365747360601b6044820152606401610b58565b600254604051639b8bf37d60e01b81526001600160a01b03838116600483015290911690639b8bf37d90602401602060405180830381865afa15801561277f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a391906138ba565b6127ef5760405162461bcd60e51b815260206004820152601d60248201527f565f5342433a206261736543757272656e6379206e6f7420666f756e640000006044820152606401610b58565b600480546001600160a01b0319166001600160a01b0383169081179091556040519081527f90c340510e0f42fbc86b8f279720f8a069bce148554c0b17f61a01db70757d7e9060200160405180910390a150565b6001600160a01b0382166000908152600a60205260408120805483929061286b90849061386f565b90915550506001600160a01b0382166000908152600a602052604081205490036129d05760055460018190036128d35760058054806128ac576128ac613fcb565b600082815260209020810160001990810180546001600160a01b03191690550190556129ce565b60005b818110156129cc57836001600160a01b0316600582815481106128fb576128fb613811565b6000918252602090912001546001600160a01b0316036129c457600561292260018461386f565b8154811061293257612932613811565b600091825260209091200154600580546001600160a01b03909216918390811061295e5761295e613811565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600580548061299d5761299d613fcb565b600082815260209020810160001990810180546001600160a01b03191690550190556129cc565b6001016128d6565b505b505b610e7a6001600160a01b03831684836121db565b60085460006001829003612af257826008600081548110612a0757612a07613811565b9060005260206000200154148015612a4f5750836001600160a01b03166006600081548110612a3857612a38613811565b6000918252602090912001546001600160a01b0316145b612a935760405162461bcd60e51b81526020600482015260156024820152741597d5cdcc8c4e88155b9adb9bdddb88185cdcd95d605a1b6044820152606401610b58565b6008805480612aa457612aa4613fcb565b600190038181906000526020600020016000905590556006805480612acb57612acb613fcb565b600082815260209020810160001990810180546001600160a01b0319169055019055612cc8565b81811015612c81578260088281548110612b0e57612b0e613811565b9060005260206000200154148015612b555750836001600160a01b031660068281548110612b3e57612b3e613811565b6000918252602090912001546001600160a01b0316145b15612c79576008612b6760018461386f565b81548110612b7757612b77613811565b906000526020600020015460088281548110612b9557612b95613811565b6000918252602090912001556008805480612bb257612bb2613fcb565b600190038181906000526020600020016000905590556006600183612bd7919061386f565b81548110612be757612be7613811565b600091825260209091200154600680546001600160a01b039092169183908110612c1357612c13613811565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506006805480612c5257612c52613fcb565b600082815260209020810160001990810180546001600160a01b0319169055019055612c81565b600101612af2565b818110612cc85760405162461bcd60e51b81526020600482015260156024820152741597d5cdcc8c4e88155b9adb9bdddb88185cdcd95d605a1b6044820152606401610b58565b604051632142170760e11b81523060048201526001600160a01b038681166024830152604482018590528516906342842e0e90606401610d51565b6009546001600160a01b0384166000908152600b6020908152604080832086845290915281208054849290612d3990849061386f565b90915550506001600160a01b0384166000908152600b602090815260408083208684529091528120549003612f5e5780600103612dcf576009805480612d8157612d81613fcb565b600190038181906000526020600020016000905590556007805480612da857612da8613fcb565b600082815260209020810160001990810180546001600160a01b0319169055019055612f5e565b60005b81811015612f5c578360098281548110612dee57612dee613811565b906000526020600020015403612f5457846001600160a01b031660078281548110612e1b57612e1b613811565b6000918252602090912001546001600160a01b031603612f54576009612e4260018461386f565b81548110612e5257612e52613811565b906000526020600020015460098281548110612e7057612e70613811565b6000918252602090912001556009805480612e8d57612e8d613fcb565b600190038181906000526020600020016000905590556007600183612eb2919061386f565b81548110612ec257612ec2613811565b600091825260209091200154600780546001600160a01b039092169183908110612eee57612eee613811565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506007805480612f2d57612f2d613fcb565b600082815260209020810160001990810180546001600160a01b0319169055019055612f5c565b600101612dd2565b505b604051637921219560e11b81526001600160a01b0385169063f242432a90610d51903090899088908890600401613882565b612fa56001600160a01b038316843084613256565b6001600160a01b0382166000908152600a60205260408120549081900361301257600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b0385161790555b506001600160a01b039091166000908152600a602052604090208054909101905550565b604051632142170760e11b81526001600160a01b038481166004830152306024830152604482018390528316906342842e0e90606401600060405180830381600087803b15801561308657600080fd5b505af115801561309a573d6000803e3d6000fd5b50506006805460018082019092557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b03969096169590951790945550506008805492830181556000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee39091015550565b604051637921219560e11b81526001600160a01b0384169063f242432a90613151908790309087908790600401613882565b600060405180830381600087803b15801561316b57600080fd5b505af115801561317f573d6000803e3d6000fd5b5050506001600160a01b0384166000908152600b602090815260408083208684529091528120549150819003613228576007805460018082019092557fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180546001600160a01b0319166001600160a01b0387161790556009805491820181556000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af018390555b506001600160a01b039092166000908152600b60209081526040808320938352929052208054909101905550565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061046a5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610b58565b600081518084526020808501945080840160005b838110156133125781516001600160a01b0316875295820195908201906001016132ed565b509495945050505050565b600081518084526020808501945080840160005b8381101561331257815187529582019590820190600101613331565b60608152600061336060608301866132d9565b8281036020840152613372818661331d565b90508281036040840152613386818561331d565b9695505050505050565b6001600160a01b0381168114611bd657600080fd5b60008083601f8401126133b757600080fd5b5081356001600160401b038111156133ce57600080fd5b6020830191508360208285010111156133e657600080fd5b9250929050565b60008060008060006080868803121561340557600080fd5b853561341081613390565b9450602086013561342081613390565b93506040860135925060608601356001600160401b0381111561344257600080fd5b61344e888289016133a5565b969995985093965092949392505050565b6000806040838503121561347257600080fd5b50508035926020909101359150565b60006020828403121561349357600080fd5b813561349e81613390565b9392505050565b6000806000606084860312156134ba57600080fd5b83356134c581613390565b95602085013595506040909401359392505050565b6000806000604084860312156134ef57600080fd5b83356134fa81613390565b925060208401356001600160401b0381111561351557600080fd5b613521868287016133a5565b9497909650939450505050565b60006020828403121561354057600080fd5b5035919050565b803561ffff8116811461355957600080fd5b919050565b60008060008060006080868803121561357657600080fd5b853561358181613390565b9450602086013561359181613390565b935061359f60408701613547565b925060608601356001600160401b0381111561344257600080fd5b60008083601f8401126135cc57600080fd5b5081356001600160401b038111156135e357600080fd5b6020830191508360208260051b85010111156133e657600080fd5b6000806000806000806060878903121561361757600080fd5b86356001600160401b038082111561362e57600080fd5b61363a8a838b016135ba565b9098509650602089013591508082111561365357600080fd5b61365f8a838b016135ba565b9096509450604089013591508082111561367857600080fd5b5061368589828a016135ba565b979a9699509497509295939492505050565b600080604083850312156136aa57600080fd5b82356136b581613390565b915060208301356136c581613390565b809150509250929050565b600080604083850312156136e357600080fd5b82356136ee81613390565b946020939093013593505050565b6000806000806080858703121561371257600080fd5b843561371d81613390565b9350602085013561372d81613390565b925061373b60408601613547565b9150606085013561374b81613390565b939692955090935050565b8015158114611bd657600080fd5b6000806040838503121561377757600080fd5b823561378281613390565b915060208301356136c581613756565b60008060008060008060a087890312156137ab57600080fd5b86356137b681613390565b955060208701356137c681613390565b9450604087013593506060870135925060808701356001600160401b038111156137ef57600080fd5b61368589828a016133a5565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8082018082111561385057613850613827565b92915050565b60006020828403121561386857600080fd5b5051919050565b8181038181111561385057613850613827565b6001600160a01b0394851681529290931660208301526040820152606081019190915260a06080820181905260009082015260c00190565b6000602082840312156138cc57600080fd5b815161349e81613756565b60405160a081016001600160401b03811182821017156138f9576138f96137fb565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613927576139276137fb565b604052919050565b60006001600160401b03821115613948576139486137fb565b5060051b60200190565b600082601f83011261396357600080fd5b813560206139786139738361392f565b6138ff565b82815260059290921b8401810191818101908684111561399757600080fd5b8286015b848110156139bb5780356139ae81613390565b835291830191830161399b565b509695505050505050565b600082601f8301126139d757600080fd5b813560206139e76139738361392f565b82815260059290921b84018101918181019086841115613a0657600080fd5b8286015b848110156139bb5780358352918301918301613a0a565b600060a08284031215613a3357600080fd5b613a3b6138d7565b905081356001600160401b0380821115613a5457600080fd5b613a6085838601613952565b83526020840135915080821115613a7657600080fd5b613a82858386016139c6565b60208401526040840135915080821115613a9b57600080fd5b613aa7858386016139c6565b60408401526060840135915080821115613ac057600080fd5b613acc858386016139c6565b60608401526080840135915080821115613ae557600080fd5b50613af2848285016139c6565b60808301525092915050565b60008060008060808587031215613b1457600080fd5b84356001600160401b0380821115613b2b57600080fd5b613b3788838901613a21565b9550602091508187013581811115613b4e57600080fd5b613b5a89828a01613a21565b955050604087013581811115613b6f57600080fd5b613b7b89828a01613952565b945050606087013581811115613b9057600080fd5b8701601f81018913613ba157600080fd5b8035613baf6139738261392f565b81815260059190911b8201840190848101908b831115613bce57600080fd5b8584015b83811015613c5a57803586811115613bea5760008081fd5b8501603f81018e13613bfc5760008081fd5b8781013587811115613c1057613c106137fb565b613c22601f8201601f19168a016138ff565b8181528f6040838501011115613c385760008081fd5b81604084018b83013760009181018a0191909152845250918601918601613bd2565b50989b979a50959850505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000613ca8602083018486613c6b565b949350505050565b600082601f830112613cc157600080fd5b81516020613cd16139738361392f565b82815260059290921b84018101918181019086841115613cf057600080fd5b8286015b848110156139bb578051613d0781613390565b8352918301918301613cf4565b600082601f830112613d2557600080fd5b81516020613d356139738361392f565b82815260059290921b84018101918181019086841115613d5457600080fd5b8286015b848110156139bb5780518352918301918301613d58565b600060208284031215613d8157600080fd5b81516001600160401b0380821115613d9857600080fd5b9083019060a08286031215613dac57600080fd5b613db46138d7565b825182811115613dc357600080fd5b613dcf87828601613cb0565b825250602083015182811115613de457600080fd5b613df087828601613d14565b602083015250604083015182811115613e0857600080fd5b613e1487828601613d14565b604083015250606083015182811115613e2c57600080fd5b613e3887828601613d14565b606083015250608083015182811115613e5057600080fd5b613e5c87828601613d14565b60808301525095945050505050565b600060208284031215613e7d57600080fd5b815161349e81613390565b60008060008060808587031215613e9e57600080fd5b8451613ea981613756565b6020860151909450613eba81613390565b6040860151909350613ecb81613390565b6060959095015193969295505050565b6001600160a01b0386811682528516602082015261ffff84166040820152608060608201819052600090613f129083018486613c6b565b979650505050505050565b6020808252600d908201526c2b1d1027b7363c9027bbb732b960991b604082015260600190565b608081526000613f5760808301876132d9565b8281036020840152613f69818761331d565b90508281036040840152613f7d818661331d565b91505060018060a01b038316606083015295945050505050565b600060208284031215613fa957600080fd5b81516001600160401b03811115613fbf57600080fd5b613ca884828501613d14565b634e487b7160e01b600052603160045260246000fdfea26469706673582212209dd459a617f2da66eb5892be19b50ccc37d78362049434eb8aef36258b40678964736f6c63430008110033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102485760003560e01c80638e33f5ee1161013b578063b8eabab0116100b8578063ebbbbc831161007c578063ebbbbc83146105fc578063edfe1faf14610604578063f23a6e611461060c578063f2fde38b1461062c578063f60935301461063f57600080fd5b8063b8eabab0146105b3578063bfed6b03146105c6578063c047e563146105ce578063cfdb12db146105d6578063d80359f1146105e957600080fd5b80639ea1a85d116100ff5780639ea1a85d1461053c578063adb33ab61461054f578063b1ffa5821461057a578063b478f4161461058d578063b6f70e76146105a057600080fd5b80638e33f5ee146104a6578063907a9429146104d157806390cf0bba146104d957806392a85fde146105165780639566433a1461052957600080fd5b80635a9c6128116101c957806371a84c711161018d57806371a84c711461042e5780637a1d16951461045c5780637b103999146104715780638d3a47ca146104845780638da5cb5b1461048c57600080fd5b80635a9c6128146103c5578063610600c8146103d85780636208e930146103eb578063656e8729146104085780636b8798d71461041b57600080fd5b806342c8d7151161021057806342c8d7151461033d57806345a50a80146103455780634a1ec375146103585780634d401a361461036d5780634e7adf2c1461039f57600080fd5b806308f1bdaa1461024d578063150b7a021461026d57806317e62b67146102a557806321ab889d146102dd5780634046ebae1461030b575b600080fd5b610255610652565b6040516102649392919061334d565b60405180910390f35b61028c61027b3660046133ed565b630a85bd0160e11b95945050505050565b6040516001600160e01b03199091168152602001610264565b6102b86102b336600461345f565b6109ea565b6040805193151584526001600160a01b03909216602084015290820152606001610264565b6102fd6102eb366004613481565b600a6020526000908152604090205481565b604051908152602001610264565b60005461032590630100000090046001600160a01b031681565b6040516001600160a01b039091168152602001610264565b6102fd610a60565b600354610325906001600160a01b031681565b61036b6103663660046134a5565b610afb565b005b61038061037b3660046134da565b610e7f565b604080516001600160a01b039093168352602083019190915201610264565b6000546103b290610100900461ffff1681565b60405161ffff9091168152602001610264565b6103256103d336600461352e565b611138565b61036b6103e636600461355e565b611162565b6000546103f89060ff1681565b6040519015158152602001610264565b61036b6104163660046135fe565b6113d9565b61032561042936600461352e565b6114b2565b6103f861043c366004613697565b600c60209081526000928352604080842090915290825290205460ff1681565b61036b61046a36600461355e565b5050505050565b600254610325906001600160a01b031681565b61036b6114c2565b60015461032590600160601b90046001600160a01b031681565b6001546104b9906001600160601b031681565b6040516001600160601b039091168152602001610264565b6102fd61164e565b6104ec6104e736600461352e565b6116e8565b604080516001600160a01b0394851681529284166020840152921691810191909152606001610264565b600454610325906001600160a01b031681565b6102fd610537366004613481565b61192e565b6102fd61054a36600461352e565b6119c2565b6102fd61055d3660046136d0565b600b60209081526000928352604080842090915290825290205481565b61036b6105883660046136fc565b6119e3565b61036b61059b366004613481565b611b49565b6102fd6105ae36600461352e565b611bd9565b61036b6105c13660046135fe565b611be9565b6102fd600f81565b6102fd611d31565b6103256105e436600461352e565b611d82565b61036b6105f7366004613764565b611d92565b6103f8611e2f565b6102fd611e61565b61028c61061a366004613792565b63f23a6e6160e01b9695505050505050565b61036b61063a366004613481565b611e93565b61036b61064d366004613481565b611fd5565b600754600654600554606092839283920101806001600160401b0381111561067c5761067c6137fb565b6040519080825280602002602001820160405280156106a5578160200160208202803683370190505b509350806001600160401b038111156106c0576106c06137fb565b6040519080825280602002602001820160405280156106e9578160200160208202803683370190505b509250806001600160401b03811115610704576107046137fb565b60405190808252806020026020018201604052801561072d578160200160208202803683370190505b50600554909250600090815b818310156107f6576005838154811061075457610754613811565b9060005260206000200160009054906101000a90046001600160a01b031690508087848151811061078757610787613811565b60200260200101906001600160a01b031690816001600160a01b031681525050600a6000826001600160a01b03166001600160a01b03168152602001908152602001600020548584815181106107df576107df613811565b602002602001018181525050826001019250610739565b6006546000905b808210156108d8576006828154811061081857610818613811565b9060005260206000200160009054906101000a90046001600160a01b031692508289868151811061084b5761084b613811565b60200260200101906001600160a01b031690816001600160a01b0316815250506008828154811061087e5761087e613811565b906000526020600020015488868151811061089b5761089b613811565b60200260200101818152505060018786815181106108bb576108bb613811565b6020026020010181815250508460010194508160010191506107fd565b600754600090815b818310156109dc57600783815481106108fb576108fb613811565b600091825260209091200154600980546001600160a01b039092169750908490811061092957610929613811565b90600052602060002001549050858c898151811061094957610949613811565b60200260200101906001600160a01b031690816001600160a01b031681525050808b898151811061097c5761097c613811565b6020908102919091018101919091526001600160a01b0387166000908152600b8252604080822084835290925220548a518b908a9081106109bf576109bf613811565b6020026020010181815250508760010197508260010192506108e0565b505050505050505050909192565b600080808315610a1b57600154610a0a906001600160601b03168561383d565b610a12611d31565b10159250610a3b565b84610a24610a60565b610a2e919061383d565b610a36611d31565b101592505b50506003546000546001600160a01b039091169061ffff610100909104169250925092565b6000805460ff16610a715750600090565b60015460035460405163c11002df60e01b81523060048201526001600160601b03909216916001600160a01b039091169063c11002df90602401602060405180830381865afa158015610ac8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610aec9190613856565b610af6919061383d565b905090565b600154600160601b90046001600160a01b03163314610b615760405162461bcd60e51b815260206004820152601860248201527f565f533a204f6e6c79206f776e65722063616e20736b696d000000000000000060448201526064015b60405180910390fd5b6001600160a01b038316610bb7576001546040516001600160a01b03600160601b90920491909116904780156108fc02916000818181858888f19350505050158015610bb1573d6000803e3d6000fd5b50505050565b80600003610c80576040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa158015610c06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c2a9190613856565b6001600160a01b0385166000908152600a60205260409020549091508082111561046a5760015461046a90600160601b90046001600160a01b0316610c6f838561386f565b6001600160a01b03881691906121db565b80600103610d8a57600654600090815b81811015610d0957856001600160a01b031660068281548110610cb557610cb5613811565b6000918252602090912001546001600160a01b0316148015610cf357508460088281548110610ce657610ce6613811565b9060005260206000200154145b15610d015760019250610d09565b600101610c90565b508161046a57600154604051632142170760e11b8152306004820152600160601b9091046001600160a01b039081166024830152604482018690528616906342842e0e906064015b600060405180830381600087803b158015610d6b57600080fd5b505af1158015610d7f573d6000803e3d6000fd5b505050505050505050565b80600203610e7a57604051627eeac760e11b8152306004820152602481018390526000906001600160a01b0385169062fdd58e90604401602060405180830381865afa158015610dde573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e029190613856565b6001600160a01b0385166000908152600b602090815260408083208784529091529020549091508082111561046a576001546001600160a01b038087169163f242432a913091600160601b90041687610e5b868861386f565b6040518563ffffffff1660e01b8152600401610d519493929190613882565b505050565b6001546000908190600160601b90046001600160a01b0316331480610eae57506003546001600160a01b031633145b80610ee55750600154600160601b90046001600160a01b03166000908152600c6020908152604080832033845290915290205460ff165b610f295760405162461bcd60e51b81526020600482015260156024820152742b1d1027b7363c9020b9b9b2ba1026b0b730b3b2b960591b6044820152606401610b58565b600254604051634fab6c4360e11b81526001600160a01b03878116600483015290911690639f56d88690602401602060405180830381865afa158015610f73573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9791906138ba565b610fe35760405162461bcd60e51b815260206004820152601960248201527f565f564d413a20416374696f6e206e6f7420616c6c6f776564000000000000006044820152606401610b58565b6000610ff184860186613afe565b505050905061100e81600001518260200151836040015189612253565b60405163a129568d60e01b81526000906001600160a01b0388169063a129568d9061103f9089908990600401613c94565b6000604051808303816000875af115801561105e573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526110869190810190613d6f565b90506110a08160000151826020015183604001518a612491565b60006110aa610a60565b6001549091506001600160601b031681111561111057806110c9611d31565b10156111105760405162461bcd60e51b8152602060048201526016602482015275565f564d413a205661756c7420556e6865616c74687960501b6044820152606401610b58565b50506003546000546001600160a01b039091169761010090910461ffff169650945050505050565b6007818154811061114857600080fd5b6000918252602090912001546001600160a01b0316905081565b600260009054906101000a90046001600160a01b03166001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156111b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111d99190613e6b565b6001600160a01b0316336001600160a01b03161461122b5760405162461bcd60e51b815260206004820152600f60248201526e563a204f6e6c7920466163746f727960881b6044820152606401610b58565b60005460ff16156112fb57600354604051638c48052360e01b815261ffff851660048201526000916001600160a01b031690638c48052390602401608060405180830381865afa158015611283573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112a79190613e88565b5050509050806112f95760405162461bcd60e51b815260206004820152601b60248201527f565f55563a20496e76616c6964207661756c742076657273696f6e00000000006044820152606401610b58565b505b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc805460028054600080546001600160a01b03198086166001600160a01b038d81169190911790975583168a87161790935562ffff0019831661010061ffff8a8116820292909217909255604051637a1d169560e01b81529486169590921693920416903090637a1d16959061139d908690869086908b908b90600401613edb565b600060405180830381600087803b1580156113b757600080fd5b505af11580156113cb573d6000803e3d6000fd5b505050505050505050505050565b600154600160601b90046001600160a01b0316331461140a5760405162461bcd60e51b8152600401610b5890613f1d565b6114aa86868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808a0282810182019093528982529093508992508891829185019084908082843760009201919091525050604080516020808902828101820190935288825290935088925087918291850190849080828437600092019190915250339250612491915050565b505050505050565b6006818154811061114857600080fd5b600154600160601b90046001600160a01b031633146114f35760405162461bcd60e51b8152600401610b5890613f1d565b60005460ff166115375760405162461bcd60e51b815260206004820152600f60248201526e1597d0d513504e881393d50814d155608a1b6044820152606401610b58565b60035460405163c11002df60e01b81523060048201526001600160a01b039091169063c11002df90602401602060405180830381865afa15801561157f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115a39190613856565b156115f05760405162461bcd60e51b815260206004820152601e60248201527f565f43544d413a204e4f4e2d5a45524f204f50454e20504f534954494f4e00006044820152606401610b58565b60008054600380546001600160a01b031916905562ffff01600160b81b0319168155600180546001600160601b031916905560405181907f7c870bf97bf11b7ae68424ab51803b0f4c489bbecd0cddd19f7747f16211f781908290a3565b60008060008061165c610652565b6002546004805460405163540629df60e01b81529598509396509194506001600160a01b039081169363540629df9361169f938993899389939091169101613f44565b602060405180830381865afa1580156116bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116e09190613856565b935050505090565b6000805481908190630100000090046001600160a01b031633146117465760405162461bcd60e51b81526020600482015260156024820152742b2fa62b1d1027b7363c902634b8bab4b230ba37b960591b6044820152606401610b58565b5060038054600080546001600160a01b0319831690935562ffff01600160b81b03199092169091556001546001600160a01b0390911690611790906001600160601b03168561383d565b61179861164e565b106117e55760405162461bcd60e51b815260206004820152601f60248201527f565f4c563a206c697156616c75652061626f766520757365644d617267696e006044820152606401610b58565b600180546001600160601b03191690556002546040805163c45a015560e01b815290516001600160a01b039092169163c45a0155916004808201926020929091908290030181865afa15801561183f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118639190613e6b565b6040516305f0caad60e31b81523360048201526001600160a01b039190911690632f86556890602401600060405180830381600087803b1580156118a657600080fd5b505af11580156118ba573d6000803e3d6000fd5b505060018054600160601b3381026001600160601b038316179092556001600160a01b039190041694506118ec915050565b60405160009081907f7c870bf97bf11b7ae68424ab51803b0f4c489bbecd0cddd19f7747f16211f781908290a36004546001600160a01b031691509193909250565b60008060008061193c610652565b600254604051632933cbe360e21b815293965091945092506001600160a01b03169063a4cf2f8c90611978908690869086908b90600401613f44565b602060405180830381865afa158015611995573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119b99190613856565b95945050505050565b600881815481106119d257600080fd5b600091825260209091200154905081565b600054610100900461ffff16158015611a0c5750600154600160601b90046001600160a01b0316155b611a585760405162461bcd60e51b815260206004820152601960248201527f565f493a20416c726561647920696e697469616c697a656421000000000000006044820152606401610b58565b8161ffff16600003611aac5760405162461bcd60e51b815260206004820152601a60248201527f565f493a20496e76616c6964207661756c742076657273696f6e0000000000006044820152606401610b58565b600180546001600160601b0316600160601b6001600160a01b038781169190910291909117909155600280546001600160a01b0319908116868416179091556000805462ffff00191661010061ffff8716021790556004805490911691831691821790556040519081527f90c340510e0f42fbc86b8f279720f8a069bce148554c0b17f61a01db70757d7e9060200160405180910390a150505050565b600154600160601b90046001600160a01b03163314611b7a5760405162461bcd60e51b8152600401610b5890613f1d565b60005460ff1615611bcd5760405162461bcd60e51b815260206004820152601b60248201527f565f5342433a2054727573746564204372656469746f722053657400000000006044820152606401610b58565b611bd681612735565b50565b600981815481106119d257600080fd5b600154600160601b90046001600160a01b03163314611c1a5760405162461bcd60e51b8152600401610b5890613f1d565b611cba86868080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808a0282810182019093528982529093508992508891829185019084908082843760009201919091525050604080516020808902828101820190935288825290935088925087918291850190849080828437600092019190915250339250612253915050565b6000611cc4610a60565b6001549091506001600160601b0316811115611d285780611ce3611d31565b1015611d285760405162461bcd60e51b8152602060048201526014602482015273565f573a205661756c7420556e6865616c74687960601b6044820152606401610b58565b50505050505050565b600080600080611d3f610652565b60025460048054604051631527047960e11b81529598509396509194506001600160a01b0390811693632a4e08f29361169f938993899389939091169101613f44565b6005818154811061114857600080fd5b600154600160601b90046001600160a01b03163314611dc35760405162461bcd60e51b8152600401610b5890613f1d565b336000818152600c602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c1910160405180910390a35050565b600080611e3a610a60565b6001549091506001600160601b0316811115611e5d5780611e5961164e565b1091505b5090565b600080611e6c611d31565b90506000611e78610a60565b9050808211611e88576000611e8c565b8082035b9250505090565b600260009054906101000a90046001600160a01b03166001600160a01b031663c45a01556040518163ffffffff1660e01b8152600401602060405180830381865afa158015611ee6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f0a9190613e6b565b6001600160a01b0316336001600160a01b031614611f5c5760405162461bcd60e51b815260206004820152600f60248201526e563a204f6e6c7920466163746f727960881b6044820152606401610b58565b6001600160a01b038116611fb25760405162461bcd60e51b815260206004820152601760248201527f565f544f3a20494e56414c49445f524543495049454e540000000000000000006044820152606401610b58565b600180546001600160601b0316600160601b6001600160a01b0384160217905550565b600154600160601b90046001600160a01b031633146120065760405162461bcd60e51b8152600401610b5890613f1d565b60005460ff161561204f5760405162461bcd60e51b81526020600482015260136024820152721597d3d513504e88105314915051164814d155606a1b6044820152606401610b58565b60008054604051638c48052360e01b815261010090910461ffff1660048201528190819081906001600160a01b03861690638c48052390602401608060405180830381865afa1580156120a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120ca9190613e88565b93509350935093508361211f5760405162461bcd60e51b815260206004820152601760248201527f565f4f544d413a20496e76616c69642056657273696f6e0000000000000000006044820152606401610b58565b600080546301000000600160b81b03191663010000006001600160a01b038581169190910291909117909155600380546001600160a01b031916878316179055600180546001600160601b0319166001600160601b0384161790556004548482169116146121905761219083612735565b6000805460ff191660011781556040516001600160a01b0380851692908816917f7c870bf97bf11b7ae68424ab51803b0f4c489bbecd0cddd19f7747f16211f7819190a35050505050565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610bb15760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610b58565b6002546040516301c24e0560e21b81526000916001600160a01b0316906307093814906122889088908890889060040161334d565b6000604051808303816000875af11580156122a7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122cf9190810190613f97565b855190915060005b81811015611d28578481815181106122f1576122f1613811565b6020026020010151600003612308576001016122d7565b82818151811061231a5761231a613811565b602002602001015160000361236b576123668488838151811061233f5761233f613811565b602002602001015187848151811061235957612359613811565b6020026020010151612843565b612489565b82818151811061237d5761237d613811565b60200260200101516001036123c957612366848883815181106123a2576123a2613811565b60200260200101518884815181106123bc576123bc613811565b60200260200101516129e4565b8281815181106123db576123db613811565b6020026020010151600203612441576123668488838151811061240057612400613811565b602002602001015188848151811061241a5761241a613811565b602002602001015188858151811061243457612434613811565b6020026020010151612d03565b60405162461bcd60e51b815260206004820152601760248201527f565f573a20556e6b6e6f776e20617373657420747970650000000000000000006044820152606401610b58565b6001016122d7565b600254604051632303480f60e01b81526000916001600160a01b031690632303480f906124c69088908890889060040161334d565b6000604051808303816000875af11580156124e5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261250d9190810190613f97565b855190915060005b818110156126cf5784818151811061252f5761252f613811565b602002602001015160000361254657600101612515565b82818151811061255857612558613811565b60200260200101516000036125a9576125a48488838151811061257d5761257d613811565b602002602001015187848151811061259757612597613811565b6020026020010151612f90565b6126c7565b8281815181106125bb576125bb613811565b6020026020010151600103612607576125a4848883815181106125e0576125e0613811565b60200260200101518884815181106125fa576125fa613811565b6020026020010151613036565b82818151811061261957612619613811565b602002602001015160020361267f576125a48488838151811061263e5761263e613811565b602002602001015188848151811061265857612658613811565b602002602001015188858151811061267257612672613811565b602002602001015161311f565b60405162461bcd60e51b815260206004820152601760248201527f565f443a20556e6b6e6f776e20617373657420747970650000000000000000006044820152606401610b58565b600101612515565b50600754600654600554600f92916126e69161383d565b6126f0919061383d565b11156114aa5760405162461bcd60e51b8152602060048201526014602482015273565f443a20546f6f206d616e792061737365747360601b6044820152606401610b58565b600254604051639b8bf37d60e01b81526001600160a01b03838116600483015290911690639b8bf37d90602401602060405180830381865afa15801561277f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127a391906138ba565b6127ef5760405162461bcd60e51b815260206004820152601d60248201527f565f5342433a206261736543757272656e6379206e6f7420666f756e640000006044820152606401610b58565b600480546001600160a01b0319166001600160a01b0383169081179091556040519081527f90c340510e0f42fbc86b8f279720f8a069bce148554c0b17f61a01db70757d7e9060200160405180910390a150565b6001600160a01b0382166000908152600a60205260408120805483929061286b90849061386f565b90915550506001600160a01b0382166000908152600a602052604081205490036129d05760055460018190036128d35760058054806128ac576128ac613fcb565b600082815260209020810160001990810180546001600160a01b03191690550190556129ce565b60005b818110156129cc57836001600160a01b0316600582815481106128fb576128fb613811565b6000918252602090912001546001600160a01b0316036129c457600561292260018461386f565b8154811061293257612932613811565b600091825260209091200154600580546001600160a01b03909216918390811061295e5761295e613811565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b03160217905550600580548061299d5761299d613fcb565b600082815260209020810160001990810180546001600160a01b03191690550190556129cc565b6001016128d6565b505b505b610e7a6001600160a01b03831684836121db565b60085460006001829003612af257826008600081548110612a0757612a07613811565b9060005260206000200154148015612a4f5750836001600160a01b03166006600081548110612a3857612a38613811565b6000918252602090912001546001600160a01b0316145b612a935760405162461bcd60e51b81526020600482015260156024820152741597d5cdcc8c4e88155b9adb9bdddb88185cdcd95d605a1b6044820152606401610b58565b6008805480612aa457612aa4613fcb565b600190038181906000526020600020016000905590556006805480612acb57612acb613fcb565b600082815260209020810160001990810180546001600160a01b0319169055019055612cc8565b81811015612c81578260088281548110612b0e57612b0e613811565b9060005260206000200154148015612b555750836001600160a01b031660068281548110612b3e57612b3e613811565b6000918252602090912001546001600160a01b0316145b15612c79576008612b6760018461386f565b81548110612b7757612b77613811565b906000526020600020015460088281548110612b9557612b95613811565b6000918252602090912001556008805480612bb257612bb2613fcb565b600190038181906000526020600020016000905590556006600183612bd7919061386f565b81548110612be757612be7613811565b600091825260209091200154600680546001600160a01b039092169183908110612c1357612c13613811565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506006805480612c5257612c52613fcb565b600082815260209020810160001990810180546001600160a01b0319169055019055612c81565b600101612af2565b818110612cc85760405162461bcd60e51b81526020600482015260156024820152741597d5cdcc8c4e88155b9adb9bdddb88185cdcd95d605a1b6044820152606401610b58565b604051632142170760e11b81523060048201526001600160a01b038681166024830152604482018590528516906342842e0e90606401610d51565b6009546001600160a01b0384166000908152600b6020908152604080832086845290915281208054849290612d3990849061386f565b90915550506001600160a01b0384166000908152600b602090815260408083208684529091528120549003612f5e5780600103612dcf576009805480612d8157612d81613fcb565b600190038181906000526020600020016000905590556007805480612da857612da8613fcb565b600082815260209020810160001990810180546001600160a01b0319169055019055612f5e565b60005b81811015612f5c578360098281548110612dee57612dee613811565b906000526020600020015403612f5457846001600160a01b031660078281548110612e1b57612e1b613811565b6000918252602090912001546001600160a01b031603612f54576009612e4260018461386f565b81548110612e5257612e52613811565b906000526020600020015460098281548110612e7057612e70613811565b6000918252602090912001556009805480612e8d57612e8d613fcb565b600190038181906000526020600020016000905590556007600183612eb2919061386f565b81548110612ec257612ec2613811565b600091825260209091200154600780546001600160a01b039092169183908110612eee57612eee613811565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506007805480612f2d57612f2d613fcb565b600082815260209020810160001990810180546001600160a01b0319169055019055612f5c565b600101612dd2565b505b604051637921219560e11b81526001600160a01b0385169063f242432a90610d51903090899088908890600401613882565b612fa56001600160a01b038316843084613256565b6001600160a01b0382166000908152600a60205260408120549081900361301257600580546001810182556000919091527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db00180546001600160a01b0319166001600160a01b0385161790555b506001600160a01b039091166000908152600a602052604090208054909101905550565b604051632142170760e11b81526001600160a01b038481166004830152306024830152604482018390528316906342842e0e90606401600060405180830381600087803b15801561308657600080fd5b505af115801561309a573d6000803e3d6000fd5b50506006805460018082019092557ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0180546001600160a01b0319166001600160a01b03969096169590951790945550506008805492830181556000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee39091015550565b604051637921219560e11b81526001600160a01b0384169063f242432a90613151908790309087908790600401613882565b600060405180830381600087803b15801561316b57600080fd5b505af115801561317f573d6000803e3d6000fd5b5050506001600160a01b0384166000908152600b602090815260408083208684529091528120549150819003613228576007805460018082019092557fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6880180546001600160a01b0319166001600160a01b0387161790556009805491820181556000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af018390555b506001600160a01b039092166000908152600b60209081526040808320938352929052208054909101905550565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061046a5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610b58565b600081518084526020808501945080840160005b838110156133125781516001600160a01b0316875295820195908201906001016132ed565b509495945050505050565b600081518084526020808501945080840160005b8381101561331257815187529582019590820190600101613331565b60608152600061336060608301866132d9565b8281036020840152613372818661331d565b90508281036040840152613386818561331d565b9695505050505050565b6001600160a01b0381168114611bd657600080fd5b60008083601f8401126133b757600080fd5b5081356001600160401b038111156133ce57600080fd5b6020830191508360208285010111156133e657600080fd5b9250929050565b60008060008060006080868803121561340557600080fd5b853561341081613390565b9450602086013561342081613390565b93506040860135925060608601356001600160401b0381111561344257600080fd5b61344e888289016133a5565b969995985093965092949392505050565b6000806040838503121561347257600080fd5b50508035926020909101359150565b60006020828403121561349357600080fd5b813561349e81613390565b9392505050565b6000806000606084860312156134ba57600080fd5b83356134c581613390565b95602085013595506040909401359392505050565b6000806000604084860312156134ef57600080fd5b83356134fa81613390565b925060208401356001600160401b0381111561351557600080fd5b613521868287016133a5565b9497909650939450505050565b60006020828403121561354057600080fd5b5035919050565b803561ffff8116811461355957600080fd5b919050565b60008060008060006080868803121561357657600080fd5b853561358181613390565b9450602086013561359181613390565b935061359f60408701613547565b925060608601356001600160401b0381111561344257600080fd5b60008083601f8401126135cc57600080fd5b5081356001600160401b038111156135e357600080fd5b6020830191508360208260051b85010111156133e657600080fd5b6000806000806000806060878903121561361757600080fd5b86356001600160401b038082111561362e57600080fd5b61363a8a838b016135ba565b9098509650602089013591508082111561365357600080fd5b61365f8a838b016135ba565b9096509450604089013591508082111561367857600080fd5b5061368589828a016135ba565b979a9699509497509295939492505050565b600080604083850312156136aa57600080fd5b82356136b581613390565b915060208301356136c581613390565b809150509250929050565b600080604083850312156136e357600080fd5b82356136ee81613390565b946020939093013593505050565b6000806000806080858703121561371257600080fd5b843561371d81613390565b9350602085013561372d81613390565b925061373b60408601613547565b9150606085013561374b81613390565b939692955090935050565b8015158114611bd657600080fd5b6000806040838503121561377757600080fd5b823561378281613390565b915060208301356136c581613756565b60008060008060008060a087890312156137ab57600080fd5b86356137b681613390565b955060208701356137c681613390565b9450604087013593506060870135925060808701356001600160401b038111156137ef57600080fd5b61368589828a016133a5565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8082018082111561385057613850613827565b92915050565b60006020828403121561386857600080fd5b5051919050565b8181038181111561385057613850613827565b6001600160a01b0394851681529290931660208301526040820152606081019190915260a06080820181905260009082015260c00190565b6000602082840312156138cc57600080fd5b815161349e81613756565b60405160a081016001600160401b03811182821017156138f9576138f96137fb565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613927576139276137fb565b604052919050565b60006001600160401b03821115613948576139486137fb565b5060051b60200190565b600082601f83011261396357600080fd5b813560206139786139738361392f565b6138ff565b82815260059290921b8401810191818101908684111561399757600080fd5b8286015b848110156139bb5780356139ae81613390565b835291830191830161399b565b509695505050505050565b600082601f8301126139d757600080fd5b813560206139e76139738361392f565b82815260059290921b84018101918181019086841115613a0657600080fd5b8286015b848110156139bb5780358352918301918301613a0a565b600060a08284031215613a3357600080fd5b613a3b6138d7565b905081356001600160401b0380821115613a5457600080fd5b613a6085838601613952565b83526020840135915080821115613a7657600080fd5b613a82858386016139c6565b60208401526040840135915080821115613a9b57600080fd5b613aa7858386016139c6565b60408401526060840135915080821115613ac057600080fd5b613acc858386016139c6565b60608401526080840135915080821115613ae557600080fd5b50613af2848285016139c6565b60808301525092915050565b60008060008060808587031215613b1457600080fd5b84356001600160401b0380821115613b2b57600080fd5b613b3788838901613a21565b9550602091508187013581811115613b4e57600080fd5b613b5a89828a01613a21565b955050604087013581811115613b6f57600080fd5b613b7b89828a01613952565b945050606087013581811115613b9057600080fd5b8701601f81018913613ba157600080fd5b8035613baf6139738261392f565b81815260059190911b8201840190848101908b831115613bce57600080fd5b8584015b83811015613c5a57803586811115613bea5760008081fd5b8501603f81018e13613bfc5760008081fd5b8781013587811115613c1057613c106137fb565b613c22601f8201601f19168a016138ff565b8181528f6040838501011115613c385760008081fd5b81604084018b83013760009181018a0191909152845250918601918601613bd2565b50989b979a50959850505050505050565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b602081526000613ca8602083018486613c6b565b949350505050565b600082601f830112613cc157600080fd5b81516020613cd16139738361392f565b82815260059290921b84018101918181019086841115613cf057600080fd5b8286015b848110156139bb578051613d0781613390565b8352918301918301613cf4565b600082601f830112613d2557600080fd5b81516020613d356139738361392f565b82815260059290921b84018101918181019086841115613d5457600080fd5b8286015b848110156139bb5780518352918301918301613d58565b600060208284031215613d8157600080fd5b81516001600160401b0380821115613d9857600080fd5b9083019060a08286031215613dac57600080fd5b613db46138d7565b825182811115613dc357600080fd5b613dcf87828601613cb0565b825250602083015182811115613de457600080fd5b613df087828601613d14565b602083015250604083015182811115613e0857600080fd5b613e1487828601613d14565b604083015250606083015182811115613e2c57600080fd5b613e3887828601613d14565b606083015250608083015182811115613e5057600080fd5b613e5c87828601613d14565b60808301525095945050505050565b600060208284031215613e7d57600080fd5b815161349e81613390565b60008060008060808587031215613e9e57600080fd5b8451613ea981613756565b6020860151909450613eba81613390565b6040860151909350613ecb81613390565b6060959095015193969295505050565b6001600160a01b0386811682528516602082015261ffff84166040820152608060608201819052600090613f129083018486613c6b565b979650505050505050565b6020808252600d908201526c2b1d1027b7363c9027bbb732b960991b604082015260600190565b608081526000613f5760808301876132d9565b8281036020840152613f69818761331d565b90508281036040840152613f7d818661331d565b91505060018060a01b038316606083015295945050505050565b600060208284031215613fa957600080fd5b81516001600160401b03811115613fbf57600080fd5b613ca884828501613d14565b634e487b7160e01b600052603160045260246000fdfea26469706673582212209dd459a617f2da66eb5892be19b50ccc37d78362049434eb8aef36258b40678964736f6c63430008110033

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

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