ETH Price: $2,901.76 (-1.24%)

Contract

0x78Db6a136EdD0F70bEd7a6eb5ca2fDF6eE16E8D6

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

Please try again later

View more zero value Internal Transactions in Advanced View mode

Advanced mode:

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
AccountV3

Compiler Version
v0.8.30+commit.73712a01

Optimization Enabled:
Yes with 200 runs

Other Settings:
prague EvmVersion
File 1 of 18 : AccountV3.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.30;

import { AccountErrors } from "../libraries/Errors.sol";
import { AccountStorageV1 } from "./AccountStorageV1.sol";
import { ActionData, IActionBase } from "../interfaces/IActionBase.sol";
import { AssetValuationLib, AssetValueAndRiskFactors } from "../libraries/AssetValuationLib.sol";
import { ERC20, SafeTransferLib } from "../../lib/solmate/src/utils/SafeTransferLib.sol";
import { IAccount } from "../interfaces/IAccount.sol";
import { IAccountsGuard } from "../interfaces/IAccountsGuard.sol";
import { IAssetManager } from "../interfaces/IAssetManager.sol";
import { ICreditor } from "../interfaces/ICreditor.sol";
import { IDistributor } from "../interfaces/IDistributor.sol";
import { IERC721 } from "../interfaces/IERC721.sol";
import { IERC1155 } from "../interfaces/IERC1155.sol";
import { IFactory } from "../interfaces/IFactory.sol";
import { IMerklOperator } from "../interfaces/IMerklOperator.sol";
import { IPermit2 } from "../interfaces/IPermit2.sol";
import { IRegistry } from "../interfaces/IRegistry.sol";

/**
 * @title Arcadia Accounts
 * @author Pragma Labs
 * @notice Arcadia Accounts 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 Account to deposit assets (fungible, non-fungible, LP positions, yield bearing assets...).
 * The Account will denominate all the deposited assets into one Numeraire (one unit of account, like USD or ETH).
 * Users can use the single denominated value of all their assets to take margin (take credit line, financing for leverage...).
 * An increase of value of one asset will offset a decrease in value of another asset.
 * Ensure your total value denomination remains above the liquidation threshold, or risk being liquidated!
 * @dev Integrating this Account as means of margin/collateral management for your own protocol that requires collateral is encouraged.
 * Arcadia's Account functions will guarantee you a certain value of the Account.
 * For allowlists or liquidation strategies specific to your protocol, contact pragmalabs.dev
 */
contract AccountV3 is AccountStorageV1, IAccount {
    using AssetValuationLib for AssetValueAndRiskFactors[];
    using SafeTransferLib for ERC20;

    /* //////////////////////////////////////////////////////////////
                                CONSTANTS
    ////////////////////////////////////////////////////////////// */

    // Bools indicating if the function being called can be paused by the AccountsGuard.
    bool internal constant WITH_PAUSE_CHECK = true;
    bool internal constant WITHOUT_PAUSE_CHECK = false;

    // The current Account Version.
    uint256 public constant ACCOUNT_VERSION = 3;
    // The maximum amount of different assets that can be used as collateral within an Arcadia Account.
    uint256 public constant ASSET_LIMIT = 15;
    // The cool-down period after an account action, that might be disadvantageous for a new Owner,
    // during which ownership cannot be transferred to prevent the old Owner from frontrunning a transferFrom().
    uint256 internal constant COOL_DOWN_PERIOD = 5 minutes;
    // 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 contract address of the Arcadia Accounts Factory.
    address public immutable FACTORY;
    // The contract address of the Accounts Guard.
    IAccountsGuard public immutable ACCOUNTS_GUARD;
    // The contract address of the Merkl Distributor.
    IDistributor public immutable MERKL_DISTRIBUTOR;
    // Uniswap Permit2 contract.
    IPermit2 internal immutable PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3);

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

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

    event AssetManagerSet(address indexed owner, address indexed assetManager, bool status);
    event MarginAccountChanged(address indexed creditor, address indexed liquidator);
    event MerklOperatorSet(address indexed merklOperator, bool status);
    event NumeraireSet(address indexed numeraire);
    event Skim(address indexed from, address indexed to, address asset, uint256 id, uint256 amount, uint256 type_);
    event Transfers(
        address indexed from, address indexed to, address[] assets, uint256[] ids, uint256[] amounts, uint256[] types
    );

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

    /**
     * @param pauseCheck Bool indicating if a pause check should be done.
     * @dev Throws if cross accounts guard is reentered or paused.
     * @dev Locks/unlocks the cross accounts guard before/after the function is executed.
     */
    modifier nonReentrant(bool pauseCheck) {
        ACCOUNTS_GUARD.lock(pauseCheck);
        _;
        ACCOUNTS_GUARD.unLock();
    }

    /**
     * @dev Throws if called when the Account is in an auction.
     */
    modifier notDuringAuction() {
        if (inAuction) revert AccountErrors.AccountInAuction();
        _;
    }

    /**
     * @dev Throws if called by any address other than an Asset Manager or the owner.
     */
    modifier onlyAssetManager() {
        // A custom error would need to read out owner + isAssetManager storage
        require(msg.sender == owner || isAssetManager[owner][msg.sender], "A: Only Asset Manager");
        _;
    }

    /**
     * @dev Throws if called by any address other than the Creditor.
     */
    modifier onlyCreditor() {
        if (msg.sender != creditor) revert AccountErrors.OnlyCreditor();
        _;
    }

    /**
     * @dev Throws if called by any address other than the Factory address.
     */
    modifier onlyFactory() {
        if (msg.sender != FACTORY) revert AccountErrors.OnlyFactory();
        _;
    }

    /**
     * @dev Throws if called by any address other than the Liquidator address.
     */
    modifier onlyLiquidator() {
        if (msg.sender != liquidator) revert AccountErrors.OnlyLiquidator();
        _;
    }

    /**
     * @dev Throws if called by any address other than the owner.
     */
    modifier onlyOwner() {
        if (msg.sender != owner) revert AccountErrors.OnlyOwner();
        _;
    }

    /**
     * @dev Starts the cool-down period during which ownership cannot be transferred.
     * This prevents the old Owner from frontrunning a transferFrom().
     */
    modifier updateActionTimestamp() {
        lastActionTimestamp = uint32(block.timestamp);
        _;
    }

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

    /**
     * @param factory The contract address of the Arcadia Accounts Factory.
     * @param accountsGuard The contract address of the Accounts Guard.
     * @param merklDistributor The contract address of the Merkl Distributor.
     */
    constructor(address factory, address accountsGuard, address merklDistributor) {
        // This will only be the owner of the Account implementation.
        // and will not affect any subsequent proxy implementation using this Account implementation.
        owner = msg.sender;

        FACTORY = factory;
        ACCOUNTS_GUARD = IAccountsGuard(accountsGuard);
        MERKL_DISTRIBUTOR = IDistributor(merklDistributor);
    }

    /* ///////////////////////////////////////////////////////////////
                          ACCOUNT MANAGEMENT
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Initiates the variables of the Account.
     * @param owner_ The sender of the 'createAccount' on the Factory
     * @param registry_ The 'beacon' contract with the external logic to price assets.
     * @param creditor_ The contract address of the Creditor.
     * @dev A proxy will be used to interact with the Account implementation.
     * This function will only be called (once) in the same transaction as the proxy Account creation through the Factory.
     * @dev The Creditor will only be set if it's a non-zero address, in this case the numeraire_ passed as input will be ignored.
     */
    function initialize(address owner_, address registry_, address creditor_)
        external
        onlyFactory
        nonReentrant(WITH_PAUSE_CHECK)
    {
        if (registry_ == address(0)) revert AccountErrors.InvalidRegistry();
        owner = owner_;
        registry = registry_;

        if (creditor_ != address(0)) _openMarginAccount(creditor_);
    }

    /**
     * @notice Upgrades the Account version and stores a new address in the EIP1967 implementation slot.
     * @param newImplementation The new contract address of the Account implementation.
     * @param newRegistry The Registry for this specific implementation (might be identical to the old registry).
     * @param data Arbitrary data, can contain instructions to execute when updating Account to new implementation.
     * @param newVersion The new version of the Account implementation.
     * @dev This function MUST be added to new Account implementations.
     */
    function upgradeAccount(address newImplementation, address newRegistry, uint256 newVersion, bytes calldata data)
        external
        onlyFactory
        nonReentrant(WITHOUT_PAUSE_CHECK)
        notDuringAuction
        updateActionTimestamp
    {
        // Cache old parameters.
        address oldImplementation = _getAddressSlot(IMPLEMENTATION_SLOT).value;
        address oldRegistry = registry;
        uint256 oldVersion = ACCOUNT_VERSION;

        // Store new parameters.
        _getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
        registry = newRegistry;

        // Prevent that Account is upgraded to a new version where the Numeraire can't be priced.
        if (newRegistry != oldRegistry && !IRegistry(newRegistry).inRegistry(numeraire)) {
            revert AccountErrors.InvalidRegistry();
        }

        // If a Creditor is set, new version should be compatible.
        if (creditor != address(0)) {
            (bool success,,,) = ICreditor(creditor).openMarginAccount(newVersion);
            if (!success) revert AccountErrors.InvalidAccountVersion();
        }

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

        // Event emitted by Factory.
    }

    /**
     * @notice Returns the "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 Account version on the new implementation Contract.
     * @param oldImplementation The old contract address of the Account implementation.
     * @param oldRegistry The Registry of the old version (might be identical to the new registry)
     * @param oldVersion The old version of the Account implementation.
     * @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, uint256 oldVersion, bytes calldata data)
        external { }

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

    /**
     * @notice Transfers ownership of the contract to a new Account.
     * @param newOwner The new owner of the Account.
     * @dev Can only be called by the current owner via the Factory.
     * A transfer of ownership of the Account is triggered by a transfer
     * of ownership of the accompanying ERC721 Account NFT, issued by the Factory.
     * Owner of Account NFT = owner of Account
     * @dev Function uses a cool-down period during which ownership cannot be transferred.
     * Cool-down period is triggered after any account action, that might be disadvantageous for a new Owner.
     * This prevents the old Owner from frontrunning a transferFrom().
     */
    function transferOwnership(address newOwner)
        external
        onlyFactory
        nonReentrant(WITHOUT_PAUSE_CHECK)
        notDuringAuction
    {
        if (block.timestamp <= lastActionTimestamp + COOL_DOWN_PERIOD) {
            revert AccountErrors.CoolDownPeriodNotPassed();
        }

        // The Factory will check that the new owner is not address(0).
        owner = newOwner;
    }

    function _transferOwnership(address newOwner) internal {
        // The Factory will check that the new owner is not address(0).
        owner = newOwner;
        IFactory(FACTORY).safeTransferAccount(newOwner);
    }

    /* ///////////////////////////////////////////////////////////////
                        NUMERAIRE LOGIC
    /////////////////////////////////////////////////////////////// */

    /**
     * @notice Sets the Numeraire of the Account.
     * @param numeraire_ The new Numeraire for the Account.
     */
    function setNumeraire(address numeraire_) external onlyOwner nonReentrant(WITH_PAUSE_CHECK) {
        if (creditor != address(0)) revert AccountErrors.CreditorAlreadySet();
        _setNumeraire(numeraire_);
    }

    /**
     * @notice Sets the Numeraire of the Account.
     * @param numeraire_ The new Numeraire for the Account.
     */
    function _setNumeraire(address numeraire_) internal {
        if (!IRegistry(registry).inRegistry(numeraire_)) revert AccountErrors.NumeraireNotFound();

        emit NumeraireSet(numeraire = numeraire_);
    }

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

    /**
     * @notice Opens a margin account on the Account for a new Creditor.
     * @param newCreditor The contract address of the Creditor.
     * @dev Currently only one Creditor can be set
     * (we are working towards a single account for multiple Creditors tho!).
     * @dev Only open margin accounts for Creditors you trust!
     * The Creditor has significant authorization: use margin, trigger liquidation, and manage assets.
     */
    function openMarginAccount(address newCreditor)
        external
        onlyOwner
        nonReentrant(WITH_PAUSE_CHECK)
        notDuringAuction
        updateActionTimestamp
    {
        (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
            generateAssetData();

        // Cache old Creditor.
        address oldCreditor = creditor;
        if (oldCreditor == newCreditor) revert AccountErrors.CreditorAlreadySet();

        // Remove the exposures of the Account for the old Creditor.
        if (oldCreditor != address(0)) {
            IRegistry(registry).batchProcessWithdrawal(oldCreditor, assetAddresses, assetIds, assetAmounts);
        }

        // Check if all assets in the Account are allowed by the new Creditor
        // and add the exposure of the account for the new Creditor.
        IRegistry(registry).batchProcessDeposit(newCreditor, assetAddresses, assetIds, assetAmounts);

        // Open margin account for the new Creditor.
        _openMarginAccount(newCreditor);

        // A margin account can only be opened for one Creditor at a time.
        // If set, close the margin account for the old Creditor.
        if (oldCreditor != address(0)) {
            // closeMarginAccount() checks if there is still an open position (open liabilities) of the Account for the old Creditor.
            // If so, the function reverts.
            ICreditor(oldCreditor).closeMarginAccount(address(this));
        }
    }

    /**
     * @notice Internal function: Opens a margin account for a new Creditor.
     * @param creditor_ The contract address of the Creditor.
     */
    function _openMarginAccount(address creditor_) internal {
        (bool success, address numeraire_, address liquidator_, uint256 minimumMargin_) =
            ICreditor(creditor_).openMarginAccount(ACCOUNT_VERSION);
        if (!success) revert AccountErrors.InvalidAccountVersion();

        // forge-lint: disable-next-line(unsafe-typecast)
        minimumMargin = uint96(minimumMargin_);
        if (numeraire != numeraire_) _setNumeraire(numeraire_);

        emit MarginAccountChanged(creditor = creditor_, liquidator = liquidator_);
    }

    /**
     * @notice Closes the margin account of the Creditor.
     * @dev Currently only one Creditor can be set.
     */
    function closeMarginAccount() external onlyOwner nonReentrant(WITHOUT_PAUSE_CHECK) notDuringAuction {
        // Cache creditor.
        address creditor_ = creditor;
        if (creditor_ == address(0)) revert AccountErrors.CreditorNotSet();

        creditor = address(0);
        liquidator = address(0);
        minimumMargin = 0;

        // Remove the exposures of the Account for the old Creditor.
        (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
            generateAssetData();
        IRegistry(registry).batchProcessWithdrawal(creditor_, assetAddresses, assetIds, assetAmounts);

        // closeMarginAccount() checks if there is still an open position (open liabilities) for the Account.
        // If so, the function reverts.
        ICreditor(creditor_).closeMarginAccount(address(this));

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

    /**
     * @notice Sets an approved Creditor.
     * @param creditor_ The contract address of the approved Creditor.
     * @dev An approved Creditor is a Creditor for which no margin Account is immediately opened.
     * But the approved Creditor itself can open the margin Account later in time to e.g. refinance liabilities.
     * @dev Potential use-cases of the approved Creditor might be to:
     * - Refinance liabilities (change creditor) without having to sell collateral to close the current position first.
     * @dev Anyone can set the approved creditor for themselves, this will not impact the current owner of the Account
     * since the combination of "current owner -> approved creditor" is used in authentication checks.
     * This guarantees that when the ownership of the Account is transferred, the approved Creditor of the old owner has no
     * impact on the new owner. But the new owner can still remove any existing approved Creditors before the transfer.
     */
    function setApprovedCreditor(address creditor_) external nonReentrant(WITH_PAUSE_CHECK) {
        approvedCreditor[msg.sender] = creditor_;
    }

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

    /**
     * @notice Calculates the total collateral value (MTM discounted with a haircut) of the Account.
     * @return collateralValue The collateral value, returned in the decimal precision of the Numeraire.
     * @dev Returns the value denominated in the Numeraire of the Account.
     * @dev The collateral value of the Account is equal to the spot value of the underlying assets,
     * discounted by a haircut (the collateral factor). Since the value of
     * collateralized assets can fluctuate, the haircut guarantees that the Account
     * remains over-collateralized with a high confidence level.
     * The size of the haircut depends on the underlying risk of the assets in the Account.
     * The bigger the volatility or the smaller the onchain 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 =
            IRegistry(registry).getCollateralValue(numeraire, creditor, assetAddresses, assetIds, assetAmounts);
    }

    /**
     * @notice Calculates the total liquidation value (MTM discounted with a factor to account for slippage) of the Account.
     * @return liquidationValue The liquidation value, returned in the decimal precision of the Numeraire.
     * @dev The liquidation value of the Account 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 liabilities: a fraction of the value is lost due to:
     * slippage while liquidating the assets,
     * fees for the auction initiator,
     * fees for the auction terminator and
     * a penalty to the protocol.
     */
    function getLiquidationValue() public view returns (uint256 liquidationValue) {
        (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
            generateAssetData();
        liquidationValue =
            IRegistry(registry).getLiquidationValue(numeraire, creditor, assetAddresses, assetIds, assetAmounts);
    }

    /**
     * @notice Returns the used margin of the Account.
     * @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 Account.
     *  - An additional fixed buffer to cover gas fees in case of a liquidation.
     * @dev The used margin is denominated in the Numeraire.
     * @dev Currently only one Creditor at a time can open a margin account.
     * The open liability is fetched at the contract of the Creditor -> only allow trusted audited Creditors!!!
     */
    function getUsedMargin() public view returns (uint256 usedMargin) {
        // Cache creditor
        address creditor_ = creditor;
        if (creditor_ == address(0)) return 0;

        // getOpenPosition() is a view function, cannot modify state.
        usedMargin = ICreditor(creditor_).getOpenPosition(address(this)) + minimumMargin;
    }

    /**
     * @notice Calculates the remaining margin the owner of the Account 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 Numeraire.
     */
    function getFreeMargin() public view returns (uint256 freeMargin) {
        uint256 collateralValue = getCollateralValue();
        uint256 usedMargin = getUsedMargin();

        unchecked {
            freeMargin = collateralValue > usedMargin ? collateralValue - usedMargin : 0;
        }
    }

    /**
     * @notice Checks if the Account is unhealthy.
     * @return isUnhealthy Boolean indicating if the Account is unhealthy.
     */
    function isAccountUnhealthy() public view returns (bool isUnhealthy) {
        // If usedMargin is equal to minimumMargin, the open liabilities are 0 and the Account is always healthy.
        // An Account is unhealthy if the collateral value is smaller than the used margin.
        uint256 usedMargin = getUsedMargin();
        isUnhealthy = usedMargin > minimumMargin && getCollateralValue() < usedMargin;
    }

    /**
     * @notice Checks if the Account can be liquidated.
     * @return success Boolean indicating if the Account can be liquidated.
     */
    function isAccountLiquidatable() external view returns (bool success) {
        // If usedMargin is equal to minimumMargin, the open liabilities are 0 and the Account is never liquidatable.
        // An Account can be liquidated if the liquidation value is smaller than the used margin.
        uint256 usedMargin = getUsedMargin();
        success = usedMargin > minimumMargin && getLiquidationValue() < usedMargin;
    }

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

    /**
     * @notice Checks if an Account is liquidatable and continues the liquidation flow.
     * @param initiator The address of the liquidation initiator.
     * @return assetAddresses Array of the contract addresses of the assets in Account.
     * @return assetIds Array of the IDs of the assets in Account.
     * @return assetAmounts Array with the amounts of the assets in Account.
     * @return creditor_ The contract address of the Creditor.
     * @return minimumMargin_ The minimum margin.
     * @return openPosition The open position (liabilities) issued against the Account.
     * @return assetAndRiskValues Array of asset values and corresponding collateral and liquidation factors.
     */
    function startLiquidation(address initiator)
        external
        onlyLiquidator
        nonReentrant(WITHOUT_PAUSE_CHECK)
        updateActionTimestamp
        returns (
            address[] memory assetAddresses,
            uint256[] memory assetIds,
            uint256[] memory assetAmounts,
            address creditor_,
            uint96 minimumMargin_,
            uint256 openPosition,
            AssetValueAndRiskFactors[] memory assetAndRiskValues
        )
    {
        inAuction = true;
        creditor_ = creditor;
        minimumMargin_ = minimumMargin;

        (assetAddresses, assetIds, assetAmounts) = generateAssetData();
        assetAndRiskValues =
            IRegistry(registry).getValuesInNumeraire(numeraire, creditor_, assetAddresses, assetIds, assetAmounts);

        // Since the function is only callable by the Liquidator, we know that a liquidator and a Creditor are set.
        openPosition = ICreditor(creditor_).startLiquidation(initiator, minimumMargin_);
        uint256 usedMargin = openPosition + minimumMargin_;

        if (openPosition == 0 || assetAndRiskValues._calculateLiquidationValue() >= usedMargin) {
            revert AccountErrors.AccountNotLiquidatable();
        }
    }

    /**
     * @notice Transfers the asset bought by a bidder during a liquidation event.
     * @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 bidder The address of the bidder.
     * @return assetAmounts_ Array with the actual transferred amounts of assets.
     */
    function auctionBid(
        address[] memory assetAddresses,
        uint256[] memory assetIds,
        uint256[] memory assetAmounts,
        address bidder
    ) external onlyLiquidator nonReentrant(WITHOUT_PAUSE_CHECK) returns (uint256[] memory assetAmounts_) {
        uint256[] memory assetTypes = IRegistry(registry).batchGetAssetTypes(assetAddresses);
        uint256 balance;
        for (uint256 i; i < assetAddresses.length; ++i) {
            // Skip if amount is 0.
            if (assetAmounts[i] == 0) continue;

            if (assetTypes[i] == 1) {
                balance = erc20Balances[assetAddresses[i]];
            } else if (assetTypes[i] == 2) {
                balance = (address(this) == IERC721(assetAddresses[i]).ownerOf(assetIds[i])) ? 1 : 0;
            } else if (assetTypes[i] == 3) {
                balance = erc1155Balances[assetAddresses[i]][assetIds[i]];
            } else {
                revert AccountErrors.UnknownAssetType();
            }
            if (assetAmounts[i] > balance) assetAmounts[i] = balance;
        }

        _withdraw(assetAddresses, assetIds, assetAmounts, bidder);

        // Return the actual withdrawn amounts.
        assetAmounts_ = assetAmounts;
    }

    /**
     * @notice Transfers all assets of the Account in case the auction did not end successful (= Bought In).
     * @param recipient The recipient address to receive the assets, set by the Creditor.
     * @dev When an auction is not successful, the Account is considered "Bought In":
     * The whole Account including any remaining assets are transferred to a certain recipient address, set by the Creditor.
     */
    function auctionBoughtIn(address recipient) external onlyLiquidator nonReentrant(WITHOUT_PAUSE_CHECK) {
        _transferOwnership(recipient);
    }

    /**
     * @notice Sets the "inAuction" flag to false when an auction ends.
     */
    function endAuction() external onlyLiquidator nonReentrant(WITHOUT_PAUSE_CHECK) {
        inAuction = false;
    }

    /*///////////////////////////////////////////////////////////////
                       ASSET MANAGER ACTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Removes an Asset Manager.
     * @param assetManager The address of the Asset Manager.
     * @dev Anyone can remove an Asset Manager for themselves, this will not impact the current owner of the Account
     * since the combination of "stored owner -> asset manager" is used in authentication checks.
     * This guarantees that when the ownership of the Account is transferred, the asset managers of the old owner have no
     * impact on the new owner. But the new owner can still remove any existing asset managers before the transfer.
     */
    function removeAssetManager(address assetManager) external nonReentrant(WITHOUT_PAUSE_CHECK) {
        emit AssetManagerSet(msg.sender, assetManager, isAssetManager[msg.sender][assetManager] = false);
    }

    /**
     * @notice Adds, removes or modifies Asset Managers.
     * @param assetManagers Array of Asset Managers.
     * @param statuses Array of Bools indicating if the corresponding Asset Manager should be enabled or disabled.
     * @param datas Array of calldata optionally passed to the corresponding Asset Manager via hook.
     * @dev Only set trusted addresses as Asset Manager. Asset Managers have full control over assets in the Account.
     * @dev No need to set the Owner as Asset Manager as they will automatically have all permissions of an Asset Manager.
     * @dev Potential use-cases of the Asset Manager might be to:
     * - Liquidity Management.
     * - Do flash actions (optimistic actions).
     * - Compounding.
     * - Chain multiple interactions together.
     */
    function setAssetManagers(address[] calldata assetManagers, bool[] calldata statuses, bytes[] calldata datas)
        external
        onlyOwner
        nonReentrant(WITH_PAUSE_CHECK)
        updateActionTimestamp
    {
        if (assetManagers.length != statuses.length || assetManagers.length != datas.length) {
            revert AccountErrors.LengthMismatch();
        }

        address assetManager;
        for (uint256 i; i < assetManagers.length; ++i) {
            assetManager = assetManagers[i];
            emit AssetManagerSet(msg.sender, assetManager, isAssetManager[msg.sender][assetManager] = statuses[i]);

            // Optionally call Hook on the Asset Manager to initialize/update it.
            if (datas[i].length > 0) {
                IAssetManager(assetManager).onSetAssetManager(msg.sender, statuses[i], datas[i]);
            }
        }
    }

    /**
     * @notice Executes a flash action.
     * @param actionTarget The contract address of the actionTarget to execute external logic.
     * @param actionData A bytes object containing three structs and two bytes objects.
     * The first struct contains the info about the assets to withdraw from this Account to the actionTarget.
     * The second struct contains the info about the owner's assets that need to be transferred from the owner to the actionTarget.
     * The third struct contains the permit for the Permit2 transfer.
     * The first bytes object contains the signature for the Permit2 transfer.
     * The second bytes object contains the encoded input for the actionTarget.
     * @dev This function optimistically chains multiple actions together (= do a flash action):
     * - It can optimistically withdraw assets from the Account to the actionTarget.
     * - It can transfer assets directly from the owner to the actionTarget.
     * - It can execute external logic on the actionTarget, and interact with any DeFi protocol to swap, stake, claim...
     * - It can deposit all recipient tokens from the actionTarget back into the Account.
     * At the very end of the flash action, the following check is performed:
     * - The Account is in a healthy state (collateral value is greater than open liabilities).
     * If a check fails, the whole transaction reverts.
     */
    function flashAction(address actionTarget, bytes calldata actionData)
        external
        onlyAssetManager
        nonReentrant(WITH_PAUSE_CHECK)
        notDuringAuction
        updateActionTimestamp
    {
        // Decode flash action data.
        (
            ActionData memory withdrawData,
            ActionData memory transferFromOwnerData,
            IPermit2.PermitBatchTransferFrom memory permit,
            bytes memory signature,
            bytes memory actionTargetData
        ) = abi.decode(actionData, (ActionData, ActionData, IPermit2.PermitBatchTransferFrom, bytes, bytes));

        // Withdraw assets to the actionTarget.
        _withdraw(withdrawData.assets, withdrawData.assetIds, withdrawData.assetAmounts, actionTarget);

        // Transfer assets from owner (that are not assets in this account) to the actionTarget.
        _transferFromOwner(transferFromOwnerData, actionTarget);

        // If the function input includes non-empty token permissions,
        // initiate a transfer from the owner to the actionTarget via Permit2.
        _transferFromOwnerWithPermit(permit, signature, actionTarget);

        // Execute external logic on the actionTarget.
        ActionData memory depositData = IActionBase(actionTarget).executeAction(actionTargetData);

        // Deposit assets from actionTarget into Account.
        _deposit(depositData.assets, depositData.assetIds, depositData.assetAmounts, actionTarget);

        // Account must be healthy after actions are executed.
        if (isAccountUnhealthy()) revert AccountErrors.AccountUnhealthy();
    }

    /**
     * @notice Removes a Merkl Operator.
     * @param operator The merkl operator.
     */
    function removeMerklOperator(address operator) external onlyOwner nonReentrant(WITHOUT_PAUSE_CHECK) {
        bool enabled = MERKL_DISTRIBUTOR.operators(address(this), operator) > 0;
        if (enabled) MERKL_DISTRIBUTOR.toggleOperator(address(this), operator);

        emit MerklOperatorSet(operator, false);
    }

    /**
     * @notice Manages Merkl Operators.
     * @param operators Array of merkl operators.
     * @param operatorStatuses Array of Bools indicating if the corresponding operator should be enabled or disabled.
     * @param operatorDatas Array of calldata optionally passed to the corresponding merkl operator via hook.
     * @param recipient The address of the recipient of the merkl rewards for each of the tokens.
     * @param tokens Array of tokens for which the recipient will be set.
     * @dev A Merkl Operator can claim any pending merkl rewards on behalf of the Account.
     * @dev The recipient will receive the Merkl rewards and only one recipient can be set per token.
     * The Recipient can be the operator itself, the Account itself, the Account owner or any other address.
     * It is up to the Account owner to add/remove/change recipient for some/all tokens when adding/removing/changing operators.
     * @dev The Operator and recipients are NOT reset when transferring ownership of the Account.
     * Since the Account is not involved in the claiming flow, a check on owner must be implemented in the Operator.
     */
    function setMerklOperators(
        address[] calldata operators,
        bool[] calldata operatorStatuses,
        bytes[] calldata operatorDatas,
        address recipient,
        address[] calldata tokens
    ) external onlyOwner nonReentrant(WITH_PAUSE_CHECK) updateActionTimestamp {
        if (operators.length != operatorStatuses.length || operators.length != operatorDatas.length) {
            revert AccountErrors.LengthMismatch();
        }

        address operator;
        bool currentStatus;
        for (uint256 i; i < operators.length; ++i) {
            operator = operators[i];
            // If the current status is different from the desired status, toggle the status of the operator.
            currentStatus = MERKL_DISTRIBUTOR.operators(address(this), operator) > 0;
            if (operatorStatuses[i] != currentStatus) MERKL_DISTRIBUTOR.toggleOperator(address(this), operator);

            // Optionally call Hook on the Operator to initialize/update it.
            if (operatorDatas[i].length > 0) {
                IMerklOperator(operator).onSetMerklOperator(msg.sender, operatorStatuses[i], operatorDatas[i]);
            }

            emit MerklOperatorSet(operator, operatorStatuses[i]);
        }

        // If provided, set recipient for tokens.
        for (uint256 j; j < tokens.length; ++j) {
            MERKL_DISTRIBUTOR.setClaimRecipient(recipient, tokens[j]);
        }
    }

    /*///////////////////////////////////////////////////////////////
                        CREDITOR ACTIONS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Checks that the increase of the open position is allowed.
     * @param openPosition The new open position.
     * @return accountVersion The current Account version.
     * @dev The Account performs the following checks when an open position (liabilities) are increased:
     *  - The caller is indeed a Creditor for which a margin account is opened.
     *  - The Account is still healthy after given the new open position.
     */
    function increaseOpenPosition(uint256 openPosition)
        external
        onlyCreditor
        nonReentrant(WITH_PAUSE_CHECK)
        notDuringAuction
        updateActionTimestamp
        returns (uint256 accountVersion)
    {
        // If the open position is 0, the Account is always healthy.
        // An Account is unhealthy if the collateral value is smaller than the used margin.
        // The used margin equals the sum of the given open position and the minimum margin.
        if (openPosition > 0 && getCollateralValue() < openPosition + minimumMargin) {
            revert AccountErrors.AccountUnhealthy();
        }

        accountVersion = ACCOUNT_VERSION;
    }

    /**
     * @notice Executes a flash action initiated by a Creditor.
     * @param callbackData A bytes object containing the data to execute arbitrary logic on the Creditor.
     * @param actionTarget The contract address of the actionTarget to execute external logic.
     * @param actionData A bytes object containing three structs and two bytes objects.
     * The first struct contains the info about the assets to withdraw from this Account to the actionTarget.
     * The second struct contains the info about the owner's assets that need to be transferred from the owner to the actionTarget.
     * The third struct contains the permit for the Permit2 transfer.
     * The first bytes object contains the signature for the Permit2 transfer.
     * The second bytes object contains the encoded input for the actionTarget.
     * @return accountVersion The current Account version.
     * @dev This function optimistically chains multiple actions together (= do a flash action):
     * - A margin Account can be opened for a new Creditor, if the new Creditor is approved by the Account Owner.
     * - It can execute arbitrary logic on the Creditor (e.g. give a flashloan to the actionTarget).
     * - It can optimistically withdraw assets from the Account to the actionTarget.
     * - It can transfer assets directly from the owner to the actionTarget.
     * - It can execute external logic on the actionTarget, and interact with any DeFi protocol to swap, stake, claim...
     * - It can deposit all recipient tokens from the actionTarget back into the Account.
     * At the very end of the flash action, the following checks are performed:
     * - The Account is in a healthy state (collateral value is greater than open liabilities).
     * - If a new margin Account is opened for a new Creditor, then the Account has no open positions anymore with the old Creditor.
     * If a check fails, the whole transaction reverts.
     * @dev This function can be used to refinance liabilities between different Creditors,
     * without the need to first sell collateral to close the open position of the old Creditor.
     */
    function flashActionByCreditor(bytes calldata callbackData, address actionTarget, bytes calldata actionData)
        external
        nonReentrant(WITH_PAUSE_CHECK)
        notDuringAuction
        updateActionTimestamp
        returns (uint256 accountVersion)
    {
        // Cache the current Creditor.
        address currentCreditor = creditor;

        // The caller has to be or the Creditor of the Account, or an approved Creditor.
        if (msg.sender != currentCreditor && msg.sender != approvedCreditor[owner]) {
            revert AccountErrors.OnlyCreditor();
        }

        // Decode flash action data.
        ActionData memory transferFromOwnerData;
        IPermit2.PermitBatchTransferFrom memory permit;
        bytes memory signature;
        bytes memory actionTargetData;
        {
            ActionData memory withdrawData;
            (withdrawData, transferFromOwnerData, permit, signature, actionTargetData) =
                abi.decode(actionData, (ActionData, ActionData, IPermit2.PermitBatchTransferFrom, bytes, bytes));

            // Callback to execute external logic on the Creditor.
            ICreditor(msg.sender).flashActionCallback(callbackData);

            // Withdraw assets to the actionTarget.
            _withdraw(withdrawData.assets, withdrawData.assetIds, withdrawData.assetAmounts, actionTarget);
        }

        if (msg.sender != currentCreditor) {
            // If the caller is the approved Creditor, a margin Account must be opened for the approved Creditor.
            // And the exposures for the current and approved Creditors need to be updated.
            approvedCreditor[owner] = address(0);

            (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
                generateAssetData();

            // Remove the exposures of the Account for the current Creditor.
            if (currentCreditor != address(0)) {
                IRegistry(registry).batchProcessWithdrawal(currentCreditor, assetAddresses, assetIds, assetAmounts);
            }

            // Check if all assets in the Account are allowed by the approved Creditor
            // and add the exposure of the account for the approved Creditor.
            IRegistry(registry).batchProcessDeposit(msg.sender, assetAddresses, assetIds, assetAmounts);

            // Open margin account for the approved Creditor.
            _openMarginAccount(msg.sender);
        }

        // Transfer assets from owner (that are not assets in this account) to the actionTarget.
        _transferFromOwner(transferFromOwnerData, actionTarget);

        // If the function input includes a signature and non-empty token permissions,
        // initiate a transfer from the owner to the actionTarget via Permit2.
        _transferFromOwnerWithPermit(permit, signature, actionTarget);

        // Execute external logic on the actionTarget.
        ActionData memory depositData = IActionBase(actionTarget).executeAction(actionTargetData);

        // Deposit assets from actionTarget into Account.
        _deposit(depositData.assets, depositData.assetIds, depositData.assetAmounts, actionTarget);

        if (currentCreditor != address(0) && msg.sender != currentCreditor) {
            // If the caller is the approved Creditor, the margin Account for the current Creditor (if set) must be closed.
            // closeMarginAccount() checks if there is still an open position (open liabilities) of the Account for the old Creditor.
            // If so, the function reverts.
            ICreditor(currentCreditor).closeMarginAccount(address(this));
        }

        // Account must be healthy after actions are executed.
        if (isAccountUnhealthy()) revert AccountErrors.AccountUnhealthy();

        accountVersion = ACCOUNT_VERSION;
    }

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

    /**
     * @notice Deposits assets into the Account.
     * @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, SandboxASSET], [0, 0, 15, 2], [10**18, 10**18, 1, 100]
     * [SandboxASSET, SandboxASSET, BAYC, BAYC, wETH], [3, 5, 16, 17, 0], [123, 456, 1, 1, 10**18]
     */
    function deposit(address[] calldata assetAddresses, uint256[] calldata assetIds, uint256[] calldata assetAmounts)
        external
        onlyOwner
        nonReentrant(WITH_PAUSE_CHECK)
        notDuringAuction
    {
        // No need to check that all arrays have equal length, this check will be done in the Registry.
        _deposit(assetAddresses, assetIds, assetAmounts, msg.sender);
    }

    /**
     * @notice Deposits assets into the Account.
     * @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 assets deposited into the Account will come from this address.
     */
    function _deposit(
        address[] memory assetAddresses,
        uint256[] memory assetIds,
        uint256[] memory assetAmounts,
        address from
    ) internal {
        // Transfer assets to Account before processing them.
        uint256[] memory assetTypes = IRegistry(registry).batchGetAssetTypes(assetAddresses);
        for (uint256 i; i < assetAddresses.length; ++i) {
            // Skip if amount is 0 to prevent storing addresses that have 0 balance.
            if (assetAmounts[i] == 0) continue;

            if (assetTypes[i] == 1) {
                if (assetIds[i] != 0) revert AccountErrors.InvalidERC20Id();
                _depositERC20(from, assetAddresses[i], assetAmounts[i]);
            } else if (assetTypes[i] == 2) {
                if (assetAmounts[i] != 1) revert AccountErrors.InvalidERC721Amount();
                _depositERC721(from, assetAddresses[i], assetIds[i]);
            } else if (assetTypes[i] == 3) {
                _depositERC1155(from, assetAddresses[i], assetIds[i], assetAmounts[i]);
            } else {
                revert AccountErrors.UnknownAssetType();
            }
        }

        if (erc20Stored.length + erc721Stored.length + erc1155Stored.length > ASSET_LIMIT) {
            revert AccountErrors.TooManyAssets();
        }

        // If no Creditor is set, batchProcessDeposit only checks if the assets can be priced.
        // If a Creditor is set, batchProcessDeposit will also update the exposures of assets and underlying assets for the Creditor.
        IRegistry(registry).batchProcessDeposit(creditor, assetAddresses, assetIds, assetAmounts);

        if (assetAddresses.length > 0) {
            emit Transfers(from, address(this), assetAddresses, assetIds, assetAmounts, assetTypes);
        }
    }

    /**
     * @notice Withdraws assets from the Account 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.
     * For ERC20 assets, the id must be 0.
     * @param assetAmounts Array with the amounts of the assets.
     * For ERC721 assets, the amount must be 1.
     * @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, SandboxASSET], [0, 0, 15, 2], [10**18, 10**18, 1, 100]
     * [SandboxASSET, SandboxASSET, BAYC, BAYC, wETH], [3, 5, 16, 17, 0], [123, 456, 1, 1, 10**18]
     * @dev Will fail if the Account is in an unhealthy state after withdrawal (collateral value is smaller than the used margin).
     * If the Account has no open position (liabilities), users are free to withdraw any asset at any time.
     */
    function withdraw(address[] calldata assetAddresses, uint256[] calldata assetIds, uint256[] calldata assetAmounts)
        external
        onlyOwner
        nonReentrant(WITHOUT_PAUSE_CHECK)
        notDuringAuction
        updateActionTimestamp
    {
        // No need to check that all arrays have equal length, this check is will be done in the Registry.
        _withdraw(assetAddresses, assetIds, assetAmounts, msg.sender);

        // Account must be healthy after assets are withdrawn.
        if (isAccountUnhealthy()) revert AccountErrors.AccountUnhealthy();
    }

    /**
     * @notice Withdraws assets from the Account 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.
     * @dev (batch)ProcessWithdrawal handles the accounting of assets in the Registry.
     */
    function _withdraw(
        address[] memory assetAddresses,
        uint256[] memory assetIds,
        uint256[] memory assetAmounts,
        address to
    ) internal {
        // Process assets before transferring them out of the Account.
        // If a Creditor is set, batchProcessWithdrawal will also update the exposures of assets and underlying assets for the Creditor.
        uint256[] memory assetTypes =
            IRegistry(registry).batchProcessWithdrawal(creditor, assetAddresses, assetIds, assetAmounts);

        // If no assets are being withdrawn, return early.
        if (assetAddresses.length == 0) return;

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

            if (assetTypes[i] == 1) {
                if (assetIds[i] != 0) revert AccountErrors.InvalidERC20Id();
                _withdrawERC20(to, assetAddresses[i], assetAmounts[i]);
            } else if (assetTypes[i] == 2) {
                if (assetAmounts[i] != 1) revert AccountErrors.InvalidERC721Amount();
                _withdrawERC721(to, assetAddresses[i], assetIds[i]);
            } else if (assetTypes[i] == 3) {
                _withdrawERC1155(to, assetAddresses[i], assetIds[i], assetAmounts[i]);
            } else {
                revert AccountErrors.UnknownAssetType();
            }
        }

        emit Transfers(address(this), to, assetAddresses, assetIds, assetAmounts, assetTypes);
    }

    /**
     * @notice Internal function to deposit ERC20 assets.
     * @param from Address the tokens should be transferred from. This address must have approved the Account.
     * @param ERC20Address The contract address of the asset.
     * @param amount The amount of ERC20 assets.
     * @dev Used for all asset type == 1.
     * @dev If the token has not yet been deposited, the ERC20 token address is stored.
     */
    // forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
    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] = currentBalance + amount;
        }
    }

    /**
     * @notice Internal function to deposit ERC721 tokens.
     * @param from Address the tokens should be transferred from. This address must have approved the Account.
     * @param ERC721Address The contract address of the asset.
     * @param id The ID of the ERC721 token.
     * @dev Used for all asset type == 2.
     * @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.
     */
    // forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
    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 Account.
     * @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 asset type == 3.
     * @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.
     */
    // forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
    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] = currentBalance + amount;
        }
    }

    /**
     * @notice Internal function to withdraw ERC20 assets.
     * @param to Address the tokens should be sent to.
     * @param ERC20Address The contract address of the asset.
     * @param amount The amount of ERC20 assets.
     * @dev Used for all asset type == 1.
     * @dev The function checks whether the Account 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.
     */
    // forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
    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; ++i) {
                    if (erc20Stored[i] == ERC20Address) {
                        erc20Stored[i] = erc20Stored[erc20StoredLength - 1];
                        erc20Stored.pop();
                        break;
                    }
                }
            }
        }

        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 asset type == 2.
     * @dev The function checks whether any other ERC721 is deposited in the Account.
     * 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.
     */
    // forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
    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.
            if (erc721TokenIds[0] != id || erc721Stored[0] != ERC721Address) revert AccountErrors.UnknownAsset();
            erc721TokenIds.pop();
            erc721Stored.pop();
        } else {
            for (; i < tokenIdLength; ++i) {
                if (erc721TokenIds[i] == id && erc721Stored[i] == ERC721Address) {
                    erc721TokenIds[i] = erc721TokenIds[tokenIdLength - 1];
                    erc721TokenIds.pop();
                    erc721Stored[i] = erc721Stored[tokenIdLength - 1];
                    erc721Stored.pop();
                    break;
                }
            }
            // For loop should break, otherwise we never went into the if-branch, meaning the token being withdrawn
            // is unknown and not properly deposited.
            // i + 1 is done after loop, so i reaches tokenIdLength.
            if (i == tokenIdLength) revert AccountErrors.UnknownAsset();
        }

        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 asset types = 3.
     * @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.
     */
    // forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
    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; ++i) {
                    if (erc1155TokenIds[i] == id) {
                        if (erc1155Stored[i] == ERC1155Address) {
                            erc1155TokenIds[i] = erc1155TokenIds[tokenIdLength - 1];
                            erc1155TokenIds.pop();
                            erc1155Stored[i] = erc1155Stored[tokenIdLength - 1];
                            erc1155Stored.pop();
                            break;
                        }
                    }
                }
            }
        }

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

    /**
     * @notice Transfers assets directly from the owner to the actionTarget contract.
     * @param transferFromOwnerData A struct containing the info of all assets transferred from the owner that are not in this account.
     * @param to The address to withdraw to.
     */
    function _transferFromOwner(ActionData memory transferFromOwnerData, address to) internal {
        uint256 assetAddressesLength = transferFromOwnerData.assets.length;
        // If no assets are being transferred, return early.
        if (assetAddressesLength == 0) return;

        address owner_ = owner;
        for (uint256 i; i < assetAddressesLength; ++i) {
            if (transferFromOwnerData.assetAmounts[i] == 0) {
                // Skip if amount is 0 to prevent transferring 0 balances.
                continue;
            }

            if (transferFromOwnerData.assetTypes[i] == 1) {
                ERC20(transferFromOwnerData.assets[i])
                    .safeTransferFrom(owner_, to, transferFromOwnerData.assetAmounts[i]);
            } else if (transferFromOwnerData.assetTypes[i] == 2) {
                IERC721(transferFromOwnerData.assets[i]).safeTransferFrom(owner_, to, transferFromOwnerData.assetIds[i]);
            } else if (transferFromOwnerData.assetTypes[i] == 3) {
                IERC1155(transferFromOwnerData.assets[i])
                    .safeTransferFrom(
                        owner_, to, transferFromOwnerData.assetIds[i], transferFromOwnerData.assetAmounts[i], ""
                    );
            } else {
                revert AccountErrors.UnknownAssetType();
            }
        }

        emit Transfers(
            owner_,
            to,
            transferFromOwnerData.assets,
            transferFromOwnerData.assetIds,
            transferFromOwnerData.assetAmounts,
            transferFromOwnerData.assetTypes
        );
    }

    /**
     * @notice Transfers assets from the owner to the actionTarget contract via Permit2.
     * @param permit Data specifying the terms of the transfer.
     * @param signature The signature to verify.
     * @param to The address to withdraw to.
     */
    function _transferFromOwnerWithPermit(
        IPermit2.PermitBatchTransferFrom memory permit,
        bytes memory signature,
        address to
    ) internal {
        uint256 tokenPermissionsLength = permit.permitted.length;
        // If no assets are being transferred, return early.
        if (tokenPermissionsLength == 0) return;

        IPermit2.SignatureTransferDetails[] memory transferDetails =
            new IPermit2.SignatureTransferDetails[](tokenPermissionsLength);

        address[] memory addresses = new address[](tokenPermissionsLength);
        uint256[] memory amounts = new uint256[](tokenPermissionsLength);
        uint256[] memory types = new uint256[](tokenPermissionsLength);

        for (uint256 i; i < tokenPermissionsLength; ++i) {
            transferDetails[i].to = to;
            transferDetails[i].requestedAmount = permit.permitted[i].amount;

            addresses[i] = permit.permitted[i].token;
            amounts[i] = permit.permitted[i].amount;
            types[i] = 1;
        }

        address owner_ = owner;
        PERMIT2.permitTransferFrom(permit, transferDetails, owner_, signature);

        emit Transfers(owner_, to, addresses, new uint256[](tokenPermissionsLength), amounts, types);
    }

    /**
     * @notice Skims non-deposited assets from the Account.
     * @param token The contract address of the asset, address(0) for native token.
     * @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 Account but not deposited
     * or can be used to claim yield for strictly upwards rebasing tokens.
     * @dev Unused input parameters (e.g. id's for ERC20, or type for native token) are not validated,
     * hence arbitrary value can be emitted in events.
     */
    function skim(address token, uint256 id, uint256 type_)
        external
        onlyOwner
        nonReentrant(WITHOUT_PAUSE_CHECK)
        updateActionTimestamp
    {
        uint256 amount;
        if (token == address(0)) {
            amount = address(this).balance;
            (bool success, bytes memory result) = payable(msg.sender).call{ value: amount }("");
            require(success, string(result));
        } else if (type_ == 1) {
            uint256 balance = ERC20(token).balanceOf(address(this));
            uint256 balanceStored = erc20Balances[token];
            if (balance > balanceStored) {
                amount = balance - balanceStored;
                ERC20(token).safeTransfer(msg.sender, amount);
            }
        } else if (type_ == 2) {
            bool isStored;
            uint256 erc721StoredLength = erc721Stored.length;
            for (uint256 i; i < erc721StoredLength; ++i) {
                if (erc721Stored[i] == token && erc721TokenIds[i] == id) {
                    isStored = true;
                    break;
                }
            }

            if (!isStored) {
                amount = 1;
                IERC721(token).safeTransferFrom(address(this), msg.sender, id);
            }
        } else if (type_ == 3) {
            uint256 balance = IERC1155(token).balanceOf(address(this), id);
            uint256 balanceStored = erc1155Balances[token][id];
            if (balance > balanceStored) {
                amount = balance - balanceStored;
                IERC1155(token).safeTransferFrom(address(this), msg.sender, id, amount, "");
            }
        } else {
            revert AccountErrors.UnknownAssetType();
        }

        if (amount > 0) emit Skim(address(this), msg.sender, token, id, amount, type_);
    }

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

    /**
     * @notice Returns the total value (mark to market) of the Account in a specific Numeraire
     * @param numeraire_ The Numeraire to return the value in.
     * @return accountValue Total value stored in the account, denominated in Numeraire.
     * @dev Fetches all stored assets with their amounts.
     * Using a specified Numeraire, fetches the value of all assets in said Numeraire.
     */
    function getAccountValue(address numeraire_) external view returns (uint256 accountValue) {
        (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
            generateAssetData();
        accountValue = IRegistry(registry).getTotalValue(numeraire_, creditor, assetAddresses, assetIds, assetAmounts);
    }

    /**
     * @notice Generates three arrays of all the stored assets in the Account.
     * @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.
        assetAddresses = new address[](totalLength);
        assetIds = new uint256[](totalLength);
        assetAmounts = new uint256[](totalLength);

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

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

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

    /*
    @notice Returns the onERC721Received selector.
    @dev Needed to receive ERC721 tokens.
    */
    // forge-lint: disable-next-item(mixed-case-function)
    function onERC721Received(address, address, uint256, bytes calldata) public pure returns (bytes4) {
        return this.onERC721Received.selector;
    }

    /*
    @notice Returns the onERC1155Received selector.
    @dev Needed to receive ERC1155 tokens.
    */
    // forge-lint: disable-next-item(mixed-case-function)
    function onERC1155Received(address, address, uint256, uint256, bytes calldata) public pure returns (bytes4) {
        return this.onERC1155Received.selector;
    }

    /*
    @notice Called when function selector doesn't match any other.
    @dev No fallback allowed.
    */
    fallback() external {
        revert AccountErrors.NoFallback();
    }
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.0;

library AccountErrors {
    error AccountInAuction();
    error AccountNotLiquidatable();
    error AccountUnhealthy();
    error AlreadyInitialized();
    error CreditorAlreadySet();
    error CreditorNotSet();
    error CoolDownPeriodNotPassed();
    error InvalidAccountVersion();
    error InvalidERC20Id();
    error InvalidERC721Amount();
    error InvalidRecipient();
    error InvalidRegistry();
    error InvalidUpgrade();
    error LengthMismatch();
    error NoFallback();
    error NoReentry();
    error NonZeroOpenPosition();
    error NumeraireNotFound();
    error OnlyCreditor();
    error OnlyFactory();
    error OnlyLiquidator();
    error OnlyOwner();
    error OnlySelf();
    error TooManyAssets();
    error UnknownAsset();
    error UnknownAssetType();
}

library FactoryErrors {
    error AccountCreationFailed();
    error AccountVersionBlocked();
    error FactoryMismatch();
    error InvalidAccountVersion();
    error InvalidRecipient();
    error InvalidUpgrade();
    error ImplIsZero();
    error OnlyAccount();
    error OnlyAccountOwner();
    error UnsafeRecipient();
    error VersionMismatch();
    error VersionRootIsZero();
}

library GuardianErrors {
    // Thrown when the cool-down period has not yet passed.
    error CoolDownPeriodNotPassed();
    // Thrown when the functionality is paused.
    error FunctionIsPaused();
    // Thrown when the caller is not the Guardian.
    error OnlyGuardian();
}

library RegistryErrors {
    error AssetAlreadyInRegistry();
    error AssetModNotUnique();
    error AssetNotAllowed();
    error InvalidAssetType();
    error LengthMismatch();
    error MaxRecursiveCallsReached();
    error Min1Oracle();
    error OnlyAccount();
    error OnlyAssetModule();
    error OnlyOracleModule();
    error OracleModNotUnique();
    error OracleNotReverting();
    error OracleReverting();
    error SequencerDown();
    error Unauthorized();
    error UnknownAsset();
}

File 3 of 18 : AccountStorageV1.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.0;

/**
 * @title Arcadia Accounts Storage
 * @author Pragma Labs
 * @notice This contract is the storage contract for the Accounts.
 * Arcadia Accounts 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.
 * More detail about the Accounts can be found in the AccountsV1.sol contract.
 * @dev Since Accounts are proxies and can be upgraded by the user, all storage variables should be declared in this contract.
 * New account versions must create a new account storage contract and inherit this storage contract.
 */
abstract contract AccountStorageV1 {
    /* //////////////////////////////////////////////////////////////
                                STORAGE
    ////////////////////////////////////////////////////////////// */

    // Flag indicating if the Account is in an auction (in liquidation).
    bool public inAuction;
    // Flag Indicating if a function is locked to protect against reentrancy.
    uint8 internal locked;
    // Used to prevent the old Owner from frontrunning a transferFrom().
    // The Timestamp of the last account action, that might be disadvantageous for a new Owner
    // (withdrawals, change of Creditor, increasing liabilities...).
    uint32 public lastActionTimestamp;
    // The contract address of the liquidator, address 0 if no creditor is set.
    address public liquidator;

    // The minimum amount of collateral that must be held in the Account before a position can be opened, denominated in the numeraire.
    // Will count as Used Margin after a position is opened.
    uint96 public minimumMargin;
    // The contract address of the Registry.
    address public registry;

    // The owner of the Account.
    address public owner;
    // The contract address of the Creditor.
    address public creditor;
    // The Numeraire (the unit in which prices are measured) of the Account,
    // in which all assets and liabilities are denominated.
    address public numeraire;

    // Array with all the contract addresses of ERC20 tokens in the account.
    address[] internal erc20Stored;
    // Array with all the contract addresses of ERC721 tokens in the account.
    address[] internal erc721Stored;
    // Array with all the contract addresses of ERC1155 tokens in the account.
    address[] internal erc1155Stored;
    // Array with all the corresponding ids for each ERC721 token in the account.
    uint256[] internal erc721TokenIds;
    // Array with all the corresponding ids for each ERC1155 token in the account.
    uint256[] internal erc1155TokenIds;

    // Map asset => balance.
    mapping(address => uint256) public erc20Balances;
    // Map asset => id => balance.
    mapping(address => mapping(uint256 => uint256)) public erc1155Balances;
    // Map owner => approved Creditor.
    // This is a Creditor for which a margin Account can be opened later in time to e.g. refinance liabilities.
    mapping(address => address) public approvedCreditor;
    // Map owner => assetManager => flag.
    mapping(address => mapping(address => bool)) public isAssetManager;
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.0;

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

interface IActionBase {
    /**
     * @notice Calls an external target contract with arbitrary calldata.
     * @param actionTargetData A bytes object containing the encoded input for the actionTarget.
     * @return resultData An actionAssetData struct with the final balances of this actionTarget contract.
     */
    function executeAction(bytes calldata actionTargetData) external returns (ActionData memory);
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity ^0.8.0;

// Struct with risk and valuation related information for a certain asset.
struct AssetValueAndRiskFactors {
    // The value of the asset.
    uint256 assetValue;
    // The collateral factor of the asset, for a given creditor.
    uint256 collateralFactor;
    // The liquidation factor of the asset, for a given creditor.
    uint256 liquidationFactor;
}

/**
 * @title Asset Valuation Library
 * @author Pragma Labs
 * @notice The Asset Valuation Library is responsible for calculating the risk weighted values of combinations of assets.
 */
library AssetValuationLib {
    /*///////////////////////////////////////////////////////////////
                        CONSTANTS
    ///////////////////////////////////////////////////////////////*/

    uint256 internal constant ONE_4 = 10_000;

    /*///////////////////////////////////////////////////////////////
                        RISK FACTORS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Calculates the collateral value given a combination of asset values and corresponding collateral factors.
     * @param valuesAndRiskFactors Array of asset values and corresponding collateral factors.
     * @return collateralValue The collateral value of the given assets.
     */
    function _calculateCollateralValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
        internal
        pure
        returns (uint256 collateralValue)
    {
        for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
            collateralValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].collateralFactor;
        }
        collateralValue = collateralValue / ONE_4;
    }

    /**
     * @notice Calculates the liquidation value given a combination of asset values and corresponding liquidation factors.
     * @param valuesAndRiskFactors List of asset values and corresponding liquidation factors.
     * @return liquidationValue The liquidation value of the given assets.
     */
    function _calculateLiquidationValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
        internal
        pure
        returns (uint256 liquidationValue)
    {
        for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
            liquidationValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].liquidationFactor;
        }
        liquidationValue = liquidationValue / ONE_4;
    }
}

// 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.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

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

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

        require(success, "ETH_TRANSFER_FAILED");
    }

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

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // 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.
            success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // 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.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "TRANSFER_FAILED");
    }

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            // 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.
            success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)

            // 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 and token has code.
            if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
                success := iszero(or(iszero(extcodesize(token)), returndatasize())) 
            }
        }

        require(success, "APPROVE_FAILED");
    }
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

// forge-lint: disable-next-item(mixed-case-function)
interface IAccount {
    /**
     * @notice Returns the Account version.
     * @return version The Account version.
     */
    function ACCOUNT_VERSION() external view returns (uint256);

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

    /**
     * @notice Initiates the variables of the Account.
     * @param owner The sender of the 'createAccount' on the factory
     * @param registry The 'beacon' contract with the external logic.
     * @param creditor The contract address of the creditor.
     */
    function initialize(address owner, address registry, address creditor) external;

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

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

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

interface IAccountsGuard {
    function lock(bool pauseCheck) external;
    function unLock() external;
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

interface IAssetManager {
    function onSetAssetManager(address owner, bool status, bytes calldata data) external;
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

/**
 * @title Creditor implementation.
 * @author Pragma Labs
 * @notice This contract contains the minimum functionality a Creditor, interacting with Arcadia Accounts, needs to implement.
 * @dev For the implementation of Arcadia Accounts, see: https://github.com/arcadia-finance/accounts-v2.
 */
interface ICreditor {
    /**
     * @notice Checks if Account fulfills all requirements and returns Creditor parameters.
     * @param accountVersion The version of the Arcadia Account.
     * @return success Bool indicating if all requirements are met.
     * @return numeraire The Numeraire of the Creditor.
     * @return liquidator The liquidator of the Creditor.
     * @return minimumMargin The minimum amount of collateral that must be held in the Account before a position can be opened,
     * denominated in the numeraire.
     */
    function openMarginAccount(uint256 accountVersion) external returns (bool, address, address, uint256);

    /**
     * @notice Checks if Account can be closed.
     * @param account The Account address.
     */
    function closeMarginAccount(address account) external;

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

    /**
     * @notice Returns the Risk Manager of the creditor.
     * @return riskManager The Risk Manager of the creditor.
     */
    function riskManager() external view returns (address);

    /**
     * @notice Callback of the Account during a flashAction.
     * @param callbackData The data for the actions that have to be executed by the Creditor during a flashAction.
     */
    function flashActionCallback(bytes calldata callbackData) external;

    /**
     * @notice Starts the liquidation of an account and returns the open position of the Account.
     * @param initiator The address of the liquidation initiator.
     * @param minimumMargin The minimum margin of the Account.
     * @return openPosition the open position of the Account.
     */
    function startLiquidation(address initiator, uint256 minimumMargin) external returns (uint256);
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

interface IDistributor {
    function operators(address user, address operator) external view returns (uint256);
    function setClaimRecipient(address recipient, address token) external;
    function toggleOperator(address user, address operator) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface IERC721 {
    function ownerOf(uint256 id) external view returns (address);

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

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

    function approve(address spender, uint256 id) external;
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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);

    function setApprovalForAll(address operator, bool approved) external;
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

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

    /**
     * @notice Function used to transfer an Account, called by the Account itself.
     * @param to The target.
     */
    function safeTransferAccount(address to) external;

    /**
     * @notice Function used to transfer an Account between users based on Account address.
     * @param from The sender.
     * @param to The target.
     * @param account The address of the Account that is transferred.
     * @dev This method transfers an Account on Account address instead of id and
     * also transfers the Account proxy contract to the new owner.
     * @dev The Account itself cannot become its owner.
     */
    function safeTransferFrom(address from, address to, address account) external;

    /**
     * @notice This function allows Account owners to upgrade the implementation of the Account.
     * @param account Account that needs to be upgraded.
     * @param version The accountVersion to upgrade to.
     * @param proofs The Merkle proofs that prove the compatibility of the upgrade from current to new account version.
     * @dev As each Account is a proxy, the implementation of the proxy can be changed by the owner of the Account.
     * Checks are done such that only compatible versions can be upgraded to.
     * Merkle proofs and their leaves can be found on https://www.github.com/arcadia-finance.
     */
    function upgradeAccountVersion(address account, uint256 version, bytes32[] calldata proofs) external;
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

interface IMerklOperator {
    function onSetMerklOperator(address accountOwner, bool status, bytes calldata data) external;
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

interface IPermit2 {
    /**
     * @notice The token and amount details for a transfer signed in the permit transfer signature
     */
    struct TokenPermissions {
        // ERC20 token address
        address token;
        // the maximum amount that can be spent
        uint256 amount;
    }

    /**
     * @notice Used to reconstruct the signed permit message for multiple token transfers
     * @dev Do not need to pass in spender address as it is required that it is msg.sender
     * @dev Note that a user still signs over a spender address
     */
    struct PermitBatchTransferFrom {
        // the tokens and corresponding amounts permitted for a transfer
        TokenPermissions[] permitted;
        // a unique value for every token owner's signature to prevent signature replays
        uint256 nonce;
        // deadline on the permit signature
        uint256 deadline;
    }

    /**
     * @notice Specifies the recipient address and amount for batched transfers.
     * @dev Recipients and amounts correspond to the index of the signed token permissions array.
     * @dev Reverts if the requested amount is greater than the permitted signed amount.
     */
    struct SignatureTransferDetails {
        // recipient address
        address to;
        // spender requested amount
        uint256 requestedAmount;
    }

    /**
     * @notice Transfers multiple tokens using a signed permit message
     * @param permit The permit data signed over by the owner
     * @param owner The owner of the tokens to transfer
     * @param transferDetails Specifies the recipient and requested amount for the token transfer
     * @param signature The signature to verify
     */
    function permitTransferFrom(
        PermitBatchTransferFrom memory permit,
        SignatureTransferDetails[] calldata transferDetails,
        address owner,
        bytes calldata signature
    ) external;
}

/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity ^0.8.0;

import { AssetValueAndRiskFactors } from "../libraries/AssetValuationLib.sol";

interface IRegistry {
    /**
     * @notice Returns the Arcadia Accounts Factory.
     * @return factory The contract address of the Arcadia Accounts Factory.
     */
    // forge-lint: disable-next-item(mixed-case-function)
    function FACTORY() external view returns (address);

    /**
     * @notice Checks if an asset is in the Registry.
     * @param asset The contract address of the asset.
     * @return boolean.
     */
    function inRegistry(address asset) external view returns (bool);

    /**
     * @notice Batch retrieves the asset types.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @return assetTypes Array with the types of the assets.
     * 0 = Unknown asset.
     * 1 = ERC20.
     * 2 = ERC721.
     * 3 = ERC1155.
     * ...
     */
    function batchGetAssetTypes(address[] calldata assetAddresses) external view returns (uint256[] memory);

    /**
     * @notice Batch deposit multiple assets.
     * @param creditor The contract address of the creditor.
     * @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.
     */
    function batchProcessDeposit(
        address creditor,
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata amounts
    ) external;

    /**
     * @notice Batch withdraw multiple assets.
     * @param creditor The contract address of the creditor.
     * @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 = Unknown asset.
     * 1 = ERC20.
     * 2 = ERC721.
     * 3 = ERC1155.
     */
    function batchProcessWithdrawal(
        address creditor,
        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 Numeraire.
     * @param numeraire The contract address of the Numeraire.
     * @param creditor The contract address of the creditor.
     * @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.
     * @return assetValue The combined value of the assets, denominated in the Numeraire.
     */
    function getTotalValue(
        address numeraire,
        address creditor,
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata assetAmounts
    ) external view returns (uint256);

    /**
     * @notice Calculates the collateralValue of a combination of assets, denominated in a given Numeraire.
     * @param numeraire The contract address of the Numeraire.
     * @param creditor The contract address of the creditor.
     * @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.
     * @return collateralValue The collateral value of the assets, denominated in the Numeraire.
     */
    function getCollateralValue(
        address numeraire,
        address creditor,
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata assetAmounts
    ) external view returns (uint256);

    /**
     * @notice Calculates the getLiquidationValue of a combination of assets, denominated in a given Numeraire.
     * @param numeraire The contract address of the Numeraire.
     * @param creditor The contract address of the creditor.
     * @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.
     * @return liquidationValue The liquidation value of the assets, denominated in the Numeraire.
     */
    function getLiquidationValue(
        address numeraire,
        address creditor,
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata assetAmounts
    ) external view returns (uint256);

    /**
     * @notice Calculates the values per asset, denominated in a given Numeraire.
     * @param numeraire The contract address of the Numeraire.
     * @param creditor The contract address of the creditor.
     * @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.
     * @return valuesAndRiskFactors The array of values per assets, denominated in the Numeraire.
     */
    function getValuesInNumeraire(
        address numeraire,
        address creditor,
        address[] calldata assetAddresses,
        uint256[] calldata assetIds,
        uint256[] calldata assetAmounts
    ) external view returns (AssetValueAndRiskFactors[] memory valuesAndRiskFactors);
}

// 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);
    }
}

Settings
{
  "remappings": [
    "@ensdomains/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/@ensdomains/",
    "@nomad-xyz/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/",
    "@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
    "@solidity-parser/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/solhint/node_modules/@solidity-parser/",
    "@uniswap/v2-core/contracts/=lib/lending-v2/lib/accounts-v2/./test/utils/fixtures/swap-router-02/",
    "@uniswap/v3-core/contracts/=lib/lending-v2/lib/accounts-v2/lib/v3-core/contracts/",
    "@uniswap/v3-periphery/contracts/=lib/lending-v2/lib/accounts-v2/lib/v3-periphery/contracts/",
    "@uniswap/v4-core/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/",
    "@utils/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/node_modules/utils/src/",
    "ExcessivelySafeCall/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/src/",
    "accounts-v2/=lib/lending-v2/lib/accounts-v2/src/",
    "arcadia-periphery/=lib/arcadia-periphery/src/",
    "asset-managers/=lib/arcadia-periphery/lib/asset-managers/src/",
    "base64-sol/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/base64/",
    "base64/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/base64/",
    "contracts/=lib/lending-v2/lib/accounts-v2/lib/slipstream/contracts/",
    "ds-test/=lib/lending-v2/lib/accounts-v2/lib/solmate/lib/ds-test/src/",
    "erc4626-tests/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
    "forge-std/=lib/lending-v2/lib/accounts-v2/lib/forge-std/src/",
    "hardhat/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/hardhat/",
    "lending-v2/=lib/lending-v2/src/",
    "merkl-contracts/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/",
    "openzeppelin-contracts-upgradeable-v4.9/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/",
    "openzeppelin-contracts-v3.4/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/contracts/",
    "openzeppelin-contracts-v4.9/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
    "openzeppelin-contracts/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/contracts/",
    "openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
    "oz/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/node_modules/@openzeppelin/contracts/",
    "permit2/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/permit2/",
    "slipstream/=lib/lending-v2/lib/accounts-v2/lib/slipstream/",
    "solady/=lib/lending-v2/lib/accounts-v2/lib/solady/src/",
    "solidity-lib/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/solidity-lib/contracts/",
    "solmate/=lib/lending-v2/lib/accounts-v2/lib/solmate/",
    "swap-router-contracts/=lib/lending-v2/lib/accounts-v2/lib/swap-router-contracts/contracts/",
    "v3-core/=lib/lending-v2/lib/accounts-v2/lib/v3-core/",
    "v3-periphery/=lib/lending-v2/lib/accounts-v2/lib/v3-periphery/contracts/",
    "v4-core/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/",
    "v4-periphery/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/",
    "lib/accounts-v2/lib/merkl-contracts:@openzeppelin/contracts-upgradeable/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/contracts/",
    "lib/accounts-v2/lib/merkl-contracts:@openzeppelin/contracts/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
    "lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
    "lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
    "lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/asset-managers/lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
    "lib/asset-managers/lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/asset-managers/lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/asset-managers/lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/merkl-contracts:@openzeppelin/contracts-upgradeable/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/contracts/",
    "lib/merkl-contracts:@openzeppelin/contracts/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
    "lib/openzeppelin-contracts-upgradeable-v4.9:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
    "lib/slipstream:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
    "lib/v3-periphery:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
    "lib/v4-periphery:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
    "lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "prague",
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"factory","type":"address"},{"internalType":"address","name":"accountsGuard","type":"address"},{"internalType":"address","name":"merklDistributor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountInAuction","type":"error"},{"inputs":[],"name":"AccountNotLiquidatable","type":"error"},{"inputs":[],"name":"AccountUnhealthy","type":"error"},{"inputs":[],"name":"CoolDownPeriodNotPassed","type":"error"},{"inputs":[],"name":"CreditorAlreadySet","type":"error"},{"inputs":[],"name":"CreditorNotSet","type":"error"},{"inputs":[],"name":"InvalidAccountVersion","type":"error"},{"inputs":[],"name":"InvalidERC20Id","type":"error"},{"inputs":[],"name":"InvalidERC721Amount","type":"error"},{"inputs":[],"name":"InvalidRegistry","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"NoFallback","type":"error"},{"inputs":[],"name":"NumeraireNotFound","type":"error"},{"inputs":[],"name":"OnlyCreditor","type":"error"},{"inputs":[],"name":"OnlyFactory","type":"error"},{"inputs":[],"name":"OnlyLiquidator","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"TooManyAssets","type":"error"},{"inputs":[],"name":"UnknownAsset","type":"error"},{"inputs":[],"name":"UnknownAssetType","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"assetManager","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AssetManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creditor","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"}],"name":"MarginAccountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"merklOperator","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"MerklOperatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"numeraire","type":"address"}],"name":"NumeraireSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"type_","type":"uint256"}],"name":"Skim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address[]","name":"assets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"types","type":"uint256[]"}],"name":"Transfers","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"ACCOUNTS_GUARD","outputs":[{"internalType":"contract IAccountsGuard","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ACCOUNT_VERSION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ASSET_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MERKL_DISTRIBUTOR","outputs":[{"internalType":"contract IDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedCreditor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"},{"internalType":"address","name":"bidder","type":"address"}],"name":"auctionBid","outputs":[{"internalType":"uint256[]","name":"assetAmounts_","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"auctionBoughtIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeMarginAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"creditor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":[],"name":"endAuction","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":"address","name":"","type":"address"}],"name":"erc20Balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"actionTarget","type":"address"},{"internalType":"bytes","name":"actionData","type":"bytes"}],"name":"flashAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"callbackData","type":"bytes"},{"internalType":"address","name":"actionTarget","type":"address"},{"internalType":"bytes","name":"actionData","type":"bytes"}],"name":"flashActionByCreditor","outputs":[{"internalType":"uint256","name":"accountVersion","type":"uint256"}],"stateMutability":"nonpayable","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":[{"internalType":"address","name":"numeraire_","type":"address"}],"name":"getAccountValue","outputs":[{"internalType":"uint256","name":"accountValue","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":[],"name":"inAuction","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"openPosition","type":"uint256"}],"name":"increaseOpenPosition","outputs":[{"internalType":"uint256","name":"accountVersion","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"registry_","type":"address"},{"internalType":"address","name":"creditor_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isAccountLiquidatable","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isAccountUnhealthy","outputs":[{"internalType":"bool","name":"isUnhealthy","type":"bool"}],"stateMutability":"view","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":"lastActionTimestamp","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumMargin","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numeraire","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":"newCreditor","type":"address"}],"name":"openMarginAccount","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"}],"name":"removeAssetManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"removeMerklOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"creditor_","type":"address"}],"name":"setApprovedCreditor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetManagers","type":"address[]"},{"internalType":"bool[]","name":"statuses","type":"bool[]"},{"internalType":"bytes[]","name":"datas","type":"bytes[]"}],"name":"setAssetManagers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"},{"internalType":"bool[]","name":"operatorStatuses","type":"bool[]"},{"internalType":"bytes[]","name":"operatorDatas","type":"bytes[]"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"setMerklOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"numeraire_","type":"address"}],"name":"setNumeraire","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":"initiator","type":"address"}],"name":"startLiquidation","outputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"},{"internalType":"address","name":"creditor_","type":"address"},{"internalType":"uint96","name":"minimumMargin_","type":"uint96"},{"internalType":"uint256","name":"openPosition","type":"uint256"},{"components":[{"internalType":"uint256","name":"assetValue","type":"uint256"},{"internalType":"uint256","name":"collateralFactor","type":"uint256"},{"internalType":"uint256","name":"liquidationFactor","type":"uint256"}],"internalType":"struct AssetValueAndRiskFactors[]","name":"assetAndRiskValues","type":"tuple[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"address","name":"newRegistry","type":"address"},{"internalType":"uint256","name":"newVersion","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oldImplementation","type":"address"},{"internalType":"address","name":"oldRegistry","type":"address"},{"internalType":"uint256","name":"oldVersion","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeHook","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":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101003461018e57601f6158d338819003918201601f19168301916001600160401b038311848410176101925780849260609460405283398101031261018e57610048816101a6565b90610061604061005a602084016101a6565b92016101a6565b6e22d473030f116ddee9f6b43ac78ba360e052600280546001600160a01b031916331790556080929092526001600160a01b0390811660a0521660c05260405161571890816101bb823960805181818161059001528181610c9101528181610db201528181612ba60152612bec015260a05181818161027201528181610378015281816105c0015281816107fd01528181610c1301528181610de201528181610f770152818161115901528181611236015281816112fb0152818161156401528181611a3d01528181611c7701528181611dce0152818161215c0152818161260b01528181612a9b01528181612c1c01528181612fb10152818161322d015281816134380152613524015260c05181818161137d015281816121e20152612f56015260e05181614d570152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361018e5756fe6080806040526004361015610028575b50346100245763b25befbf60e01b5f5260045ffd5b5f80fd5b5f905f3560e01c90816304dababe1461364557816305549367146134f95750806305c0608c146134d257806305f1a68d1461340757806308f1bdaa146133b25780630b9252f0146131d4578063150b7a02146131af5780631e03544214612f85578063219461ed14612f4057806321ab889d14612f075780632723ae1f14612ede5780632c3c717314612bd55780632dd3100014612b90578063358bca7714612a7e5780634046ebae14612a5357806342c8d71514612a3857806347cf6c9e146129f75780634a1ec375146125d45780635ae80951146125185780636048dd00146120a6578063653b92b414611d3c578063656e872914611c4c57806368719ee314611a0c57806371a84c71146119b45780637afc01be146115055780637b103999146114e45780637c0b5337146112cb57806387bbaeb6146112a95780638da5cb5b14611280578063907a9429146112655780639086ab3914611220578063a0d9f5cd1461113c578063ac891de4146110f0578063adb33ab6146110ad578063b8eabab014610f4f578063bc68c67614610f33578063bfed6b0314610f17578063c047e56314610efc578063c0c53b8b14610d79578063d5d4305b14610d54578063df03e1a014610be1578063e1c50100146107ca578063e36db785146107a1578063eac697c91461077b578063edfe1faf1461073b578063f23a6e61146106e4578063f2fde38b14610573578063fb787d01146103535763fe67a54b0361000f5734610313578060031936011261031357805460301c6001600160a01b0316330361034457807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610321576040516306e8502160e11b815260048101839052828160248183865af1908115610339578391610324575b5050815460ff19168255803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b8161030c916138a5565b6103135780f35b80fd5b6040513d84823e3d90fd5b50fd5b8161032e916138a5565b61032157815f6102cc565b6040513d85823e3d90fd5b6399b01c4760e01b8152600490fd5b50346103135780600319360112610313576002546001600160a01b03163303610564577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b15610313576040516306e8502160e11b815260048101829052818160248183875af180156103165790829161054f575b50549160ff831661054057600354926001600160a01b0384169081156105325783946001600160601b0360a01b166003556601000000000000600160d01b031916835561045583806001546001600160601b03198116600155610434613abe565b604051630578da2160e41b8152968795869490938593918b6004860161427a565b039260601c5af1801561052757610507575b50803b156104ee57828091602460405180948193632ce1ed1560e01b83523060048401525af19081156103395783916104f2575b50506040519082807fa521c704a92cee73ea8410dc251eece5bd5362f6887c45f3e9566e817aee73698180a3803b156104ee5781600481858094633b4438cf60e21b83525af18015610316576103025750f35b5050fd5b816104fc916138a5565b61032157815f61049b565b610522903d8086833e61051a81836138a5565b810190614255565b610467565b6040513d86823e3d90fd5b623cb2cf60e01b8452600484fd5b635441f54960e11b8252600482fd5b81610559916138a5565b61031357805f6103d3565b635fc483c560e01b8152600490fd5b50346103135760203660031901126103135761058d613655565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036106d5577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156106d1576040516306e8502160e11b815260048101839052828160248183865af18015610339579083916106bc575b505460ff81166106ad5763ffffffff9060101c1661012c81018091116106995742111561068a57819260018060a01b03166001600160601b0360a01b6002541617600255803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b63036a87d960e21b8252600482fd5b634e487b7160e01b83526011600452602483fd5b635441f54960e11b8352600483fd5b816106c6916138a5565b6106d157815f61061a565b5080fd5b630636a15760e11b8152600490fd5b50346103135760a0366003190112610313576106fe613655565b50610707613681565b506084356001600160401b0381116106d1576107279036906004016136ab565b505060405163f23a6e6160e01b8152602090f35b5034610313578060031936011261031357602090610757614374565b61075f614130565b91828211156107735750035b604051908152f35b91505061076b565b503461031357806003193601126103135763ffffffff6020915460101c16604051908152f35b50346103135780600319360112610313576003546040516001600160a01b039091168152602090f35b5034610313576020366003190112610313576107e4613655565b815490919060301c6001600160a01b03163303610344577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b15610313576040516306e8502160e11b815260048101829052818160248183875af1801561031657908291610bcc575b505465ffffffff00ff19164260101b65ffffffff00001617600190811782556003549054936001600160a01b039190911691906001600160601b0385168261089e613abe565b9282829992939a8960018060a01b03600454166108d2886040519e8f9687958695633a137bf360e11b875260048701614202565b039160601c5afa978815610a2a578698610b00575b506040516342f2e72760e11b81526001600160a01b03909516600486015260248501849052602085604481898b5af1948515610a2a578695610acc575b5061092f84866139b6565b8515908115610a47575b50610a3957803b15610a3557858091600460405180948193633b4438cf60e21b83525af18015610a2a57908691610a11575b505061099197969593929161099f6109ad926040519a8b9a60e08c5260e08c0190613733565b908a820360208c015261376f565b9088820360408a015261376f565b926060870152608086015260a085015283810360c0850152602080845192838152019301915b8181106109e1575050500390f35b919350916020606060019260408751805183528481015185840152015160408201520194019101918493926109d3565b81610a1b916138a5565b610a2657845f61096b565b8480fd5b6040513d88823e3d90fd5b8580fd5b627616e960e61b8652600486fd5b98999593919794929050869787985b8b518a1015610ab5578b906040610a798c610a718186613aaa565b515194613aaa565b51015191828102928184041490151715610aa157600191610a99916139b6565b990198610a56565b634e487b7160e01b8a52601160045260248afd5b612710919395979b9a92949699500410155f610939565b9094506020813d602011610af8575b81610ae8602093836138a5565b810103126100245751935f610924565b3d9150610adb565b9097503d8087833e610b1281836138a5565b810190602081830312610bc4578051906001600160401b038211610bc8570181601f82011215610bc457805190610b48826138c6565b92610b5660405194856138a5565b82845260206060818601940283010191818311610bc057602001925b828410610b845750505050965f6108e7565b606084830312610bc0576020606091604051610b9f81613840565b86518152828701518382015260408701516040820152815201930192610b72565b8980fd5b8680fd5b8780fd5b81610bd6916138a5565b61031357805f610858565b503461031357602036600319011261031357610bfb613655565b815460301c6001600160a01b03163303610d455781907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af1908115610527578491610d30575b5050600280546001600160a01b0319166001600160a01b039283169081179091557f000000000000000000000000000000000000000000000000000000000000000090911690813b15610d2b578391602483926040519485938492637ccd031560e01b845260048401525af1908115610339578391610d16575b5050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81610d20916138a5565b61032157815f610ce8565b505050fd5b81610d3a916138a5565b6104ee57825f610c6e565b6399b01c4760e01b8252600482fd5b50346103135780600319360112610313576020610d6f6143c2565b6040519015158152f35b503461031357606036600319011261031357610d93613655565b90610d9c613681565b6044356001600160a01b038116808203610ef8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610ee9577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692833b15610a26576040516306e8502160e11b815260016004820152858160248183895af18015610a2a57908691610ed4575b50506001600160a01b03811615610ec557849560018060a01b03166001600160601b0360a01b60025416176002556001600160601b036001549181199060601b16911617600155610eb6575b50803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b610ebf90615425565b5f610e89565b6311a1e69760e01b8552600485fd5b81610ede916138a5565b610a2657845f610e3d565b630636a15760e11b8452600484fd5b8380fd5b5034610313578060031936011261031357602061076b614374565b50346103135780600319360112610313576020604051600f8152f35b5034610313578060031936011261031357602060405160038152f35b503461031357610f5e366137d2565b60025493969590936001600160a01b0316330361109e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031694853b15610bc4576040516306e8502160e11b8152600481018890528781602481838b5af180156110935790889161107e575b505460ff811661106f579761100e6110209361101693611026999a9b65ffffffff00004260101b169065ffffffff00001916178c5536916138dd565b94369161394f565b913394369161394f565b916144e4565b61102e6143c2565b611060578082913b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b6302fb7a9160e31b8252600482fd5b635441f54960e11b8852600488fd5b81611088916138a5565b610bc457865f610fd2565b6040513d8a823e3d90fd5b635fc483c560e01b8652600486fd5b5034610313576040366003190112610313576020906040906001600160a01b036110d5613655565b168152600b8352818120602435825283522054604051908152f35b5034610313578060031936011261031357602061110b614130565b6001600160601b03600154168111908161112b575b506040519015158152f35b90506111356142e2565b1082611120565b50346103135760203660031901126103135780611157613655565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156104ee576040516306e8502160e11b815260016004820152838160248183875af190811561052757849161120b575b5050338352600c6020526040832080546001600160a01b0319166001600160a01b03909216919091179055803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81611215916138a5565b6104ee57825f6111b4565b50346103135780600319360112610313576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034610313578060031936011261031357602061076b6142e2565b50346103135780600319360112610313576002546040516001600160a01b039091168152602090f35b503461031357806003193601126103135760ff60209154166040519015158152f35b5034610313576020366003190112610313576112e5613655565b6002546001600160a01b031633036114d55781907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af19081156105275784916114c0575b505060405163131bac4760e11b81523060048201526001600160a01b0382811660248301527f00000000000000000000000000000000000000000000000000000000000000001690602081604481855afa9081156114b5578591611480575b50611420575b506040518381526001600160a01b03909116907fd61f2c33e366f60a2f32f50d952952a311e9ed22662d58f31dc6a3e2c8ba61f190602090a2803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b803b15610d2b5760405163bdac7ca360e01b81523060048201526001600160a01b03831660248201529084908290604490829084905af190811561052757849161146b575b506113bb565b81611475916138a5565b6104ee57825f611465565b9450506020843d6020116114ad575b8161149c602093836138a5565b81010312610024578493515f6113b5565b3d915061148f565b6040513d87823e3d90fd5b816114ca916138a5565b6104ee57825f611356565b635fc483c560e01b8252600482fd5b5034610313578060031936011261031357602060015460601c604051908152f35b5034610313576060366003190112610313576004356001600160401b0381116106d1576115369036906004016136ab565b9190611540613681565b906044356001600160401b038111610ef8576115609036906004016136ab565b90947f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031693843b15610a35576040516306e8502160e11b8152600160048201528681602481838a5af180156119a957908791611994575b505460ff81166119855765ffffffff000019164260101b65ffffffff0000161786556003546001600160a01b03169333851415938480611961575b61195257879861163b9161160f9998996142be565b50886040805161161e81613840565b6060815282602082015201526116326142be565b50810190613d25565b93949096929192333b1561194e57604051637afcd25560e11b815260206004820152918b9183918291611673916024840191906140a2565b038183335af18015611943578a9589928892611921575b5090816116a392519060406020820151910151916144e4565b6117ee575b6116e2956116c0926116bb878094614afe565b614ce0565b6040518094819263a129568d60e01b835260206004840152602483019061400c565b0381836001600160a01b0386165af180156114b5576117169286916117cc575b508051906040602082015191015191614f8c565b81151590816117c4575b5061177f575b50506117306143c2565b611060578082913b15610321578190600460405180948193633b4438cf60e21b83525af180156103165761176a575b602060405160038152f35b6117758280926138a5565b610313578061175f565b803b156106d157818091602460405180948193632ce1ed1560e01b83523060048401525af18015610316571561172657816117b9916138a5565b6106d157815f611726565b90505f611720565b6117e891503d8088833e6117e081836138a5565b810190613ef5565b5f611702565b6002546001600160a01b03168952600c6020526040892080546001600160a01b0319169055925061181d613abe565b89959291956118ba575b60015460601c803b156118b657611859968c8094604051998a9586948593631a1bb38960e21b8552336004860161427a565b03925af19384156118ab578994611890575b506116e2956116c0926116bb87809461188333615425565b94505050925095506116a8565b8461189e91979592976138a5565b610bc8578792945f61186b565b6040513d8b823e3d90fd5b8b80fd5b808b878c858360015460601c926118e760405197889687958694630578da2160e41b86526004860161427a565b03925af18015611916576118fc575b50611827565b61190f903d808e833e61051a81836138a5565b505f6118f6565b6040513d8e823e3d90fd5b87935061193192509690966138a5565b61193f5786858a955f61168a565b8880fd5b6040513d8c823e3d90fd5b8a80fd5b63a9faa8f560e01b8852600488fd5b506002546001600160a01b039081168952600c6020526040892054163314156115fa565b635441f54960e11b8752600487fd5b8161199e916138a5565b610a3557855f6115bf565b6040513d89823e3d90fd5b50346103135760403660031901126103135760406119d0613655565b916119d9613681565b9260018060a01b03168152600d602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b503461031357602036600319011261031357611a26613655565b6002549091906001600160a01b03163303610564577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156106d1576040516306e8502160e11b815260016004820152828160248183865af1801561033957908391611c37575b505460ff81166106ad5765ffffffff000019164260101b65ffffffff000016178255611ac1613abe565b60035492956001600160a01b03938416939192909182168414611c285785968415159384611bcd575b60015460601c803b1561193f57611b1c9389809460405196879586948593631a1bb38960e21b85528b6004860161427a565b03925af1908115610a2a578691611bb8575b5050611b3990615425565b611b6a5750803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b803b156104ee57828091602460405180948193632ce1ed1560e01b83523060048401525af1908115610339578391611ba3575b50610e89565b81611bad916138a5565b61032157815f611b9d565b81611bc2916138a5565b610a2657845f611b2e565b80888460015460601c85838b611bf960405197889687958694630578da2160e41b86526004860161427a565b03925af180156118ab57611c0e575b50611aea565b611c21903d808b833e61051a81836138a5565b505f611c08565b63ce5a86f760e01b8652600486fd5b81611c41916138a5565b6106d157815f611a97565b503461031357611c5b366137d2565b60025490969592939192906001600160a01b0316330361109e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031694853b15610bc4576040516306e8502160e11b8152600160048201528781602481838b5af1801561109357908891611d27575b505460ff1661198557611016611cfb959361100e899a9694611cf59436916138dd565b91614f8c565b803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81611d31916138a5565b610bc457865f611cd2565b5034610313576080366003190112610313576004356001600160401b0381116106d157611d6d903690600401613931565b6024356001600160401b0381116120a257611d8c90369060040161399b565b916044356001600160401b0381116106d157611dac90369060040161399b565b90611db561366b565b815490939060301c6001600160a01b03163303610d45577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156120a2576040516306e8502160e11b815260048101849052838160248183865af180156105275790849161208d575b505060015460601c90836040518093630c3d587560e11b8252602060048301528180611e576024820189613733565b03915afa918215610527578492612071575b50835b835181101561200957611e7f8187613aaa565b5115612001576001611e918285613aaa565b5103611ee6576001906001600160a01b03611eac8287613aaa565b51168652600a60205260408620545b80611ec6838a613aaa565b5111611ed5575b505b01611e6c565b611edf8289613aaa565b525f611ecd565b6002611ef28285613aaa565b5103611fa4576001600160a01b03611f0a8286613aaa565b5116906020611f19828b613aaa565b516024604051809581936331a9108f60e11b835260048301525afa8015610a2a578690611f69575b60019250306001600160a01b039190911603611f615760ff825b16611ebb565b60ff86611f5b565b50906020813d8211611f9c575b81611f83602093836138a5565b81010312610a355790611f97600192613e84565b611f41565b3d9150611f76565b6003611fb08285613aaa565b5103611ff2576001906001600160a01b03611fcb8287613aaa565b51168652600b60205260408620611fe2828b613aaa565b5187526020526040862054611ebb565b63b8bcee3f60e01b8552600485fd5b600190611ecf565b848681846120198b848e8b6144e4565b803b156106d1578190600460405180948193633b4438cf60e21b83525af180156103395761205c575b604051602080825281906120589082018561376f565b0390f35b6120678380926138a5565b6106d15781612042565b6120869192503d8086833e61051a81836138a5565b905f611e69565b81612097916138a5565b6120a257825f611e28565b8280fd5b50346103135760a0366003190112610313576004356001600160401b0381116106d1576120d79036906004016137a2565b91906024356001600160401b0381116120a2576120f89036906004016137a2565b936044356001600160401b038111610a26576121189036906004016137a2565b93909261212361366b565b926084356001600160401b038111610bc8576121439036906004016137a2565b60025491969093916001600160a01b03163303612509577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031697883b15610bc0576040516306e8502160e11b8152600160048201528a81602481838e5af180156124fe57908b916124e9575b505465ffffffff000019164260101b65ffffffff000016178a55838b148015906124df575b6124cd577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031695938a5b8181106122ce575050505050508580975050855b81811061225857868087803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b61226b612266828488614030565b614040565b833b15610bc857604051630d0a119360e31b81526001600160a01b038681166004830152919091166024820152878160448183885af19081156110935788916122b9575b5050600101612222565b816122c3916138a5565b610bc457865f6122af565b6122dc612266828489614030565b60405163131bac4760e11b81523060048201526001600160a01b03821660248201526020816044818d5afa908115612427578f918f91859291612494575b506123319161232c911515938a614030565b614054565b151503612438575b8d612345838688614061565b9050612399575b907fd61f2c33e366f60a2f32f50d952952a311e9ed22662d58f31dc6a3e2c8ba61f1602061238161232c86600197968c614030565b926040519315158452858060a01b031692a20161220e565b61232c836123a79289614030565b6123b2838688614061565b916001600160a01b0384163b1561243457908f916123e56040519485938493630ede8ef360e11b855233600486016140c2565b0381836001600160a01b0387165af18015612427578f918f9161240a575b505061234c565b81925090612417916138a5565b612423578d8d5f612403565b8c80fd5b8e604051903d90823e3d90fd5b8f80fd5b883b156124235760405163bdac7ca360e01b81523060048201526001600160a01b03821660248201528d81604481838e5af1801561242757908e9161247f575b5050612339565b81612489916138a5565b612423578c5f612478565b925050506020813d82116124c5575b816124b0602093836138a5565b8101031261002457518e90839061233161231a565b3d91506124a3565b6001621398b960e31b03198a5260048afd5b50808414156121dc565b816124f3916138a5565b610bc057895f6121b7565b6040513d8d823e3d90fd5b635fc483c560e01b8952600489fd5b503461031357602036600319011261031357612532613655565b90602061253d613abe565b60015460035460405163068e097b60e51b815297889560609390931c948694859461257994919390916001600160a01b03169060048701614202565b03915afa9081156125c85790612595575b602090604051908152f35b506020813d6020116125c0575b816125af602093836138a5565b81010312610024576020905161258a565b3d91506125a2565b604051903d90823e3d90fd5b5034610313576060366003190112610313576125ee613655565b6002546024359291604435916001600160a01b031633036129e8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b15610ef8576040516306e8502160e11b815260048101859052848160248183885af180156114b5579085916129d3575b505465ffffffff000019164260101b65ffffffff0000161784556001600160a01b039091169083826127785750478480808084335af1953d15612770573d966126ae88613cc4565b976126bc604051998a6138a5565b88523d8760208a013e5b1561274b578596505b81612705575b50505050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b60405193845260208401526040830152606082015233907fb180a6a3fa923907a3d3d7175c65292a5003d4f5b24dea54d607814d5664378d60803092a35f8080806126d5565b60405162461bcd60e51b8152602060048201528061276c602482018a61400c565b0390fd5b6060966126c6565b6001820361281d5784956040516370a0823160e01b8152306004820152602081602481885afa9081156119a95787916127e8575b50848752600a6020526040872054908181116127ca575b50506126cf565b6127d59293506141bd565b906127e1823386615395565b5f806127c3565b9650506020863d602011612815575b81612804602093836138a5565b81010312610024578695515f6127ac565b3d91506127f7565b600282036128ed576006549585805b888110612899575b508697506126cf5760019150833b15610a3557604051632142170760e11b815230600482015233602482015260448101829052868160648183895af19081156119a95787916128845750506126cf565b8161288e916138a5565b610a3557855f6127c3565b856128a382613a09565b905460039190911b1c6001600160a01b031614806128d5575b6128c85760010161282c565b505085965060015f612834565b50826128e082613a35565b90549060031b1c146128bc565b60038203611ff2578495604051627eeac760e11b8152306004820152816024820152602081604481885afa9081156119a957879161299e575b50848752600b6020526040872082885260205260408720549081811161294d5750506126cf565b6129589293506141bd565b90833b15610a3557604051637921219560e11b815286818061298086863330600486016141ca565b038183895af19081156119a9578791156127c3578161288e916138a5565b9650506020863d6020116129cb575b816129ba602093836138a5565b81010312610024578695515f612926565b3d91506129ad565b816129dd916138a5565b610ef857835f612666565b635fc483c560e01b8352600483fd5b5034610313576020366003190112610313576020906001600160a01b03612a1c613655565b168152600c8252604060018060a01b0391205416604051908152f35b5034610313578060031936011261031357602061076b614130565b50346103135780600319360112610313575460405160309190911c6001600160a01b03168152602090f35b50346103135760203660031901126103135780612a99613655565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af1908115610527578491612b7b575b505033808452600d602090815260408086206001600160a01b03949094165f81815294835293819020805460ff19169055518581527f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c19190a3803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81612b85916138a5565b6104ee57825f612af6565b50346103135780600319360112610313576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461031357612be4366136d8565b9294939192917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303612ecf577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031693843b15610a35576040516306e8502160e11b8152600481018790528681602481838a5af180156119a957908791612eba575b505460ff81166119855765ffffffff000019164260101b65ffffffff0000161786557f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8054600180546001600160a01b031983166001600160a01b03968716179093556001600160601b03831660608b811b6bffffffffffffffffffffffff191691909117909155909891901c9216808314159081612e41575b50612e32576003546001600160a01b03169081612db6575b50508495303b15610a3557612d758693604051958694859463026d5d5f60e11b865260018060a01b031660048601526024850152600360448501526080606485015260848401916140a2565b038183305af1908115610339578391610d16575050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b869160246080926040519485938492638c48052360e01b845260048401525af1908115610a2a578691612e00575b5015612df1575f80612d29565b63a93eca7960e01b8552600485fd5b612e22915060803d608011612e2b575b612e1a81836138a5565b8101906140f9565b5050505f612de4565b503d612e10565b6311a1e69760e01b8652600486fd5b9050602060018060a01b036004541660246040518094819363e4ec552b60e01b835260048301525afa9081156119a9578791612e80575b50155f612d11565b90506020813d602011612eb2575b81612e9b602093836138a5565b81010312610bc457612eac906140ec565b5f612e78565b3d9150612e8e565b81612ec4916138a5565b610a3557855f612c77565b630636a15760e11b8552600485fd5b50346103135780600319360112610313576004546040516001600160a01b039091168152602090f35b5034610313576020366003190112610313576020906040906001600160a01b03612f2f613655565b168152600a83522054604051908152f35b50346103135780600319360112610313576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461031357612f94366137d2565b6002549496959294929391926001600160a01b0316330361109e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031694853b15610bc4576040516306e8502160e11b8152600160048201528781602481838b5af180156110935790889161319a575b505465ffffffff000019164260101b65ffffffff000016178755878114801590613190575b61317e578697875b81811061306f57888089803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b61307d61226682848a614030565b61308b61232c838689614030565b33808c52600d60209081526040808e206001600160a01b039095165f81815295835294819020805460ff95151595861660ff19919091161790555192835283927f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c19190a36130fa828689614061565b905061310a575b50600101613039565b61311861232c838689614030565b61312383878a614061565b9092803b1561242357613151938d809460405196879586948593635f4860df60e01b855233600486016140c2565b03925af1908115611943578a91613169575b50613101565b81613173916138a5565b61193f57885f613163565b6001621398b960e31b03198752600487fd5b5081881415613031565b816131a4916138a5565b610bc457865f61300c565b5034610313576131be366136d8565b50505050506020604051630a85bd0160e11b8152f35b5034610313576040366003190112610313576131ee613655565b6024356001600160401b0381116120a25761320d9036906004016136ab565b919060018060a01b0360025416803314908115613390575b5015613353577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692833b15610a26576040516306e8502160e11b81526001600482015285908181602481838a5af180156103165761333e575b505460ff811661332f57926116c0816132e3816116bb6132c982988c986132e89b65ffffffff00004260101b169065ffffffff00001916178a55810190613d25565b9993809792969197519060406020820151910151916144e4565b614afe565b0381836001600160a01b0386165af180156105275761102692859161331b57508051906040602082015191015191614f8c565b6117e891503d8087833e6117e081836138a5565b635441f54960e11b8652600486fd5b81613348916138a5565b610a2657845f613287565b60405162461bcd60e51b8152602060048201526015602482015274209d1027b7363c9020b9b9b2ba1026b0b730b3b2b960591b6044820152606490fd5b855250600d60209081526040808620335f908152925281205460ff1690613225565b50346103135780600319360112610313576133eb6133f96120586133d4613abe565b919390604051958695606087526060870190613733565b90858203602087015261376f565b90838203604085015261376f565b503461031357602036600319011261031357613421613655565b6002549091906001600160a01b03163303610564577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156106d1576040516306e8502160e11b815260016004820152828160248183865af18015610339579083916134bd575b50506003546001600160a01b03166134ae57611cfb82936143f0565b63ce5a86f760e01b8252600482fd5b816134c7916138a5565b6106d157815f613492565b503461031357806003193601126103135760206001600160601b0360015416604051908152f35b90503461002457602036600319011261002457600354600435906001600160a01b03163303613636577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b15610024576306e8502160e11b8152600160048201525f8160248183875af1801561362b57613616575b50825460ff81166136075765ffffffff000019164260101b65ffffffff00001617835580151590816135e0575b50611060578082913b15610321578190600460405180948193633b4438cf60e21b83525af180156103165761176a57602060405160038152f35b90506136006135ed614374565b916001600160601b0360015416906139b6565b115f6135a6565b635441f54960e11b8452600484fd5b6136239193505f906138a5565b5f915f613579565b6040513d5f823e3d90fd5b63a9faa8f560e01b5f5260045ffd5b3461002457613653366136d8565b005b600435906001600160a01b038216820361002457565b606435906001600160a01b038216820361002457565b602435906001600160a01b038216820361002457565b35906001600160a01b038216820361002457565b9181601f84011215610024578235916001600160401b038311610024576020838186019501011161002457565b906080600319830112610024576004356001600160a01b038116810361002457916024356001600160a01b0381168103610024579160443591606435906001600160401b0382116100245761372f916004016136ab565b9091565b90602080835192838152019201905f5b8181106137505750505090565b82516001600160a01b0316845260209384019390920191600101613743565b90602080835192838152019201905f5b81811061378c5750505090565b825184526020938401939092019160010161377f565b9181601f84011215610024578235916001600160401b038311610024576020808501948460051b01011161002457565b6060600319820112610024576004356001600160401b03811161002457816137fc916004016137a2565b929092916024356001600160401b038111610024578161381e916004016137a2565b92909291604435906001600160401b0382116100245761372f916004016137a2565b606081019081106001600160401b0382111761385b57604052565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761385b57604052565b604081019081106001600160401b0382111761385b57604052565b90601f801991011681019081106001600160401b0382111761385b57604052565b6001600160401b03811161385b5760051b60200190565b9291906138e9816138c6565b936138f760405195866138a5565b602085838152019160051b810192831161002457905b82821061391957505050565b6020809161392684613697565b81520191019061390d565b9080601f830112156100245781602061394c933591016138dd565b90565b92919061395b816138c6565b9361396960405195866138a5565b602085838152019160051b810192831161002457905b82821061398b57505050565b813581526020918201910161397f565b9080601f830112156100245781602061394c9335910161394f565b919082018092116139c357565b634e487b7160e01b5f52601160045260245ffd5b906139e1826138c6565b6139ee60405191826138a5565b82815280926139ff601f19916138c6565b0190602036910137565b600654811015613a215760065f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b600854811015613a215760085f5260205f2001905f90565b600554811015613a215760055f5260205f2001905f90565b600754811015613a215760075f5260205f2001905f90565b600954811015613a215760095f5260205f2001905f90565b8054821015613a21575f5260205f2001905f90565b8051821015613a215760209160051b010190565b6005549060065460075491828285010192613ad8846139d7565b94613aeb613ae5866139d7565b956139d7565b935f915b808310613bd857505f905b808210613b805750505f905b828210613b1257505050565b60018091613b1f84613a65565b838060a01b0391549060031b1c16613b3685613a7d565b90549060031b1c9080613b49848d613aaa565b5281613b55848c613aaa565b525f52600b60205260405f20905f5260205260405f2054613b768289613aaa565b5201910190613b06565b909160018091613b8f85613a09565b838060a01b0391549060031b1c16613ba7828c613aaa565b52613bb185613a35565b90549060031b1c613bc2828b613aaa565b5281613bce828a613aaa565b5201920190613afa565b9180613be5600192613a4d565b838060a01b0391549060031b1c1680613bfe838c613aaa565b525f52600a60205260405f2054613c158289613aaa565b520191613aef565b91906080838203126100245760405190613c368261386f565b819380356001600160401b0381116100245782613c54918301613931565b835260208101356001600160401b0381116100245782613c7591830161399b565b602084015260408101356001600160401b0381116100245782613c9991830161399b565b60408401526060810135916001600160401b03831161002457606092613cbf920161399b565b910152565b6001600160401b03811161385b57601f01601f191660200190565b81601f8201121561002457803590613cf682613cc4565b92613d0460405194856138a5565b8284526020838301011161002457815f926020809301838601378301015290565b91909160a0818403126100245780356001600160401b0381116100245783613d4e918301613c1d565b9260208201356001600160401b0381116100245781613d6e918401613c1d565b9260408301356001600160401b0381116100245783016060818403126100245760405190613d9b82613840565b80356001600160401b03811161002457810184601f82011215610024578035613dc3816138c6565b91613dd160405193846138a5565b81835260208084019260061b8201019087821161002457602001915b818310613e4c5750505090604091835260208101356020840152013560408201529260608101356001600160401b0381116100245783613e2e918301613cdf565b9260808201356001600160401b0381116100245761394c9201613cdf565b6040838903126100245760206040918251613e668161388a565b613e6f86613697565b81528286013583820152815201920191613ded565b51906001600160a01b038216820361002457565b9080601f83011215610024578151613eaf816138c6565b92613ebd60405194856138a5565b81845260208085019260051b82010192831161002457602001905b828210613ee55750505090565b8151815260209182019101613ed8565b602081830312610024578051906001600160401b03821161002457016080818303126100245760405191613f288361386f565b81516001600160401b03811161002457820181601f8201121561002457805190613f51826138c6565b91613f5f60405193846138a5565b80835260208084019160051b8301019184831161002457602001905b828210613ff457505050835260208201516001600160401b0381116100245781613fa6918401613e98565b602084015260408201516001600160401b0381116100245781613fca918401613e98565b604084015260608201516001600160401b03811161002457613fec9201613e98565b606082015290565b6020809161400184613e84565b815201910190613f7b565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9190811015613a215760051b0190565b356001600160a01b03811681036100245790565b3580151581036100245790565b9190811015613a215760051b81013590601e19813603018212156100245701908135916001600160401b038311610024576020018236038113610024579190565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160a01b039091168152901515602082015260606040820181905261394c939101916140a2565b5190811515820361002457565b91908260809103126100245761410e826140ec565b9161411b60208201613e84565b91606061412a60408401613e84565b92015190565b6003546001600160a01b031680156141b85760206024916040519283809263c11002df60e01b82523060048301525afa801561362b575f90614184575b61394c91506001600160601b0360015416906139b6565b506020813d6020116141b0575b8161419e602093836138a5565b810103126100245761394c905161416d565b3d9150614191565b505f90565b919082039182116139c357565b6001600160a01b039182168152911660208201526040810191909152606081019190915260a0608082018190525f9082015260c00190565b6001600160a01b0391821681529116602082015260a06040820181905261394c949193919261424792916142399190860190613733565b90848203606086015261376f565b91608081840391015261376f565b906020828203126100245781516001600160401b0381116100245761394c9201613e98565b926142a261394c95936142b09360018060a01b03168652608060208701526080860190613733565b90848203604086015261376f565b91606081840391015261376f565b604051906142cb8261386f565b606080838181528160208201528160408201520152565b60206142ec613abe565b6001546004805460035460405163c068a4cd60e01b81529796889660609590951c95879586956143309592949193926001600160a01b039283169216908701614202565b03915afa90811561362b575f91614345575090565b90506020813d60201161436c575b81614360602093836138a5565b81010312610024575190565b3d9150614353565b602061437e613abe565b60015460048054600354604051631b3355ef60e01b81529796889660609590951c95879586956143309592949193926001600160a01b039283169216908701614202565b6143ca614130565b6001600160601b0360015416811190816143e2575090565b90506143ec614374565b1090565b60015460405163e4ec552b60e01b81526001600160a01b039092166004830181905291906020908290602490829060601c5afa90811561362b575f9161447e575b501561446f57600480546001600160a01b031916821790557f9ed2bd12cb184cf96abec450551864ef441ead0f80ac1c3bab504ce07d5a84cd5f80a2565b635958323160e01b5f5260045ffd5b90506020813d6020116144b0575b81614499602093836138a5565b81010312610024576144aa906140ec565b5f614431565b3d915061448c565b926142a261394c95936144d66142b094608088526080880190613733565b90868203602088015261376f565b9093929193845f8260015460601c858360018060a01b036003541661451f60405197889687958694630578da2160e41b86526004860161427a565b03925af190811561362b575f91614ae4575b50825115614adc575f5b8351811015614aa75761454e8188613aaa565b5115614a9f5760016145608284613aaa565b51036146ca576145708184613aaa565b516146bb576001600160a01b036145878286613aaa565b511685614594838a613aaa565b5191805f52600a60205260405f206145ad8482546141bd565b9055805f52600a60205260405f2054156145d6575b916145d09160019493615395565b0161453b565b600554600181036145fe5750916145d091600194936145f3615605565b9193945091506145c2565b5f5b818110614618575b5050916145d091600194936145f3565b909180935061462682613a4d565b905460039190911b1c6001600160a01b0316146146495760010190889291614600565b9193925f19820192909183116139c3576001946146a88a926146846146706145d097613a4d565b8a8060a01b0391549060031b1c1691613a4d565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b6146b0615605565b919394819350614608565b6318ffa99360e31b5f5260045ffd5b60026146d68284613aaa565b510361490d5760016146e88289613aaa565b51036148fe576001600160a01b036147008286613aaa565b51169061470d8185613aaa565b51916008545f600182145f1461482057505060085415613a215760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee35483148015906147e0575b6147d157614763615593565b61476b6155e6565b803b1561002457604051632142170760e11b81523060048201526001600160a01b038816602482015260448101939093525f908390818381606481015b03925af191821561362b576001926147c1575b506145d0565b5f6147cb916138a5565b5f6147bb565b63c97d95cf60e01b5f5260045ffd5b5060065415613a215760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f546001600160a01b0316811415614757565b81811061483c575b0361476b5763c97d95cf60e01b5f5260045ffd5b8461484682613a35565b90549060031b1c14806148db575b61486057600101614820565b5f1982018281116139c3576148b3816148a661487e6148ce94613a35565b90549060031b1c61488e86613a35565b90919082549060031b91821b915f19901b1916179055565b6148ae615593565b613a09565b905460039190911b1c6001600160a01b031661468483613a09565b6148d66155e6565b614828565b50826148e682613a09565b905460039190911b1c6001600160a01b031614614854565b63053dda6d60e21b5f5260045ffd5b60036149198284613aaa565b5103614a90576001600160a01b036149318286613aaa565b51169061493e8185613aaa565b51614949828a613aaa565b51600954845f52600b60205260405f20835f5260205260405f2061496e8382546141bd565b9055845f52600b60205260405f20835f5260205260405f2054156149bb575b50833b15610024576147a8935f92838a60405197889586948593637921219560e11b855230600486016141ca565b600181036149da57506149cc615550565b6149d46155b2565b5f61498d565b5f5b8181106149eb575b50506149d4565b836149f582613a7d565b90549060031b1c14614a0a575b6001016149dc565b85614a1482613a65565b905460039190911b1c6001600160a01b031603614a02575f1982019182116139c357614684614a6983614a5c614a4c614a8196613a7d565b90549060031b1c61488e86613a7d565b614a64615550565b613a65565b905460039190911b1c6001600160a01b031691613a65565b614a896155b2565b5f806149e4565b63b8bcee3f60e01b5f5260045ffd5b6001906145d0565b5092614ad75f5160206156c35f395f51905f5293949692959660405193849360018060a01b0316973097856144b8565b0390a3565b505050509050565b614af891503d805f833e61051a81836138a5565b5f614531565b805151908115614cdb576002546001600160a01b0316915f5b818110614b5b575050805f5160206156c35f395f51905f52915193614ad7602083015192606060408201519101519060405194859460018060a01b031698856144b8565b6040830190614b6b818351613aaa565b5115614cd257606084016001614b82838351613aaa565b5103614bbc5750614bb66001928787614baf85878060a01b03614ba6828c51613aaa565b51169451613aaa565b5192615624565b01614b17565b6002614bc9838351613aaa565b5103614c5f5750905060018060a01b03614be4828551613aaa565b511690614bf5816020860151613aaa565b5191803b1561002457604051632142170760e11b81526001600160a01b0380881660048301528816602482015260448101939093525f908390818381606481015b03925af191821561362b57600192614c4f575b50614bb6565b5f614c59916138a5565b5f614c49565b614c6c8260039251613aaa565b5103614a905783516001600160a01b0390614c88908390613aaa565b5116614ca482614c9c816020890151613aaa565b519451613aaa565b5192813b15610024575f91888389614c3660405198899687958694637921219560e11b8652600486016141ca565b60019150614bb6565b505050565b9190825151908115614f8657614cf5826138c6565b92614d0360405194856138a5565b828452601f19614d12846138c6565b015f5b818110614f63575050614d27836139d7565b94614d31846139d7565b92614d3b856139d7565b905f5b868110614ee057506002546001600160a01b03908116977f000000000000000000000000000000000000000000000000000000000000000090911691823b15610024579088949160405195869363edd9444b60e01b85526080600486015260e4850190805191606060848801528251809152602061010488019301905f5b818110614ea357505050806020604092015160a4880152015160c4860152600319858203016024860152602080855192838152019401905f5b818110614e725750505083614e205f969482948894604485015260031984830301606485015261400c565b03925af1801561362b575f5160206156c35f395f51905f5295614ad793614e4c92614e62575b506139d7565b9360405194859460018060a01b031698856144b8565b5f614e6c916138a5565b5f614e46565b825180516001600160a01b03168752602090810151818801528a97506040909601958e945090920191600101614df5565b825180516001600160a01b0316865260209081015181870152979850959691959294509260019190604001950191019089969594928e9492614dbc565b80614eed6001928a613aaa565b51828060a01b03871690526020614f05828751613aaa565b5101516020614f14838c613aaa565b510152818060a01b03614f28828751613aaa565b515116614f35828c613aaa565b526020614f43828751613aaa565b510151614f508289613aaa565b5281614f5c8286613aaa565b5201614d3e565b602090604051614f728161388a565b5f81525f8382015282828901015201614d15565b50505050565b909392919360015460601c5f6040518092630c3d587560e11b8252602060048301528180614fbd6024820189613733565b03915afa90811561362b575f9161537b575b505f5b835181101561529b57614fe58188613aaa565b5115615293576001614ff78284613aaa565b5103615091576150078184613aaa565b516146bb576001600160a01b0361501e8286613aaa565b51169061502b8189613aaa565b5161503881308986615624565b825f52600a60205260405f20548015615063575b6001935f52600a6020520160405f20555b01614fd2565b60055493600160401b85101561385b5761508a816146848760018099016005556005613a95565b935061504c565b600261509d8284613aaa565b510361517b5760016150af8289613aaa565b51036148fe576001600160a01b036150c78286613aaa565b5116906150d48185613aaa565b5191803b1561002457604051632142170760e11b81526001600160a01b0388166004820152306024820152604481018490525f8160648183865af1801561362b5761516b575b5060065490600160401b82101561385b5761468482600161514094016006556006613a95565b600854600160401b81101561385b5760019261488e828561516694016008556008613a95565b61505d565b5f615175916138a5565b5f61511a565b60036151878284613aaa565b5103614a90576001600160a01b0361519f8286613aaa565b5116906151ac8185613aaa565b516151b7828a613aaa565b5190833b1561002457604051637921219560e11b81525f81806151e08686308f600486016141ca565b038183895af1801561362b57615283575b50835f52600b60205260405f20815f5260205260405f2054908115615230575b6001945f52600b60205260405f20905f526020520160405f205561505d565b600754600160401b81101561385b578561468482600161525594016007556007613a95565b60095494600160401b86101561385b5761527c8261488e886001809a016009556009613a95565b9450615211565b5f61528d916138a5565b5f6151f1565b60019061505d565b509192939490600f6152be6152b5600554600654906139b6565b600754906139b6565b1161536c576001546003546001600160a01b03169060601c803b1561002457825f918783879561530460405197889687958694631a1bb38960e21b86526004860161427a565b03925af1801561362b5761535c575b508351615322575b5050505050565b60405130956001600160a01b0316945f5160206156c35f395f51905f52949193849361534f9392856144b8565b0390a35f8080808061531b565b5f615366916138a5565b5f615313565b63df8153c760e01b5f5260045ffd5b61538f91503d805f833e61051a81836138a5565b5f614fcf565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f5114161516615418575b50156153e157565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f6153d9565b604051638c48052360e01b8152600360048201526001600160a01b0391909116906080816024815f865af1801561362b575f905f915f935f9261552b575b501561551c576001600160601b03166001600160601b0319600154161760015560018060a01b036004541660018060a01b0382160361550d575b50600380546001600160a01b031916831790555f80546601000000000000600160d01b031916603083901b6601000000000000600160d01b03161781556001600160a01b0390911691907fa521c704a92cee73ea8410dc251eece5bd5362f6887c45f3e9566e817aee73699080a3565b615516906143f0565b5f61549d565b63a93eca7960e01b5f5260045ffd5b92505050615548915060803d608011612e2b57612e1a81836138a5565b90925f615463565b600954801561557f575f190161557a61556a826009613a95565b8154905f199060031b1b19169055565b600955565b634e487b7160e01b5f52603160045260245ffd5b600854801561557f575f19016155ad61556a826008613a95565b600855565b600754801561557f575f19016155e16155cc826007613a95565b81549060018060a01b039060031b1b19169055565b600755565b600654801561557f575f19016156006155cc826006613a95565b600655565b600554801561557f575f190161561f6155cc826005613a95565b600555565b6040516323b872dd60e01b81526001600160a01b03928316600482015292909116602483015260448201929092526020905f9060649082855af19081601f3d1160015f51141615166156b5575b501561567957565b60405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606490fd5b3b153d171590505f61567156fe71e9b057bc061d94e1e08fc8c03d958f8fe3068ec75e774d478e7e32b9ff4b2aa26469706673582212209a3a8df7f3b31a9d91364ceefdcb0479c37e5a9f459182a5c83d27ad844dfa3764736f6c634300081e0033000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e590000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae

Deployed Bytecode

0x6080806040526004361015610028575b50346100245763b25befbf60e01b5f5260045ffd5b5f80fd5b5f905f3560e01c90816304dababe1461364557816305549367146134f95750806305c0608c146134d257806305f1a68d1461340757806308f1bdaa146133b25780630b9252f0146131d4578063150b7a02146131af5780631e03544214612f85578063219461ed14612f4057806321ab889d14612f075780632723ae1f14612ede5780632c3c717314612bd55780632dd3100014612b90578063358bca7714612a7e5780634046ebae14612a5357806342c8d71514612a3857806347cf6c9e146129f75780634a1ec375146125d45780635ae80951146125185780636048dd00146120a6578063653b92b414611d3c578063656e872914611c4c57806368719ee314611a0c57806371a84c71146119b45780637afc01be146115055780637b103999146114e45780637c0b5337146112cb57806387bbaeb6146112a95780638da5cb5b14611280578063907a9429146112655780639086ab3914611220578063a0d9f5cd1461113c578063ac891de4146110f0578063adb33ab6146110ad578063b8eabab014610f4f578063bc68c67614610f33578063bfed6b0314610f17578063c047e56314610efc578063c0c53b8b14610d79578063d5d4305b14610d54578063df03e1a014610be1578063e1c50100146107ca578063e36db785146107a1578063eac697c91461077b578063edfe1faf1461073b578063f23a6e61146106e4578063f2fde38b14610573578063fb787d01146103535763fe67a54b0361000f5734610313578060031936011261031357805460301c6001600160a01b0316330361034457807f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b15610321576040516306e8502160e11b815260048101839052828160248183865af1908115610339578391610324575b5050815460ff19168255803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b8161030c916138a5565b6103135780f35b80fd5b6040513d84823e3d90fd5b50fd5b8161032e916138a5565b61032157815f6102cc565b6040513d85823e3d90fd5b6399b01c4760e01b8152600490fd5b50346103135780600319360112610313576002546001600160a01b03163303610564577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b15610313576040516306e8502160e11b815260048101829052818160248183875af180156103165790829161054f575b50549160ff831661054057600354926001600160a01b0384169081156105325783946001600160601b0360a01b166003556601000000000000600160d01b031916835561045583806001546001600160601b03198116600155610434613abe565b604051630578da2160e41b8152968795869490938593918b6004860161427a565b039260601c5af1801561052757610507575b50803b156104ee57828091602460405180948193632ce1ed1560e01b83523060048401525af19081156103395783916104f2575b50506040519082807fa521c704a92cee73ea8410dc251eece5bd5362f6887c45f3e9566e817aee73698180a3803b156104ee5781600481858094633b4438cf60e21b83525af18015610316576103025750f35b5050fd5b816104fc916138a5565b61032157815f61049b565b610522903d8086833e61051a81836138a5565b810190614255565b610467565b6040513d86823e3d90fd5b623cb2cf60e01b8452600484fd5b635441f54960e11b8252600482fd5b81610559916138a5565b61031357805f6103d3565b635fc483c560e01b8152600490fd5b50346103135760203660031901126103135761058d613655565b907f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b031633036106d5577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b156106d1576040516306e8502160e11b815260048101839052828160248183865af18015610339579083916106bc575b505460ff81166106ad5763ffffffff9060101c1661012c81018091116106995742111561068a57819260018060a01b03166001600160601b0360a01b6002541617600255803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b63036a87d960e21b8252600482fd5b634e487b7160e01b83526011600452602483fd5b635441f54960e11b8352600483fd5b816106c6916138a5565b6106d157815f61061a565b5080fd5b630636a15760e11b8152600490fd5b50346103135760a0366003190112610313576106fe613655565b50610707613681565b506084356001600160401b0381116106d1576107279036906004016136ab565b505060405163f23a6e6160e01b8152602090f35b5034610313578060031936011261031357602090610757614374565b61075f614130565b91828211156107735750035b604051908152f35b91505061076b565b503461031357806003193601126103135763ffffffff6020915460101c16604051908152f35b50346103135780600319360112610313576003546040516001600160a01b039091168152602090f35b5034610313576020366003190112610313576107e4613655565b815490919060301c6001600160a01b03163303610344577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b15610313576040516306e8502160e11b815260048101829052818160248183875af1801561031657908291610bcc575b505465ffffffff00ff19164260101b65ffffffff00001617600190811782556003549054936001600160a01b039190911691906001600160601b0385168261089e613abe565b9282829992939a8960018060a01b03600454166108d2886040519e8f9687958695633a137bf360e11b875260048701614202565b039160601c5afa978815610a2a578698610b00575b506040516342f2e72760e11b81526001600160a01b03909516600486015260248501849052602085604481898b5af1948515610a2a578695610acc575b5061092f84866139b6565b8515908115610a47575b50610a3957803b15610a3557858091600460405180948193633b4438cf60e21b83525af18015610a2a57908691610a11575b505061099197969593929161099f6109ad926040519a8b9a60e08c5260e08c0190613733565b908a820360208c015261376f565b9088820360408a015261376f565b926060870152608086015260a085015283810360c0850152602080845192838152019301915b8181106109e1575050500390f35b919350916020606060019260408751805183528481015185840152015160408201520194019101918493926109d3565b81610a1b916138a5565b610a2657845f61096b565b8480fd5b6040513d88823e3d90fd5b8580fd5b627616e960e61b8652600486fd5b98999593919794929050869787985b8b518a1015610ab5578b906040610a798c610a718186613aaa565b515194613aaa565b51015191828102928184041490151715610aa157600191610a99916139b6565b990198610a56565b634e487b7160e01b8a52601160045260248afd5b612710919395979b9a92949699500410155f610939565b9094506020813d602011610af8575b81610ae8602093836138a5565b810103126100245751935f610924565b3d9150610adb565b9097503d8087833e610b1281836138a5565b810190602081830312610bc4578051906001600160401b038211610bc8570181601f82011215610bc457805190610b48826138c6565b92610b5660405194856138a5565b82845260206060818601940283010191818311610bc057602001925b828410610b845750505050965f6108e7565b606084830312610bc0576020606091604051610b9f81613840565b86518152828701518382015260408701516040820152815201930192610b72565b8980fd5b8680fd5b8780fd5b81610bd6916138a5565b61031357805f610858565b503461031357602036600319011261031357610bfb613655565b815460301c6001600160a01b03163303610d455781907f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af1908115610527578491610d30575b5050600280546001600160a01b0319166001600160a01b039283169081179091557f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e5990911690813b15610d2b578391602483926040519485938492637ccd031560e01b845260048401525af1908115610339578391610d16575b5050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81610d20916138a5565b61032157815f610ce8565b505050fd5b81610d3a916138a5565b6104ee57825f610c6e565b6399b01c4760e01b8252600482fd5b50346103135780600319360112610313576020610d6f6143c2565b6040519015158152f35b503461031357606036600319011261031357610d93613655565b90610d9c613681565b6044356001600160a01b038116808203610ef8577f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03163303610ee9577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031692833b15610a26576040516306e8502160e11b815260016004820152858160248183895af18015610a2a57908691610ed4575b50506001600160a01b03811615610ec557849560018060a01b03166001600160601b0360a01b60025416176002556001600160601b036001549181199060601b16911617600155610eb6575b50803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b610ebf90615425565b5f610e89565b6311a1e69760e01b8552600485fd5b81610ede916138a5565b610a2657845f610e3d565b630636a15760e11b8452600484fd5b8380fd5b5034610313578060031936011261031357602061076b614374565b50346103135780600319360112610313576020604051600f8152f35b5034610313578060031936011261031357602060405160038152f35b503461031357610f5e366137d2565b60025493969590936001600160a01b0316330361109e577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031694853b15610bc4576040516306e8502160e11b8152600481018890528781602481838b5af180156110935790889161107e575b505460ff811661106f579761100e6110209361101693611026999a9b65ffffffff00004260101b169065ffffffff00001916178c5536916138dd565b94369161394f565b913394369161394f565b916144e4565b61102e6143c2565b611060578082913b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b6302fb7a9160e31b8252600482fd5b635441f54960e11b8852600488fd5b81611088916138a5565b610bc457865f610fd2565b6040513d8a823e3d90fd5b635fc483c560e01b8652600486fd5b5034610313576040366003190112610313576020906040906001600160a01b036110d5613655565b168152600b8352818120602435825283522054604051908152f35b5034610313578060031936011261031357602061110b614130565b6001600160601b03600154168111908161112b575b506040519015158152f35b90506111356142e2565b1082611120565b50346103135760203660031901126103135780611157613655565b7f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b156104ee576040516306e8502160e11b815260016004820152838160248183875af190811561052757849161120b575b5050338352600c6020526040832080546001600160a01b0319166001600160a01b03909216919091179055803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81611215916138a5565b6104ee57825f6111b4565b50346103135780600319360112610313576040517f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b03168152602090f35b5034610313578060031936011261031357602061076b6142e2565b50346103135780600319360112610313576002546040516001600160a01b039091168152602090f35b503461031357806003193601126103135760ff60209154166040519015158152f35b5034610313576020366003190112610313576112e5613655565b6002546001600160a01b031633036114d55781907f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af19081156105275784916114c0575b505060405163131bac4760e11b81523060048201526001600160a01b0382811660248301527f0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae1690602081604481855afa9081156114b5578591611480575b50611420575b506040518381526001600160a01b03909116907fd61f2c33e366f60a2f32f50d952952a311e9ed22662d58f31dc6a3e2c8ba61f190602090a2803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b803b15610d2b5760405163bdac7ca360e01b81523060048201526001600160a01b03831660248201529084908290604490829084905af190811561052757849161146b575b506113bb565b81611475916138a5565b6104ee57825f611465565b9450506020843d6020116114ad575b8161149c602093836138a5565b81010312610024578493515f6113b5565b3d915061148f565b6040513d87823e3d90fd5b816114ca916138a5565b6104ee57825f611356565b635fc483c560e01b8252600482fd5b5034610313578060031936011261031357602060015460601c604051908152f35b5034610313576060366003190112610313576004356001600160401b0381116106d1576115369036906004016136ab565b9190611540613681565b906044356001600160401b038111610ef8576115609036906004016136ab565b90947f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031693843b15610a35576040516306e8502160e11b8152600160048201528681602481838a5af180156119a957908791611994575b505460ff81166119855765ffffffff000019164260101b65ffffffff0000161786556003546001600160a01b03169333851415938480611961575b61195257879861163b9161160f9998996142be565b50886040805161161e81613840565b6060815282602082015201526116326142be565b50810190613d25565b93949096929192333b1561194e57604051637afcd25560e11b815260206004820152918b9183918291611673916024840191906140a2565b038183335af18015611943578a9589928892611921575b5090816116a392519060406020820151910151916144e4565b6117ee575b6116e2956116c0926116bb878094614afe565b614ce0565b6040518094819263a129568d60e01b835260206004840152602483019061400c565b0381836001600160a01b0386165af180156114b5576117169286916117cc575b508051906040602082015191015191614f8c565b81151590816117c4575b5061177f575b50506117306143c2565b611060578082913b15610321578190600460405180948193633b4438cf60e21b83525af180156103165761176a575b602060405160038152f35b6117758280926138a5565b610313578061175f565b803b156106d157818091602460405180948193632ce1ed1560e01b83523060048401525af18015610316571561172657816117b9916138a5565b6106d157815f611726565b90505f611720565b6117e891503d8088833e6117e081836138a5565b810190613ef5565b5f611702565b6002546001600160a01b03168952600c6020526040892080546001600160a01b0319169055925061181d613abe565b89959291956118ba575b60015460601c803b156118b657611859968c8094604051998a9586948593631a1bb38960e21b8552336004860161427a565b03925af19384156118ab578994611890575b506116e2956116c0926116bb87809461188333615425565b94505050925095506116a8565b8461189e91979592976138a5565b610bc8578792945f61186b565b6040513d8b823e3d90fd5b8b80fd5b808b878c858360015460601c926118e760405197889687958694630578da2160e41b86526004860161427a565b03925af18015611916576118fc575b50611827565b61190f903d808e833e61051a81836138a5565b505f6118f6565b6040513d8e823e3d90fd5b87935061193192509690966138a5565b61193f5786858a955f61168a565b8880fd5b6040513d8c823e3d90fd5b8a80fd5b63a9faa8f560e01b8852600488fd5b506002546001600160a01b039081168952600c6020526040892054163314156115fa565b635441f54960e11b8752600487fd5b8161199e916138a5565b610a3557855f6115bf565b6040513d89823e3d90fd5b50346103135760403660031901126103135760406119d0613655565b916119d9613681565b9260018060a01b03168152600d602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b503461031357602036600319011261031357611a26613655565b6002549091906001600160a01b03163303610564577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b156106d1576040516306e8502160e11b815260016004820152828160248183865af1801561033957908391611c37575b505460ff81166106ad5765ffffffff000019164260101b65ffffffff000016178255611ac1613abe565b60035492956001600160a01b03938416939192909182168414611c285785968415159384611bcd575b60015460601c803b1561193f57611b1c9389809460405196879586948593631a1bb38960e21b85528b6004860161427a565b03925af1908115610a2a578691611bb8575b5050611b3990615425565b611b6a5750803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b803b156104ee57828091602460405180948193632ce1ed1560e01b83523060048401525af1908115610339578391611ba3575b50610e89565b81611bad916138a5565b61032157815f611b9d565b81611bc2916138a5565b610a2657845f611b2e565b80888460015460601c85838b611bf960405197889687958694630578da2160e41b86526004860161427a565b03925af180156118ab57611c0e575b50611aea565b611c21903d808b833e61051a81836138a5565b505f611c08565b63ce5a86f760e01b8652600486fd5b81611c41916138a5565b6106d157815f611a97565b503461031357611c5b366137d2565b60025490969592939192906001600160a01b0316330361109e577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031694853b15610bc4576040516306e8502160e11b8152600160048201528781602481838b5af1801561109357908891611d27575b505460ff1661198557611016611cfb959361100e899a9694611cf59436916138dd565b91614f8c565b803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81611d31916138a5565b610bc457865f611cd2565b5034610313576080366003190112610313576004356001600160401b0381116106d157611d6d903690600401613931565b6024356001600160401b0381116120a257611d8c90369060040161399b565b916044356001600160401b0381116106d157611dac90369060040161399b565b90611db561366b565b815490939060301c6001600160a01b03163303610d45577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b156120a2576040516306e8502160e11b815260048101849052838160248183865af180156105275790849161208d575b505060015460601c90836040518093630c3d587560e11b8252602060048301528180611e576024820189613733565b03915afa918215610527578492612071575b50835b835181101561200957611e7f8187613aaa565b5115612001576001611e918285613aaa565b5103611ee6576001906001600160a01b03611eac8287613aaa565b51168652600a60205260408620545b80611ec6838a613aaa565b5111611ed5575b505b01611e6c565b611edf8289613aaa565b525f611ecd565b6002611ef28285613aaa565b5103611fa4576001600160a01b03611f0a8286613aaa565b5116906020611f19828b613aaa565b516024604051809581936331a9108f60e11b835260048301525afa8015610a2a578690611f69575b60019250306001600160a01b039190911603611f615760ff825b16611ebb565b60ff86611f5b565b50906020813d8211611f9c575b81611f83602093836138a5565b81010312610a355790611f97600192613e84565b611f41565b3d9150611f76565b6003611fb08285613aaa565b5103611ff2576001906001600160a01b03611fcb8287613aaa565b51168652600b60205260408620611fe2828b613aaa565b5187526020526040862054611ebb565b63b8bcee3f60e01b8552600485fd5b600190611ecf565b848681846120198b848e8b6144e4565b803b156106d1578190600460405180948193633b4438cf60e21b83525af180156103395761205c575b604051602080825281906120589082018561376f565b0390f35b6120678380926138a5565b6106d15781612042565b6120869192503d8086833e61051a81836138a5565b905f611e69565b81612097916138a5565b6120a257825f611e28565b8280fd5b50346103135760a0366003190112610313576004356001600160401b0381116106d1576120d79036906004016137a2565b91906024356001600160401b0381116120a2576120f89036906004016137a2565b936044356001600160401b038111610a26576121189036906004016137a2565b93909261212361366b565b926084356001600160401b038111610bc8576121439036906004016137a2565b60025491969093916001600160a01b03163303612509577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031697883b15610bc0576040516306e8502160e11b8152600160048201528a81602481838e5af180156124fe57908b916124e9575b505465ffffffff000019164260101b65ffffffff000016178a55838b148015906124df575b6124cd577f0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae6001600160a01b031695938a5b8181106122ce575050505050508580975050855b81811061225857868087803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b61226b612266828488614030565b614040565b833b15610bc857604051630d0a119360e31b81526001600160a01b038681166004830152919091166024820152878160448183885af19081156110935788916122b9575b5050600101612222565b816122c3916138a5565b610bc457865f6122af565b6122dc612266828489614030565b60405163131bac4760e11b81523060048201526001600160a01b03821660248201526020816044818d5afa908115612427578f918f91859291612494575b506123319161232c911515938a614030565b614054565b151503612438575b8d612345838688614061565b9050612399575b907fd61f2c33e366f60a2f32f50d952952a311e9ed22662d58f31dc6a3e2c8ba61f1602061238161232c86600197968c614030565b926040519315158452858060a01b031692a20161220e565b61232c836123a79289614030565b6123b2838688614061565b916001600160a01b0384163b1561243457908f916123e56040519485938493630ede8ef360e11b855233600486016140c2565b0381836001600160a01b0387165af18015612427578f918f9161240a575b505061234c565b81925090612417916138a5565b612423578d8d5f612403565b8c80fd5b8e604051903d90823e3d90fd5b8f80fd5b883b156124235760405163bdac7ca360e01b81523060048201526001600160a01b03821660248201528d81604481838e5af1801561242757908e9161247f575b5050612339565b81612489916138a5565b612423578c5f612478565b925050506020813d82116124c5575b816124b0602093836138a5565b8101031261002457518e90839061233161231a565b3d91506124a3565b6001621398b960e31b03198a5260048afd5b50808414156121dc565b816124f3916138a5565b610bc057895f6121b7565b6040513d8d823e3d90fd5b635fc483c560e01b8952600489fd5b503461031357602036600319011261031357612532613655565b90602061253d613abe565b60015460035460405163068e097b60e51b815297889560609390931c948694859461257994919390916001600160a01b03169060048701614202565b03915afa9081156125c85790612595575b602090604051908152f35b506020813d6020116125c0575b816125af602093836138a5565b81010312610024576020905161258a565b3d91506125a2565b604051903d90823e3d90fd5b5034610313576060366003190112610313576125ee613655565b6002546024359291604435916001600160a01b031633036129e8577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031691823b15610ef8576040516306e8502160e11b815260048101859052848160248183885af180156114b5579085916129d3575b505465ffffffff000019164260101b65ffffffff0000161784556001600160a01b039091169083826127785750478480808084335af1953d15612770573d966126ae88613cc4565b976126bc604051998a6138a5565b88523d8760208a013e5b1561274b578596505b81612705575b50505050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b60405193845260208401526040830152606082015233907fb180a6a3fa923907a3d3d7175c65292a5003d4f5b24dea54d607814d5664378d60803092a35f8080806126d5565b60405162461bcd60e51b8152602060048201528061276c602482018a61400c565b0390fd5b6060966126c6565b6001820361281d5784956040516370a0823160e01b8152306004820152602081602481885afa9081156119a95787916127e8575b50848752600a6020526040872054908181116127ca575b50506126cf565b6127d59293506141bd565b906127e1823386615395565b5f806127c3565b9650506020863d602011612815575b81612804602093836138a5565b81010312610024578695515f6127ac565b3d91506127f7565b600282036128ed576006549585805b888110612899575b508697506126cf5760019150833b15610a3557604051632142170760e11b815230600482015233602482015260448101829052868160648183895af19081156119a95787916128845750506126cf565b8161288e916138a5565b610a3557855f6127c3565b856128a382613a09565b905460039190911b1c6001600160a01b031614806128d5575b6128c85760010161282c565b505085965060015f612834565b50826128e082613a35565b90549060031b1c146128bc565b60038203611ff2578495604051627eeac760e11b8152306004820152816024820152602081604481885afa9081156119a957879161299e575b50848752600b6020526040872082885260205260408720549081811161294d5750506126cf565b6129589293506141bd565b90833b15610a3557604051637921219560e11b815286818061298086863330600486016141ca565b038183895af19081156119a9578791156127c3578161288e916138a5565b9650506020863d6020116129cb575b816129ba602093836138a5565b81010312610024578695515f612926565b3d91506129ad565b816129dd916138a5565b610ef857835f612666565b635fc483c560e01b8352600483fd5b5034610313576020366003190112610313576020906001600160a01b03612a1c613655565b168152600c8252604060018060a01b0391205416604051908152f35b5034610313578060031936011261031357602061076b614130565b50346103135780600319360112610313575460405160309190911c6001600160a01b03168152602090f35b50346103135760203660031901126103135780612a99613655565b7f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af1908115610527578491612b7b575b505033808452600d602090815260408086206001600160a01b03949094165f81815294835293819020805460ff19169055518581527f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c19190a3803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81612b85916138a5565b6104ee57825f612af6565b50346103135780600319360112610313576040517f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03168152602090f35b503461031357612be4366136d8565b9294939192917f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03163303612ecf577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031693843b15610a35576040516306e8502160e11b8152600481018790528681602481838a5af180156119a957908791612eba575b505460ff81166119855765ffffffff000019164260101b65ffffffff0000161786557f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8054600180546001600160a01b031983166001600160a01b03968716179093556001600160601b03831660608b811b6bffffffffffffffffffffffff191691909117909155909891901c9216808314159081612e41575b50612e32576003546001600160a01b03169081612db6575b50508495303b15610a3557612d758693604051958694859463026d5d5f60e11b865260018060a01b031660048601526024850152600360448501526080606485015260848401916140a2565b038183305af1908115610339578391610d16575050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b869160246080926040519485938492638c48052360e01b845260048401525af1908115610a2a578691612e00575b5015612df1575f80612d29565b63a93eca7960e01b8552600485fd5b612e22915060803d608011612e2b575b612e1a81836138a5565b8101906140f9565b5050505f612de4565b503d612e10565b6311a1e69760e01b8652600486fd5b9050602060018060a01b036004541660246040518094819363e4ec552b60e01b835260048301525afa9081156119a9578791612e80575b50155f612d11565b90506020813d602011612eb2575b81612e9b602093836138a5565b81010312610bc457612eac906140ec565b5f612e78565b3d9150612e8e565b81612ec4916138a5565b610a3557855f612c77565b630636a15760e11b8552600485fd5b50346103135780600319360112610313576004546040516001600160a01b039091168152602090f35b5034610313576020366003190112610313576020906040906001600160a01b03612f2f613655565b168152600a83522054604051908152f35b50346103135780600319360112610313576040517f0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae6001600160a01b03168152602090f35b503461031357612f94366137d2565b6002549496959294929391926001600160a01b0316330361109e577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031694853b15610bc4576040516306e8502160e11b8152600160048201528781602481838b5af180156110935790889161319a575b505465ffffffff000019164260101b65ffffffff000016178755878114801590613190575b61317e578697875b81811061306f57888089803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b61307d61226682848a614030565b61308b61232c838689614030565b33808c52600d60209081526040808e206001600160a01b039095165f81815295835294819020805460ff95151595861660ff19919091161790555192835283927f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c19190a36130fa828689614061565b905061310a575b50600101613039565b61311861232c838689614030565b61312383878a614061565b9092803b1561242357613151938d809460405196879586948593635f4860df60e01b855233600486016140c2565b03925af1908115611943578a91613169575b50613101565b81613173916138a5565b61193f57885f613163565b6001621398b960e31b03198752600487fd5b5081881415613031565b816131a4916138a5565b610bc457865f61300c565b5034610313576131be366136d8565b50505050506020604051630a85bd0160e11b8152f35b5034610313576040366003190112610313576131ee613655565b6024356001600160401b0381116120a25761320d9036906004016136ab565b919060018060a01b0360025416803314908115613390575b5015613353577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031692833b15610a26576040516306e8502160e11b81526001600482015285908181602481838a5af180156103165761333e575b505460ff811661332f57926116c0816132e3816116bb6132c982988c986132e89b65ffffffff00004260101b169065ffffffff00001916178a55810190613d25565b9993809792969197519060406020820151910151916144e4565b614afe565b0381836001600160a01b0386165af180156105275761102692859161331b57508051906040602082015191015191614f8c565b6117e891503d8087833e6117e081836138a5565b635441f54960e11b8652600486fd5b81613348916138a5565b610a2657845f613287565b60405162461bcd60e51b8152602060048201526015602482015274209d1027b7363c9020b9b9b2ba1026b0b730b3b2b960591b6044820152606490fd5b855250600d60209081526040808620335f908152925281205460ff1690613225565b50346103135780600319360112610313576133eb6133f96120586133d4613abe565b919390604051958695606087526060870190613733565b90858203602087015261376f565b90838203604085015261376f565b503461031357602036600319011261031357613421613655565b6002549091906001600160a01b03163303610564577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b156106d1576040516306e8502160e11b815260016004820152828160248183865af18015610339579083916134bd575b50506003546001600160a01b03166134ae57611cfb82936143f0565b63ce5a86f760e01b8252600482fd5b816134c7916138a5565b6106d157815f613492565b503461031357806003193601126103135760206001600160601b0360015416604051908152f35b90503461002457602036600319011261002457600354600435906001600160a01b03163303613636577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031691823b15610024576306e8502160e11b8152600160048201525f8160248183875af1801561362b57613616575b50825460ff81166136075765ffffffff000019164260101b65ffffffff00001617835580151590816135e0575b50611060578082913b15610321578190600460405180948193633b4438cf60e21b83525af180156103165761176a57602060405160038152f35b90506136006135ed614374565b916001600160601b0360015416906139b6565b115f6135a6565b635441f54960e11b8452600484fd5b6136239193505f906138a5565b5f915f613579565b6040513d5f823e3d90fd5b63a9faa8f560e01b5f5260045ffd5b3461002457613653366136d8565b005b600435906001600160a01b038216820361002457565b606435906001600160a01b038216820361002457565b602435906001600160a01b038216820361002457565b35906001600160a01b038216820361002457565b9181601f84011215610024578235916001600160401b038311610024576020838186019501011161002457565b906080600319830112610024576004356001600160a01b038116810361002457916024356001600160a01b0381168103610024579160443591606435906001600160401b0382116100245761372f916004016136ab565b9091565b90602080835192838152019201905f5b8181106137505750505090565b82516001600160a01b0316845260209384019390920191600101613743565b90602080835192838152019201905f5b81811061378c5750505090565b825184526020938401939092019160010161377f565b9181601f84011215610024578235916001600160401b038311610024576020808501948460051b01011161002457565b6060600319820112610024576004356001600160401b03811161002457816137fc916004016137a2565b929092916024356001600160401b038111610024578161381e916004016137a2565b92909291604435906001600160401b0382116100245761372f916004016137a2565b606081019081106001600160401b0382111761385b57604052565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761385b57604052565b604081019081106001600160401b0382111761385b57604052565b90601f801991011681019081106001600160401b0382111761385b57604052565b6001600160401b03811161385b5760051b60200190565b9291906138e9816138c6565b936138f760405195866138a5565b602085838152019160051b810192831161002457905b82821061391957505050565b6020809161392684613697565b81520191019061390d565b9080601f830112156100245781602061394c933591016138dd565b90565b92919061395b816138c6565b9361396960405195866138a5565b602085838152019160051b810192831161002457905b82821061398b57505050565b813581526020918201910161397f565b9080601f830112156100245781602061394c9335910161394f565b919082018092116139c357565b634e487b7160e01b5f52601160045260245ffd5b906139e1826138c6565b6139ee60405191826138a5565b82815280926139ff601f19916138c6565b0190602036910137565b600654811015613a215760065f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b600854811015613a215760085f5260205f2001905f90565b600554811015613a215760055f5260205f2001905f90565b600754811015613a215760075f5260205f2001905f90565b600954811015613a215760095f5260205f2001905f90565b8054821015613a21575f5260205f2001905f90565b8051821015613a215760209160051b010190565b6005549060065460075491828285010192613ad8846139d7565b94613aeb613ae5866139d7565b956139d7565b935f915b808310613bd857505f905b808210613b805750505f905b828210613b1257505050565b60018091613b1f84613a65565b838060a01b0391549060031b1c16613b3685613a7d565b90549060031b1c9080613b49848d613aaa565b5281613b55848c613aaa565b525f52600b60205260405f20905f5260205260405f2054613b768289613aaa565b5201910190613b06565b909160018091613b8f85613a09565b838060a01b0391549060031b1c16613ba7828c613aaa565b52613bb185613a35565b90549060031b1c613bc2828b613aaa565b5281613bce828a613aaa565b5201920190613afa565b9180613be5600192613a4d565b838060a01b0391549060031b1c1680613bfe838c613aaa565b525f52600a60205260405f2054613c158289613aaa565b520191613aef565b91906080838203126100245760405190613c368261386f565b819380356001600160401b0381116100245782613c54918301613931565b835260208101356001600160401b0381116100245782613c7591830161399b565b602084015260408101356001600160401b0381116100245782613c9991830161399b565b60408401526060810135916001600160401b03831161002457606092613cbf920161399b565b910152565b6001600160401b03811161385b57601f01601f191660200190565b81601f8201121561002457803590613cf682613cc4565b92613d0460405194856138a5565b8284526020838301011161002457815f926020809301838601378301015290565b91909160a0818403126100245780356001600160401b0381116100245783613d4e918301613c1d565b9260208201356001600160401b0381116100245781613d6e918401613c1d565b9260408301356001600160401b0381116100245783016060818403126100245760405190613d9b82613840565b80356001600160401b03811161002457810184601f82011215610024578035613dc3816138c6565b91613dd160405193846138a5565b81835260208084019260061b8201019087821161002457602001915b818310613e4c5750505090604091835260208101356020840152013560408201529260608101356001600160401b0381116100245783613e2e918301613cdf565b9260808201356001600160401b0381116100245761394c9201613cdf565b6040838903126100245760206040918251613e668161388a565b613e6f86613697565b81528286013583820152815201920191613ded565b51906001600160a01b038216820361002457565b9080601f83011215610024578151613eaf816138c6565b92613ebd60405194856138a5565b81845260208085019260051b82010192831161002457602001905b828210613ee55750505090565b8151815260209182019101613ed8565b602081830312610024578051906001600160401b03821161002457016080818303126100245760405191613f288361386f565b81516001600160401b03811161002457820181601f8201121561002457805190613f51826138c6565b91613f5f60405193846138a5565b80835260208084019160051b8301019184831161002457602001905b828210613ff457505050835260208201516001600160401b0381116100245781613fa6918401613e98565b602084015260408201516001600160401b0381116100245781613fca918401613e98565b604084015260608201516001600160401b03811161002457613fec9201613e98565b606082015290565b6020809161400184613e84565b815201910190613f7b565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9190811015613a215760051b0190565b356001600160a01b03811681036100245790565b3580151581036100245790565b9190811015613a215760051b81013590601e19813603018212156100245701908135916001600160401b038311610024576020018236038113610024579190565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160a01b039091168152901515602082015260606040820181905261394c939101916140a2565b5190811515820361002457565b91908260809103126100245761410e826140ec565b9161411b60208201613e84565b91606061412a60408401613e84565b92015190565b6003546001600160a01b031680156141b85760206024916040519283809263c11002df60e01b82523060048301525afa801561362b575f90614184575b61394c91506001600160601b0360015416906139b6565b506020813d6020116141b0575b8161419e602093836138a5565b810103126100245761394c905161416d565b3d9150614191565b505f90565b919082039182116139c357565b6001600160a01b039182168152911660208201526040810191909152606081019190915260a0608082018190525f9082015260c00190565b6001600160a01b0391821681529116602082015260a06040820181905261394c949193919261424792916142399190860190613733565b90848203606086015261376f565b91608081840391015261376f565b906020828203126100245781516001600160401b0381116100245761394c9201613e98565b926142a261394c95936142b09360018060a01b03168652608060208701526080860190613733565b90848203604086015261376f565b91606081840391015261376f565b604051906142cb8261386f565b606080838181528160208201528160408201520152565b60206142ec613abe565b6001546004805460035460405163c068a4cd60e01b81529796889660609590951c95879586956143309592949193926001600160a01b039283169216908701614202565b03915afa90811561362b575f91614345575090565b90506020813d60201161436c575b81614360602093836138a5565b81010312610024575190565b3d9150614353565b602061437e613abe565b60015460048054600354604051631b3355ef60e01b81529796889660609590951c95879586956143309592949193926001600160a01b039283169216908701614202565b6143ca614130565b6001600160601b0360015416811190816143e2575090565b90506143ec614374565b1090565b60015460405163e4ec552b60e01b81526001600160a01b039092166004830181905291906020908290602490829060601c5afa90811561362b575f9161447e575b501561446f57600480546001600160a01b031916821790557f9ed2bd12cb184cf96abec450551864ef441ead0f80ac1c3bab504ce07d5a84cd5f80a2565b635958323160e01b5f5260045ffd5b90506020813d6020116144b0575b81614499602093836138a5565b81010312610024576144aa906140ec565b5f614431565b3d915061448c565b926142a261394c95936144d66142b094608088526080880190613733565b90868203602088015261376f565b9093929193845f8260015460601c858360018060a01b036003541661451f60405197889687958694630578da2160e41b86526004860161427a565b03925af190811561362b575f91614ae4575b50825115614adc575f5b8351811015614aa75761454e8188613aaa565b5115614a9f5760016145608284613aaa565b51036146ca576145708184613aaa565b516146bb576001600160a01b036145878286613aaa565b511685614594838a613aaa565b5191805f52600a60205260405f206145ad8482546141bd565b9055805f52600a60205260405f2054156145d6575b916145d09160019493615395565b0161453b565b600554600181036145fe5750916145d091600194936145f3615605565b9193945091506145c2565b5f5b818110614618575b5050916145d091600194936145f3565b909180935061462682613a4d565b905460039190911b1c6001600160a01b0316146146495760010190889291614600565b9193925f19820192909183116139c3576001946146a88a926146846146706145d097613a4d565b8a8060a01b0391549060031b1c1691613a4d565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b6146b0615605565b919394819350614608565b6318ffa99360e31b5f5260045ffd5b60026146d68284613aaa565b510361490d5760016146e88289613aaa565b51036148fe576001600160a01b036147008286613aaa565b51169061470d8185613aaa565b51916008545f600182145f1461482057505060085415613a215760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee35483148015906147e0575b6147d157614763615593565b61476b6155e6565b803b1561002457604051632142170760e11b81523060048201526001600160a01b038816602482015260448101939093525f908390818381606481015b03925af191821561362b576001926147c1575b506145d0565b5f6147cb916138a5565b5f6147bb565b63c97d95cf60e01b5f5260045ffd5b5060065415613a215760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f546001600160a01b0316811415614757565b81811061483c575b0361476b5763c97d95cf60e01b5f5260045ffd5b8461484682613a35565b90549060031b1c14806148db575b61486057600101614820565b5f1982018281116139c3576148b3816148a661487e6148ce94613a35565b90549060031b1c61488e86613a35565b90919082549060031b91821b915f19901b1916179055565b6148ae615593565b613a09565b905460039190911b1c6001600160a01b031661468483613a09565b6148d66155e6565b614828565b50826148e682613a09565b905460039190911b1c6001600160a01b031614614854565b63053dda6d60e21b5f5260045ffd5b60036149198284613aaa565b5103614a90576001600160a01b036149318286613aaa565b51169061493e8185613aaa565b51614949828a613aaa565b51600954845f52600b60205260405f20835f5260205260405f2061496e8382546141bd565b9055845f52600b60205260405f20835f5260205260405f2054156149bb575b50833b15610024576147a8935f92838a60405197889586948593637921219560e11b855230600486016141ca565b600181036149da57506149cc615550565b6149d46155b2565b5f61498d565b5f5b8181106149eb575b50506149d4565b836149f582613a7d565b90549060031b1c14614a0a575b6001016149dc565b85614a1482613a65565b905460039190911b1c6001600160a01b031603614a02575f1982019182116139c357614684614a6983614a5c614a4c614a8196613a7d565b90549060031b1c61488e86613a7d565b614a64615550565b613a65565b905460039190911b1c6001600160a01b031691613a65565b614a896155b2565b5f806149e4565b63b8bcee3f60e01b5f5260045ffd5b6001906145d0565b5092614ad75f5160206156c35f395f51905f5293949692959660405193849360018060a01b0316973097856144b8565b0390a3565b505050509050565b614af891503d805f833e61051a81836138a5565b5f614531565b805151908115614cdb576002546001600160a01b0316915f5b818110614b5b575050805f5160206156c35f395f51905f52915193614ad7602083015192606060408201519101519060405194859460018060a01b031698856144b8565b6040830190614b6b818351613aaa565b5115614cd257606084016001614b82838351613aaa565b5103614bbc5750614bb66001928787614baf85878060a01b03614ba6828c51613aaa565b51169451613aaa565b5192615624565b01614b17565b6002614bc9838351613aaa565b5103614c5f5750905060018060a01b03614be4828551613aaa565b511690614bf5816020860151613aaa565b5191803b1561002457604051632142170760e11b81526001600160a01b0380881660048301528816602482015260448101939093525f908390818381606481015b03925af191821561362b57600192614c4f575b50614bb6565b5f614c59916138a5565b5f614c49565b614c6c8260039251613aaa565b5103614a905783516001600160a01b0390614c88908390613aaa565b5116614ca482614c9c816020890151613aaa565b519451613aaa565b5192813b15610024575f91888389614c3660405198899687958694637921219560e11b8652600486016141ca565b60019150614bb6565b505050565b9190825151908115614f8657614cf5826138c6565b92614d0360405194856138a5565b828452601f19614d12846138c6565b015f5b818110614f63575050614d27836139d7565b94614d31846139d7565b92614d3b856139d7565b905f5b868110614ee057506002546001600160a01b03908116977f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba390911691823b15610024579088949160405195869363edd9444b60e01b85526080600486015260e4850190805191606060848801528251809152602061010488019301905f5b818110614ea357505050806020604092015160a4880152015160c4860152600319858203016024860152602080855192838152019401905f5b818110614e725750505083614e205f969482948894604485015260031984830301606485015261400c565b03925af1801561362b575f5160206156c35f395f51905f5295614ad793614e4c92614e62575b506139d7565b9360405194859460018060a01b031698856144b8565b5f614e6c916138a5565b5f614e46565b825180516001600160a01b03168752602090810151818801528a97506040909601958e945090920191600101614df5565b825180516001600160a01b0316865260209081015181870152979850959691959294509260019190604001950191019089969594928e9492614dbc565b80614eed6001928a613aaa565b51828060a01b03871690526020614f05828751613aaa565b5101516020614f14838c613aaa565b510152818060a01b03614f28828751613aaa565b515116614f35828c613aaa565b526020614f43828751613aaa565b510151614f508289613aaa565b5281614f5c8286613aaa565b5201614d3e565b602090604051614f728161388a565b5f81525f8382015282828901015201614d15565b50505050565b909392919360015460601c5f6040518092630c3d587560e11b8252602060048301528180614fbd6024820189613733565b03915afa90811561362b575f9161537b575b505f5b835181101561529b57614fe58188613aaa565b5115615293576001614ff78284613aaa565b5103615091576150078184613aaa565b516146bb576001600160a01b0361501e8286613aaa565b51169061502b8189613aaa565b5161503881308986615624565b825f52600a60205260405f20548015615063575b6001935f52600a6020520160405f20555b01614fd2565b60055493600160401b85101561385b5761508a816146848760018099016005556005613a95565b935061504c565b600261509d8284613aaa565b510361517b5760016150af8289613aaa565b51036148fe576001600160a01b036150c78286613aaa565b5116906150d48185613aaa565b5191803b1561002457604051632142170760e11b81526001600160a01b0388166004820152306024820152604481018490525f8160648183865af1801561362b5761516b575b5060065490600160401b82101561385b5761468482600161514094016006556006613a95565b600854600160401b81101561385b5760019261488e828561516694016008556008613a95565b61505d565b5f615175916138a5565b5f61511a565b60036151878284613aaa565b5103614a90576001600160a01b0361519f8286613aaa565b5116906151ac8185613aaa565b516151b7828a613aaa565b5190833b1561002457604051637921219560e11b81525f81806151e08686308f600486016141ca565b038183895af1801561362b57615283575b50835f52600b60205260405f20815f5260205260405f2054908115615230575b6001945f52600b60205260405f20905f526020520160405f205561505d565b600754600160401b81101561385b578561468482600161525594016007556007613a95565b60095494600160401b86101561385b5761527c8261488e886001809a016009556009613a95565b9450615211565b5f61528d916138a5565b5f6151f1565b60019061505d565b509192939490600f6152be6152b5600554600654906139b6565b600754906139b6565b1161536c576001546003546001600160a01b03169060601c803b1561002457825f918783879561530460405197889687958694631a1bb38960e21b86526004860161427a565b03925af1801561362b5761535c575b508351615322575b5050505050565b60405130956001600160a01b0316945f5160206156c35f395f51905f52949193849361534f9392856144b8565b0390a35f8080808061531b565b5f615366916138a5565b5f615313565b63df8153c760e01b5f5260045ffd5b61538f91503d805f833e61051a81836138a5565b5f614fcf565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f5114161516615418575b50156153e157565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f6153d9565b604051638c48052360e01b8152600360048201526001600160a01b0391909116906080816024815f865af1801561362b575f905f915f935f9261552b575b501561551c576001600160601b03166001600160601b0319600154161760015560018060a01b036004541660018060a01b0382160361550d575b50600380546001600160a01b031916831790555f80546601000000000000600160d01b031916603083901b6601000000000000600160d01b03161781556001600160a01b0390911691907fa521c704a92cee73ea8410dc251eece5bd5362f6887c45f3e9566e817aee73699080a3565b615516906143f0565b5f61549d565b63a93eca7960e01b5f5260045ffd5b92505050615548915060803d608011612e2b57612e1a81836138a5565b90925f615463565b600954801561557f575f190161557a61556a826009613a95565b8154905f199060031b1b19169055565b600955565b634e487b7160e01b5f52603160045260245ffd5b600854801561557f575f19016155ad61556a826008613a95565b600855565b600754801561557f575f19016155e16155cc826007613a95565b81549060018060a01b039060031b1b19169055565b600755565b600654801561557f575f19016156006155cc826006613a95565b600655565b600554801561557f575f190161561f6155cc826005613a95565b600555565b6040516323b872dd60e01b81526001600160a01b03928316600482015292909116602483015260448201929092526020905f9060649082855af19081601f3d1160015f51141615166156b5575b501561567957565b60405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606490fd5b3b153d171590505f61567156fe71e9b057bc061d94e1e08fc8c03d958f8fe3068ec75e774d478e7e32b9ff4b2aa26469706673582212209a3a8df7f3b31a9d91364ceefdcb0479c37e5a9f459182a5c83d27ad844dfa3764736f6c634300081e0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e590000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae

-----Decoded View---------------
Arg [0] : factory (address): 0xDa14Fdd72345c4d2511357214c5B89A919768e59
Arg [1] : accountsGuard (address): 0x2529AE4a3c9d3285DD06CaDfc8516D3faBD6240b
Arg [2] : merklDistributor (address): 0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae

-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59
Arg [1] : 0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b
Arg [2] : 0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

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.