ETH Price: $4,067.76 (-3.05%)

Contract

0xEafabd9dcC2A8106Ed87a03cD150af0875288380

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

N/A
Transaction Hash
Method
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:
MorphoHelper

Compiler Version
v0.8.21+commit.d9974bed

Optimization Enabled:
No with 200 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

// Morpho Blue interfaces
import {Id, IMorpho, MarketParams, Market, Position} from "../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {IOracle} from "../lib/morpho-blue/src/interfaces/IOracle.sol";
import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol";

// Morpho Blue libraries
import {MathLib} from "../lib/morpho-blue/src/libraries/MathLib.sol";
import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol";
import {MorphoBalancesLib} from "../lib/morpho-blue/src/libraries/periphery/MorphoBalancesLib.sol";
import {MorphoStorageLib} from "../lib/morpho-blue/src/libraries/periphery/MorphoStorageLib.sol";
import {MorphoLib} from "../lib/morpho-blue/src/libraries/periphery/MorphoLib.sol";
import "../lib/morpho-blue/src/libraries/ConstantsLib.sol";

// OpenZeppelin upgradeability
import {Initializable} from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "../lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol";
import {AccessControlUpgradeable} from "../lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol";

// Project interfaces
import {IMetaMorpho, MarketAllocation} from "./interfaces/IMetaMorpho.sol";
import {IPublicAllocator, FlowCapsConfig} from "./interfaces/IPublicAllocator.sol";
import {IMorphoReader, MarketDataExt, PositionExt} from "./interfaces/IMorphoReader.sol";

/**
 * @title MorphoHelper
 * @author Morpho Labs
 * @notice Helper contract facilitating interactions with Morpho Blue and MetaMorpho vaults.
 * @dev Provides restricted functions for operators/admins to manage vaults and public allocator settings,
 *      as well as view functions to read data from Morpho Blue.
 *      Upgradeable using the UUPS pattern.
 */
contract MorphoHelper is Initializable, UUPSUpgradeable, AccessControlUpgradeable {
    using MathLib for uint256;
    using MorphoBalancesLib for IMorpho;
    using MorphoStorageLib for IMorpho;
    using MorphoLib for IMorpho;
    using MarketParamsLib for MarketParams;

    // =========================================================================
    //                           Custom Structs
    // =========================================================================

    /// @notice Represents a withdrawal/deposit instruction for a specific market.
    struct Withdrawal {
        MarketParams market; /// The market parameters.
        int256 amount;       /// The amount of assets to withdraw (positive) or deposit (negative).
    }

    /// @notice Represents a withdrawal/deposit instruction for a market ID.
    struct WithdrawalById {
        Id marketId; /// The market ID.
        int256 amount; /// The amount of assets to withdraw (positive) or deposit (negative).
    }

    // =========================================================================
    //                           Constants & State
    // =========================================================================

    /// @notice Role for operators allowed to perform restricted actions.
    bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
    /// @notice Role for accounts allowed to upgrade the contract.
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

    /// @notice The Morpho Blue core contract instance.
    IMorpho public morpho;
    /// @notice The Public Allocator contract instance.
    IPublicAllocator public public_allocator;


    // =========================================================================
    //                           Constructor & Initializer
    // =========================================================================

    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor() {
        if (block.chainid == 1) { // Ethereum Mainnet
            morpho = IMorpho(0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb);
            public_allocator = IPublicAllocator(0xfd32fA2ca22c76dD6E550706Ad913FC6CE91c75D);
        } else if (block.chainid == 8453) { // Base Mainnet
            morpho = IMorpho(0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb);
            public_allocator = IPublicAllocator(0xA090dD1a701408Df1d4d0B85b716c87565f90467);
        } else if (block.chainid == 130) { // Unichain Mainnet
            morpho = IMorpho(0x8f5ae9CddB9f68de460C77730b018Ae7E04a140A);
            public_allocator = IPublicAllocator(0xB0c9a107fA17c779B3378210A7a593e88938C7C9);
        } else if (block.chainid == 747474) { // Katana Mainnet
            morpho = IMorpho(0xD50F2DffFd62f94Ee4AEd9ca05C61d0753268aBc);
            public_allocator = IPublicAllocator(0x39EB6Da5e88194C82B13491Df2e8B3E213eD2412);
        }
        _disableInitializers();
    }

    /**
     * @notice Initializes the contract, setting up access control and UUPS upgradeability.
     * @param initialAdmin The address to grant initial admin, upgrader, and operator roles.
     */
    function initialize(address initialAdmin) public initializer {
        __AccessControl_init();
        __UUPSUpgradeable_init();

        _grantRole(DEFAULT_ADMIN_ROLE, initialAdmin);
        _grantRole(UPGRADER_ROLE, initialAdmin);
        _grantRole(OPERATOR_ROLE, initialAdmin);
    }

    // =========================================================================
    //                              Modifiers
    // =========================================================================

    /**
     * @notice Restricts execution to OPERATOR_ROLE or DEFAULT_ADMIN_ROLE.
     */
    modifier onlyOperator() {
        address sender = _msgSender();
        require(hasRole(OPERATOR_ROLE, sender) || hasRole(DEFAULT_ADMIN_ROLE, sender), "MorphoHelper: Caller is not an operator");
        _;
    }

    modifier onlyUpgrader() {
        address sender = _msgSender();
        require(hasRole(UPGRADER_ROLE, sender) || hasRole(DEFAULT_ADMIN_ROLE, sender), "MorphoHelper: Caller is not an upgrader");
        _;
    }

    modifier onlyAdmin() {
        address sender = _msgSender();
        require(hasRole(DEFAULT_ADMIN_ROLE, sender), "MorphoHelper: Caller is not an admin");
        _;
    }

    // =========================================================================
    //                        Admin & Configuration
    // =========================================================================

    /**
     * @notice Authorizes an upgrade to a new implementation.
     * @dev Only callable by UPGRADER_ROLE. Part of OpenZeppelin UUPS pattern.
     * @param newImplementation The address of the new implementation contract.
     */
    function _authorizeUpgrade(address newImplementation) internal override onlyUpgrader {}

    /**
     * @notice Updates the Morpho Blue core contract address.
     * @dev Only callable by UPGRADER_ROLE.
     * @param morpho_ The new Morpho Blue contract address.
     */
    function setMorpho(address morpho_) external onlyUpgrader {
        morpho = IMorpho(morpho_);
    }

    /**
     * @notice Updates the Public Allocator contract address.
     * @dev Only callable by UPGRADER_ROLE.
     * @param publicAllocator_ The new Public Allocator contract address.
     */
    function setPublicAllocator(address publicAllocator_) external onlyUpgrader {
        public_allocator = IPublicAllocator(publicAllocator_);
    }

    /**
     * @notice Grants or revokes the OPERATOR_ROLE.
     * @dev Only callable by UPGRADER_ROLE.
     * @param operator The address to grant or revoke the role from.
     * @param grant True to grant the role, false to revoke it.
     */
    function setOperatorRole(address operator, bool grant) external onlyUpgrader {
        if (grant) {
            _grantRole(OPERATOR_ROLE, operator);
        } else {
            _revokeRole(OPERATOR_ROLE, operator);
        }
    }

    // =========================================================================
    //                          Vault Operations
    // =========================================================================

    /**
     * @notice Reallocates assets for a MetaMorpho vault.
     * @dev Wrapper for `IMetaMorpho.reallocate`.
     * @param vault The MetaMorpho vault instance.
     * @param allocations Target assets for each market.
     */
    function reallocate(IMetaMorpho vault, MarketAllocation[] calldata allocations) external onlyOperator {
        vault.reallocate(allocations);
    }

    /**
     * @notice Sets the supply queue for a MetaMorpho vault.
     * @dev Wrapper for `IMetaMorpho.setSupplyQueue`.
     * @param vault The MetaMorpho vault instance.
     * @param newSupplyQueue Market IDs for the new supply queue order.
     */
    function vault_setSupplyQueue(IMetaMorpho vault, Id[] calldata newSupplyQueue) external onlyOperator {
        vault.setSupplyQueue(newSupplyQueue);
    }

    /**
     * @notice Updates the withdraw queue for a MetaMorpho vault.
     * @dev Wrapper for `IMetaMorpho.updateWithdrawQueue`.
     * @param vault The MetaMorpho vault instance.
     * @param indexes Indexes to update in the withdraw queue.
     */
    function vault_updateWithdrawQueue(IMetaMorpho vault, uint256[] calldata indexes) external onlyOperator {
        vault.updateWithdrawQueue(indexes);
    }

    /**
     * @notice Gets the supplied assets of a vault in a specific market.
     * @param vault The MetaMorpho vault instance.
     * @param market The market parameters.
     * @return The amount of assets supplied by the vault.
     */
    function vaultPosition(IMetaMorpho vault, MarketParams memory market) public view returns (uint256) {
        return morpho.expectedSupplyAssets(market, address(vault));
    }

    /**
     * @notice Moves assets between two markets for a vault.
     * @dev Calls `reallocate` setting the destination allocation to `type(uint256).max`.
     * @param vault The MetaMorpho vault instance.
     * @param sourceMarket Market to move assets from.
     * @param destinationMarket Market to move assets to.
     * @param amount Net amount to move. Positive withdraws from source, negative deposits to source.
     */
    function move(
        IMetaMorpho vault,
        MarketParams memory sourceMarket,
        MarketParams memory destinationMarket,
        int256 amount
    ) public onlyOperator {
        MarketAllocation[] memory allocations = new MarketAllocation[](2);
        uint256 sourcePosition = vaultPosition(vault, sourceMarket);

        uint256 newSourcePosition;
        if (amount < 0) { // Deposit to source
            newSourcePosition = sourcePosition + uint256(-amount);
        } else { // Withdraw from source
            newSourcePosition = uint256(amount) <= sourcePosition ? sourcePosition - uint256(amount) : 0;
        }

        allocations[0] = MarketAllocation({marketParams: sourceMarket, assets: newSourcePosition});
        allocations[1] = MarketAllocation({marketParams: destinationMarket, assets: type(uint256).max});

        vault.reallocate(allocations);
    }

    /**
     * @notice Moves assets between two markets (by ID) for a vault.
     * @dev Converts IDs to `MarketParams` and calls the internal `move` logic.
     * @param vault The MetaMorpho vault instance.
     * @param sourceMarketId ID of the market to move assets from.
     * @param destinationMarketId ID of the market to move assets to.
     * @param amount Net amount to move. Positive withdraws from source, negative deposits to source.
     */
    function move(IMetaMorpho vault, Id sourceMarketId, Id destinationMarketId, int256 amount) external onlyOperator {
        move(vault, morpho.idToMarketParams(sourceMarketId), morpho.idToMarketParams(destinationMarketId), amount);
    }

    /**
     * @notice Moves assets from multiple source markets to a single destination market.
     * @dev Aggregates withdrawals/deposits into one `reallocate` call.
     * @param vault The MetaMorpho vault instance.
     * @param withdrawals Amounts to move from each source market.
     * @param destinationMarket Market to move assets to.
     */
    function move(
        IMetaMorpho vault,
        Withdrawal[] calldata withdrawals,
        MarketParams calldata destinationMarket
    ) external onlyOperator {
        _moveInternal(vault, withdrawals, destinationMarket);
    }

    /**
     * @notice Moves assets from multiple source markets (by ID) to a single destination market (by ID).
     * @dev Converts IDs to `MarketParams` and calls the internal move logic.
     * @param vault The MetaMorpho vault instance.
     * @param withdrawals Amounts to move from each source market (by ID).
     * @param destinationMarketId ID of the market to move assets to.
     */
    function move(IMetaMorpho vault, WithdrawalById[] calldata withdrawals, Id destinationMarketId) external onlyOperator {
        Withdrawal[] memory withdrawals_ = new Withdrawal[](withdrawals.length);
        for (uint256 i = 0; i < withdrawals.length; i++) {
            withdrawals_[i] = Withdrawal({market: morpho.idToMarketParams(withdrawals[i].marketId), amount: withdrawals[i].amount});
        }
        _moveInternal(vault, withdrawals_, morpho.idToMarketParams(destinationMarketId));
    }

    /**
     * @notice Internal logic for moving assets from multiple sources to one destination.
     */
    function _moveInternal(IMetaMorpho vault, Withdrawal[] memory withdrawals, MarketParams memory destinationMarket) internal {
        MarketAllocation[] memory allocations = new MarketAllocation[](withdrawals.length + 1);
        for (uint256 i = 0; i < withdrawals.length; i++) {
            uint256 sourcePosition = vaultPosition(vault, withdrawals[i].market);

            uint256 newSourcePosition;
            if (withdrawals[i].amount < 0) { // Deposit to source
                newSourcePosition = sourcePosition + uint256(-withdrawals[i].amount);
            } else { // Withdraw from source
                newSourcePosition = uint256(withdrawals[i].amount) <= sourcePosition
                    ? sourcePosition - uint256(withdrawals[i].amount)
                    : 0;
            }

            allocations[i] = MarketAllocation({marketParams: withdrawals[i].market, assets: newSourcePosition});
        }
        allocations[withdrawals.length] = MarketAllocation({marketParams: destinationMarket, assets: type(uint256).max});

        vault.reallocate(allocations);
    }


    // =========================================================================
    //                      Public Allocator Operations
    // =========================================================================

    /**
     * @notice Sets flow caps for a vault via the Public Allocator.
     * @dev Wrapper for `IPublicAllocator.setFlowCaps`.
     * @param vault Address used as identifier in Public Allocator.
     * @param flowCaps Flow caps definitions for each market.
     */
    function pa_setFlowCaps(IMetaMorpho vault, FlowCapsConfig[] calldata flowCaps) external onlyOperator {
        public_allocator.setFlowCaps(address(vault), flowCaps);
    }

    /**
     * @notice Sets the fee for a vault within the Public Allocator.
     * @dev Wrapper for `IPublicAllocator.setFee`.
     * @param vault Address used as identifier in Public Allocator.
     * @param newFee New fee basis points.
     */
    function pa_setFee(address vault, uint256 newFee) external onlyOperator {
        public_allocator.setFee(vault, newFee);
    }

    /**
     * @notice Transfers accumulated fees for a vault from the Public Allocator.
     * @dev Wrapper for `IPublicAllocator.transferFee`.
     * @param vault Address used as identifier in Public Allocator.
     * @param feeRecipient Address to receive the fees.
     */
    function pa_transferFee(address vault, address payable feeRecipient) external onlyOperator {
        public_allocator.transferFee(vault, feeRecipient);
    }

    /**
     * @notice Sets the admin for a vault within the Public Allocator.
     * @dev Wrapper for `IPublicAllocator.setAdmin`. Requires DEFAULT_ADMIN_ROLE.
     * @param vault Address used as identifier in Public Allocator.
     * @param newAdmin New admin address for the vault in the Public Allocator.
     */
    function pa_setAdmin(address vault, address newAdmin) external onlyUpgrader {
        public_allocator.setAdmin(vault, newAdmin);
    }

    // =========================================================================
    //                           Morpho Reader Views
    // =========================================================================

    /**
     * @notice Converts Morpho Blue market parameters to its unique identifier.
     * @param marketParams The market parameters.
     * @return id The unique identifier for the market.
     */
    function marketParamsToId(MarketParams memory marketParams) public pure returns (Id) {
        return marketParams.id();
    }

    /**
     * @notice Retrieves extended data for a Morpho Blue market.
     * @param id The unique identifier of the market.
     * @return marketData A `MarketDataExt` struct with detailed market information.
     */
    function getMarketData(Id id) public view returns (MarketDataExt memory marketData) {
        Market memory market = morpho.market(id);
        MarketParams memory marketParams = morpho.idToMarketParams(id);

        (marketData.totalSupplyAssets, marketData.totalSupplyShares, marketData.totalBorrowAssets, marketData.totalBorrowShares) = morpho
            .expectedMarketBalances(marketParams);

        marketData.fee = morpho.fee(id);

        // Get the borrow rate
        marketData.borrowRate = 0;
        if (address(marketParams.irm) != address(0)) {
            marketData.borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market).wTaylorCompounded(365 days);
        }

        // Get the supply rate
        marketData.utilization = marketData.totalSupplyAssets == 0 ? 0 : marketData.totalBorrowAssets.wDivUp(marketData.totalSupplyAssets);

        marketData.supplyRate = marketData.borrowRate.wMulDown(1 ether - market.fee).wMulDown(marketData.utilization);
    }

    /**
     * @notice Retrieves extended position data for a user in a Morpho Blue market.
     * @param id The unique identifier of the market.
     * @param user The address of the user.
     * @return position A `PositionExt` struct with detailed position information.
     */
    function getPosition(Id id, address user) public view returns (PositionExt memory position) {
        MarketParams memory marketParams = morpho.idToMarketParams(id);

        Position memory p = morpho.position(id, user);

        uint256 collateralPrice = (address(marketParams.oracle) == address(0)) ? 0 : IOracle(marketParams.oracle).price();

        position.collateral = p.collateral;
        position.collateralValue = position.collateral.mulDivDown(collateralPrice, ORACLE_PRICE_SCALE);

        position.borrowedAssets = morpho.expectedBorrowAssets(marketParams, user);
        position.borrowedShares = p.borrowShares;
        position.suppliedAssets = morpho.expectedSupplyAssets(marketParams, user);
        position.suppliedShares = p.supplyShares;

        position.ltv = (position.collateralValue == 0) ? 0 : position.borrowedAssets.wDivUp(position.collateralValue);

        uint256 maxBorrow = position.collateral.mulDivDown(collateralPrice, ORACLE_PRICE_SCALE).wMulDown(marketParams.lltv);

        position.healthFactor = (position.borrowedAssets == 0) ? type(uint256).max : maxBorrow.wDivDown(position.borrowedAssets);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC1822.sol)

pragma solidity ^0.8.20;

/**
 * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
 * proxy whose upgrades are fully controlled by the current implementation.
 */
interface IERC1822Proxiable {
    /**
     * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
     * address.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy.
     */
    function proxiableUUID() external view returns (bytes32);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Id} from "../../interfaces/IMorpho.sol";

/// @title MorphoStorageLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters to access Morpho storage variables' slot.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
library MorphoStorageLib {
    /* SLOTS */

    uint256 internal constant OWNER_SLOT = 0;
    uint256 internal constant FEE_RECIPIENT_SLOT = 1;
    uint256 internal constant POSITION_SLOT = 2;
    uint256 internal constant MARKET_SLOT = 3;
    uint256 internal constant IS_IRM_ENABLED_SLOT = 4;
    uint256 internal constant IS_LLTV_ENABLED_SLOT = 5;
    uint256 internal constant IS_AUTHORIZED_SLOT = 6;
    uint256 internal constant NONCE_SLOT = 7;
    uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 8;

    /* SLOT OFFSETS */

    uint256 internal constant LOAN_TOKEN_OFFSET = 0;
    uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1;
    uint256 internal constant ORACLE_OFFSET = 2;
    uint256 internal constant IRM_OFFSET = 3;
    uint256 internal constant LLTV_OFFSET = 4;

    uint256 internal constant SUPPLY_SHARES_OFFSET = 0;
    uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1;

    uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0;
    uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1;
    uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2;

    /* GETTERS */

    function ownerSlot() internal pure returns (bytes32) {
        return bytes32(OWNER_SLOT);
    }

    function feeRecipientSlot() internal pure returns (bytes32) {
        return bytes32(FEE_RECIPIENT_SLOT);
    }

    function positionSupplySharesSlot(Id id, address user) internal pure returns (bytes32) {
        return bytes32(
            uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + SUPPLY_SHARES_OFFSET
        );
    }

    function positionBorrowSharesAndCollateralSlot(Id id, address user) internal pure returns (bytes32) {
        return bytes32(
            uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT)))))
                + BORROW_SHARES_AND_COLLATERAL_OFFSET
        );
    }

    function marketTotalSupplyAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET);
    }

    function marketTotalBorrowAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET);
    }

    function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + LAST_UPDATE_AND_FEE_OFFSET);
    }

    function isIrmEnabledSlot(address irm) internal pure returns (bytes32) {
        return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT));
    }

    function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) {
        return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT));
    }

    function isAuthorizedSlot(address authorizer, address authorizee) internal pure returns (bytes32) {
        return keccak256(abi.encode(authorizee, keccak256(abi.encode(authorizer, IS_AUTHORIZED_SLOT))));
    }

    function nonceSlot(address authorizer) internal pure returns (bytes32) {
        return keccak256(abi.encode(authorizer, NONCE_SLOT));
    }

    function idToLoanTokenSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LOAN_TOKEN_OFFSET);
    }

    function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + COLLATERAL_TOKEN_OFFSET);
    }

    function idToOracleSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + ORACLE_OFFSET);
    }

    function idToIrmSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + IRM_OFFSET);
    }

    function idToLltvSlot(Id id) internal pure returns (bytes32) {
        return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LLTV_OFFSET);
    }
}

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

import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol";

struct MarketDataExt {
    uint256 totalSupplyAssets;
    uint256 totalSupplyShares;
    uint256 totalBorrowAssets;
    uint256 totalBorrowShares;
    uint256 fee;
    uint256 utilization;
    uint256 supplyRate;
    uint256 borrowRate;
}

struct PositionExt {
    uint256 suppliedShares;
    uint256 suppliedAssets;
    uint256 borrowedShares;
    uint256 borrowedAssets;
    uint256 collateral;
    uint256 collateralValue;
    uint256 ltv;
    uint256 healthFactor;
}

interface IMorphoReader {
    function getMarketData(Id id) external view returns (MarketDataExt memory);

    function getPosition(
        Id id,
        address user
    ) external view returns (PositionExt memory);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {
    IMetaMorpho,
    IMorpho,
    MarketAllocation,
    Id,
    MarketParams
} from "./IMetaMorpho.sol";

/// @dev Max settable flow cap, such that caps can always be stored on 128 bits.
/// @dev The actual max possible flow cap is type(uint128).max-1.
/// @dev Equals to 170141183460469231731687303715884105727;
uint128 constant MAX_SETTABLE_FLOW_CAP = type(uint128).max / 2;

struct FlowCaps {
    /// @notice The maximum allowed inflow in a market.
    uint128 maxIn;
    /// @notice The maximum allowed outflow in a market.
    uint128 maxOut;
}

struct FlowCapsConfig {
    /// @notice Market for which to change flow caps.
    Id id;
    /// @notice New flow caps for this market.
    FlowCaps caps;
}

struct Withdrawal {
    /// @notice The market from which to withdraw.
    MarketParams marketParams;
    /// @notice The amount to withdraw.
    uint128 amount;
}

/// @dev This interface is used for factorizing IPublicAllocatorStaticTyping and IPublicAllocator.
/// @dev Consider using the IPublicAllocator interface instead of this one.
interface IPublicAllocatorBase {
    /// @notice The Morpho contract.
    function MORPHO() external view returns (IMorpho);

    /// @notice The admin for a given vault.
    function admin(address vault) external view returns (address);

    /// @notice The current ETH fee for a given vault.
    function fee(address vault) external view returns (uint256);

    /// @notice The accrued ETH fee for a given vault.
    function accruedFee(address vault) external view returns (uint256);

    /// @notice Reallocates from a list of markets to one market.
    /// @param vault The MetaMorpho vault to reallocate.
    /// @param withdrawals The markets to withdraw from,and the amounts to withdraw.
    /// @param supplyMarketParams The market receiving total withdrawn to.
    /// @dev Will call MetaMorpho's `reallocate`.
    /// @dev Checks that the flow caps are respected.
    /// @dev Will revert when `withdrawals` contains a duplicate or is not sorted.
    /// @dev Will revert if `withdrawals` contains the supply market.
    /// @dev Will revert if a withdrawal amount is larger than available liquidity.
    function reallocateTo(address vault, Withdrawal[] calldata withdrawals, MarketParams calldata supplyMarketParams)
        external
        payable;

    /// @notice Sets the admin for a given vault.
    function setAdmin(address vault, address newAdmin) external;

    /// @notice Sets the fee for a given vault.
    function setFee(address vault, uint256 newFee) external;

    /// @notice Transfers the current balance to `feeRecipient` for a given vault.
    function transferFee(address vault, address payable feeRecipient) external;

    /// @notice Sets the maximum inflow and outflow through public allocation for some markets for a given vault.
    /// @dev Max allowed inflow/outflow is MAX_SETTABLE_FLOW_CAP.
    /// @dev Doesn't revert if it doesn't change the storage at all.
    function setFlowCaps(address vault, FlowCapsConfig[] calldata config) external;
}

/// @dev This interface is inherited by PublicAllocator so that function signatures are checked by the compiler.
/// @dev Consider using the IPublicAllocator interface instead of this one.
interface IPublicAllocatorStaticTyping is IPublicAllocatorBase {
    /// @notice Returns (maximum inflow, maximum outflow) through public allocation of a given market for a given vault.
    function flowCaps(address vault, Id) external view returns (uint128, uint128);
}

/// @title IPublicAllocator
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for PublicAllocator to have access to all the functions with the appropriate function
/// signatures.
interface IPublicAllocator is IPublicAllocatorBase {
    /// @notice Returns the maximum inflow and maximum outflow through public allocation of a given market for a given
    /// vault.
    function flowCaps(address vault, Id) external view returns (FlowCaps memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol)

pragma solidity ^0.8.20;

/**
 * @dev External interface of AccessControl declared to support ERC165 detection.
 */
interface IAccessControl {
    /**
     * @dev The `account` is missing a role.
     */
    error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);

    /**
     * @dev The caller of a function is not the expected one.
     *
     * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
     */
    error AccessControlBadConfirmation();

    /**
     * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
     *
     * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
     * {RoleAdminChanged} not being emitted signaling this.
     */
    event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);

    /**
     * @dev Emitted when `account` is granted `role`.
     *
     * `sender` is the account that originated the contract call, an admin role
     * bearer except when using {AccessControl-_setupRole}.
     */
    event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Emitted when `account` is revoked `role`.
     *
     * `sender` is the account that originated the contract call:
     *   - if using `revokeRole`, it is the admin role bearer
     *   - if using `renounceRole`, it is the role bearer (i.e. `account`)
     */
    event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) external view returns (bool);

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {AccessControl-_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) external view returns (bytes32);

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function grantRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     */
    function revokeRole(bytes32 role, address account) external;

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been granted `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     */
    function renounceRole(bytes32 role, address callerConfirmation) external;
}

File 8 of 33 : IOracle.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

/// @title IOracle
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface that oracles used by Morpho must implement.
/// @dev It is the user's responsibility to select markets with safe oracles.
interface IOracle {
    /// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36.
    /// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in
    /// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals`
    /// decimals of precision.
    function price() external view returns (uint256);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/ERC1967/ERC1967Utils.sol)

pragma solidity ^0.8.20;

import {IBeacon} from "../beacon/IBeacon.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";

/**
 * @dev This abstract contract provides getters and event emitting update functions for
 * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
 */
library ERC1967Utils {
    // We re-declare ERC-1967 events here because they can't be used directly from IERC1967.
    // This will be fixed in Solidity 0.8.21. At that point we should remove these events.
    /**
     * @dev Emitted when the implementation is upgraded.
     */
    event Upgraded(address indexed implementation);

    /**
     * @dev Emitted when the admin account has changed.
     */
    event AdminChanged(address previousAdmin, address newAdmin);

    /**
     * @dev Emitted when the beacon is changed.
     */
    event BeaconUpgraded(address indexed beacon);

    /**
     * @dev Storage slot with the address of the current implementation.
     * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

    /**
     * @dev The `implementation` of the proxy is invalid.
     */
    error ERC1967InvalidImplementation(address implementation);

    /**
     * @dev The `admin` of the proxy is invalid.
     */
    error ERC1967InvalidAdmin(address admin);

    /**
     * @dev The `beacon` of the proxy is invalid.
     */
    error ERC1967InvalidBeacon(address beacon);

    /**
     * @dev An upgrade function sees `msg.value > 0` that may be lost.
     */
    error ERC1967NonPayable();

    /**
     * @dev Returns the current implementation address.
     */
    function getImplementation() internal view returns (address) {
        return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 implementation slot.
     */
    function _setImplementation(address newImplementation) private {
        if (newImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(newImplementation);
        }
        StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
    }

    /**
     * @dev Performs implementation upgrade with additional setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) internal {
        _setImplementation(newImplementation);
        emit Upgraded(newImplementation);

        if (data.length > 0) {
            Address.functionDelegateCall(newImplementation, data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Storage slot with the admin of the contract.
     * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    /**
     * @dev Returns the current admin.
     *
     * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using
     * the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
     * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
     */
    function getAdmin() internal view returns (address) {
        return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
    }

    /**
     * @dev Stores a new address in the EIP1967 admin slot.
     */
    function _setAdmin(address newAdmin) private {
        if (newAdmin == address(0)) {
            revert ERC1967InvalidAdmin(address(0));
        }
        StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
    }

    /**
     * @dev Changes the admin of the proxy.
     *
     * Emits an {IERC1967-AdminChanged} event.
     */
    function changeAdmin(address newAdmin) internal {
        emit AdminChanged(getAdmin(), newAdmin);
        _setAdmin(newAdmin);
    }

    /**
     * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
     * This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
     */
    // solhint-disable-next-line private-vars-leading-underscore
    bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;

    /**
     * @dev Returns the current beacon.
     */
    function getBeacon() internal view returns (address) {
        return StorageSlot.getAddressSlot(BEACON_SLOT).value;
    }

    /**
     * @dev Stores a new beacon in the EIP1967 beacon slot.
     */
    function _setBeacon(address newBeacon) private {
        if (newBeacon.code.length == 0) {
            revert ERC1967InvalidBeacon(newBeacon);
        }

        StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;

        address beaconImplementation = IBeacon(newBeacon).implementation();
        if (beaconImplementation.code.length == 0) {
            revert ERC1967InvalidImplementation(beaconImplementation);
        }
    }

    /**
     * @dev Change the beacon and trigger a setup call if data is nonempty.
     * This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
     * to avoid stuck value in the contract.
     *
     * Emits an {IERC1967-BeaconUpgraded} event.
     *
     * CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
     * it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
     * efficiency.
     */
    function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
        _setBeacon(newBeacon);
        emit BeaconUpgraded(newBeacon);

        if (data.length > 0) {
            Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
        } else {
            _checkNonPayable();
        }
    }

    /**
     * @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
     * if an upgrade doesn't perform an initialization call.
     */
    function _checkNonPayable() private {
        if (msg.value > 0) {
            revert ERC1967NonPayable();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.20;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     * See {_onlyProxy}.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {MathLib} from "./MathLib.sol";

/// @title SharesMathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Shares management library.
/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
library SharesMathLib {
    using MathLib for uint256;

    /// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
    /// high precision computations.
    /// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
    /// stays low enough not to inflate these assets to a significant value.
    /// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
    uint256 internal constant VIRTUAL_SHARES = 1e6;

    /// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
    /// empty.
    uint256 internal constant VIRTUAL_ASSETS = 1;

    /// @dev Calculates the value of `assets` quoted in shares, rounding down.
    function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
    }

    /// @dev Calculates the value of `shares` quoted in assets, rounding down.
    function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
    }

    /// @dev Calculates the value of `assets` quoted in shares, rounding up.
    function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
    }

    /// @dev Calculates the value of `shares` quoted in assets, rounding up.
    function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
        return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {IMorpho, Id} from "../../interfaces/IMorpho.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";

/// @title MorphoLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library to access Morpho storage variables.
/// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest.
library MorphoLib {
    function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionSupplySharesSlot(id, user));
        return uint256(morpho.extSloads(slot)[0]);
    }

    function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
        return uint128(uint256(morpho.extSloads(slot)[0]));
    }

    function fee(IMorpho morpho, Id id) internal view returns (uint256) {
        bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
        return uint256(morpho.extSloads(slot)[0] >> 128);
    }

    function _array(bytes32 x) private pure returns (bytes32[] memory) {
        bytes32[] memory res = new bytes32[](1);
        res[0] = x;
        return res;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {ErrorsLib} from "../libraries/ErrorsLib.sol";

/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing helpers.
/// @dev Inspired by https://github.com/morpho-org/morpho-utils.
library UtilsLib {
    /// @dev Returns true if there is exactly one zero among `x` and `y`.
    function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
        assembly {
            z := xor(iszero(x), iszero(y))
        }
    }

    /// @dev Returns the min of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns `x` safely cast to uint128.
    function toUint128(uint256 x) internal pure returns (uint128) {
        require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED);
        return uint128(x);
    }

    /// @dev Returns max(0, x - y).
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }
}

File 16 of 33 : ErrorsLib.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing error messages.
library ErrorsLib {
    /// @notice Thrown when the caller is not the owner.
    string internal constant NOT_OWNER = "not owner";

    /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV.
    string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded";

    /// @notice Thrown when the fee to set exceeds the maximum fee.
    string internal constant MAX_FEE_EXCEEDED = "max fee exceeded";

    /// @notice Thrown when the value is already set.
    string internal constant ALREADY_SET = "already set";

    /// @notice Thrown when the IRM is not enabled at market creation.
    string internal constant IRM_NOT_ENABLED = "IRM not enabled";

    /// @notice Thrown when the LLTV is not enabled at market creation.
    string internal constant LLTV_NOT_ENABLED = "LLTV not enabled";

    /// @notice Thrown when the market is already created.
    string internal constant MARKET_ALREADY_CREATED = "market already created";

    /// @notice Thrown when a token to transfer doesn't have code.
    string internal constant NO_CODE = "no code";

    /// @notice Thrown when the market is not created.
    string internal constant MARKET_NOT_CREATED = "market not created";

    /// @notice Thrown when not exactly one of the input amount is zero.
    string internal constant INCONSISTENT_INPUT = "inconsistent input";

    /// @notice Thrown when zero assets is passed as input.
    string internal constant ZERO_ASSETS = "zero assets";

    /// @notice Thrown when a zero address is passed as input.
    string internal constant ZERO_ADDRESS = "zero address";

    /// @notice Thrown when the caller is not authorized to conduct an action.
    string internal constant UNAUTHORIZED = "unauthorized";

    /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`.
    string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral";

    /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`.
    string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity";

    /// @notice Thrown when the position to liquidate is healthy.
    string internal constant HEALTHY_POSITION = "position is healthy";

    /// @notice Thrown when the authorization signature is invalid.
    string internal constant INVALID_SIGNATURE = "invalid signature";

    /// @notice Thrown when the authorization signature is expired.
    string internal constant SIGNATURE_EXPIRED = "signature expired";

    /// @notice Thrown when the nonce is invalid.
    string internal constant INVALID_NONCE = "invalid nonce";

    /// @notice Thrown when a token transfer reverted.
    string internal constant TRANSFER_REVERTED = "transfer reverted";

    /// @notice Thrown when a token transfer returned false.
    string internal constant TRANSFER_RETURNED_FALSE = "transfer returned false";

    /// @notice Thrown when a token transferFrom reverted.
    string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted";

    /// @notice Thrown when a token transferFrom returned false
    string internal constant TRANSFER_FROM_RETURNED_FALSE = "transferFrom returned false";

    /// @notice Thrown when the maximum uint128 is exceeded.
    string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded";
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

uint256 constant WAD = 1e18;

/// @title MathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to manage fixed-point arithmetic.
library MathLib {
    /// @dev Returns (`x` * `y`) / `WAD` rounded down.
    function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD);
    }

    /// @dev Returns (`x` * `WAD`) / `y` rounded down.
    function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y);
    }

    /// @dev Returns (`x` * `WAD`) / `y` rounded up.
    function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y);
    }

    /// @dev Returns (`x` * `y`) / `d` rounded down.
    function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y) / d;
    }

    /// @dev Returns (`x` * `y`) / `d` rounded up.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y + (d - 1)) / d;
    }

    /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
    /// continuous compound interest rate.
    function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
        uint256 firstTerm = x * n;
        uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
        uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);

        return firstTerm + secondTerm + thirdTerm;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

File 20 of 33 : IMetaMorpho.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {IMorpho, Id, MarketParams} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {IERC4626} from "../../lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol";
import {IERC20Permit} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol";

import {MarketConfig, PendingUint192, PendingAddress} from "../libraries/PendingLib.sol";

struct MarketAllocation {
    /// @notice The market to allocate.
    MarketParams marketParams;
    /// @notice The amount of assets to allocate.
    uint256 assets;
}

interface IMulticall {
    function multicall(bytes[] calldata) external returns (bytes[] memory);
}

interface IOwnable {
    function owner() external view returns (address);
    function transferOwnership(address) external;
    function renounceOwnership() external;
    function acceptOwnership() external;
    function pendingOwner() external view returns (address);
}

/// @dev This interface is used for factorizing IMetaMorphoStaticTyping and IMetaMorpho.
/// @dev Consider using the IMetaMorpho interface instead of this one.
interface IMetaMorphoBase {
    /// @notice The address of the Morpho contract.
    function MORPHO() external view returns (IMorpho);
    function DECIMALS_OFFSET() external view returns (uint8);

    /// @notice The address of the curator.
    function curator() external view returns (address);

    /// @notice Stores whether an address is an allocator or not.
    function isAllocator(address target) external view returns (bool);

    /// @notice The current guardian. Can be set even without the timelock set.
    function guardian() external view returns (address);

    /// @notice The current fee.
    function fee() external view returns (uint96);

    /// @notice The fee recipient.
    function feeRecipient() external view returns (address);

    /// @notice The skim recipient.
    function skimRecipient() external view returns (address);

    /// @notice The current timelock.
    function timelock() external view returns (uint256);

    /// @dev Stores the order of markets on which liquidity is supplied upon deposit.
    /// @dev Can contain any market. A market is skipped as soon as its supply cap is reached.
    function supplyQueue(uint256) external view returns (Id);

    /// @notice Returns the length of the supply queue.
    function supplyQueueLength() external view returns (uint256);

    /// @dev Stores the order of markets from which liquidity is withdrawn upon withdrawal.
    /// @dev Always contain all non-zero cap markets as well as all markets on which the vault supplies liquidity,
    /// without duplicate.
    function withdrawQueue(uint256) external view returns (Id);

    /// @notice Returns the length of the withdraw queue.
    function withdrawQueueLength() external view returns (uint256);

    /// @notice Stores the total assets managed by this vault when the fee was last accrued.
    /// @dev May be greater than `totalAssets()` due to removal of markets with non-zero supply or socialized bad debt.
    /// This difference will decrease the fee accrued until one of the functions updating `lastTotalAssets` is
    /// triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient).
    function lastTotalAssets() external view returns (uint256);

    /// @notice Submits a `newTimelock`.
    /// @dev Warning: Reverts if a timelock is already pending. Revoke the pending timelock to overwrite it.
    /// @dev In case the new timelock is higher than the current one, the timelock is set immediately.
    function submitTimelock(uint256 newTimelock) external;

    /// @notice Accepts the pending timelock.
    function acceptTimelock() external;

    /// @notice Revokes the pending timelock.
    /// @dev Does not revert if there is no pending timelock.
    function revokePendingTimelock() external;

    /// @notice Submits a `newSupplyCap` for the market defined by `marketParams`.
    /// @dev Warning: Reverts if a cap is already pending. Revoke the pending cap to overwrite it.
    /// @dev Warning: Reverts if a market removal is pending.
    /// @dev In case the new cap is lower than the current one, the cap is set immediately.
    function submitCap(MarketParams memory marketParams, uint256 newSupplyCap) external;

    /// @notice Accepts the pending cap of the market defined by `marketParams`.
    function acceptCap(MarketParams memory marketParams) external;

    /// @notice Revokes the pending cap of the market defined by `id`.
    /// @dev Does not revert if there is no pending cap.
    function revokePendingCap(Id id) external;

    /// @notice Submits a forced market removal from the vault, eventually losing all funds supplied to the market.
    /// @notice This forced removal is expected to be used as an emergency process in case a market constantly reverts.
    /// To softly remove a sane market, the curator role is expected to bundle a reallocation that empties the market
    /// first (using `reallocate`), followed by the removal of the market (using `updateWithdrawQueue`).
    /// @dev Warning: Removing a market with non-zero supply will instantly impact the vault's price per share.
    /// @dev Warning: Reverts for non-zero cap or if there is a pending cap. Successfully submitting a zero cap will
    /// prevent such reverts.
    function submitMarketRemoval(MarketParams memory marketParams) external;

    /// @notice Revokes the pending removal of the market defined by `id`.
    /// @dev Does not revert if there is no pending market removal.
    function revokePendingMarketRemoval(Id id) external;

    /// @notice Submits a `newGuardian`.
    /// @notice Warning: a malicious guardian could disrupt the vault's operation, and would have the power to revoke
    /// any pending guardian.
    /// @dev In case there is no guardian, the gardian is set immediately.
    /// @dev Warning: Submitting a gardian will overwrite the current pending gardian.
    function submitGuardian(address newGuardian) external;

    /// @notice Accepts the pending guardian.
    function acceptGuardian() external;

    /// @notice Revokes the pending guardian.
    function revokePendingGuardian() external;

    /// @notice Skims the vault `token` balance to `skimRecipient`.
    function skim(address) external;

    /// @notice Sets `newAllocator` as an allocator or not (`newIsAllocator`).
    function setIsAllocator(address newAllocator, bool newIsAllocator) external;

    /// @notice Sets `curator` to `newCurator`.
    function setCurator(address newCurator) external;

    /// @notice Sets the `fee` to `newFee`.
    function setFee(uint256 newFee) external;

    /// @notice Sets `feeRecipient` to `newFeeRecipient`.
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Sets `skimRecipient` to `newSkimRecipient`.
    function setSkimRecipient(address newSkimRecipient) external;

    /// @notice Sets `supplyQueue` to `newSupplyQueue`.
    /// @param newSupplyQueue is an array of enabled markets, and can contain duplicate markets, but it would only
    /// increase the cost of depositing to the vault.
    function setSupplyQueue(Id[] calldata newSupplyQueue) external;

    /// @notice Updates the withdraw queue. Some markets can be removed, but no market can be added.
    /// @notice Removing a market requires the vault to have 0 supply on it, or to have previously submitted a removal
    /// for this market (with the function `submitMarketRemoval`).
    /// @notice Warning: Anyone can supply on behalf of the vault so the call to `updateWithdrawQueue` that expects a
    /// market to be empty can be griefed by a front-run. To circumvent this, the allocator can simply bundle a
    /// reallocation that withdraws max from this market with a call to `updateWithdrawQueue`.
    /// @dev Warning: Removing a market with supply will decrease the fee accrued until one of the functions updating
    /// `lastTotalAssets` is triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient).
    /// @dev Warning: `updateWithdrawQueue` is not idempotent. Submitting twice the same tx will change the queue twice.
    /// @param indexes The indexes of each market in the previous withdraw queue, in the new withdraw queue's order.
    function updateWithdrawQueue(uint256[] calldata indexes) external;

    /// @notice Reallocates the vault's liquidity so as to reach a given allocation of assets on each given market.
    /// @dev The behavior of the reallocation can be altered by state changes, including:
    /// - Deposits on the vault that supplies to markets that are expected to be supplied to during reallocation.
    /// - Withdrawals from the vault that withdraws from markets that are expected to be withdrawn from during
    /// reallocation.
    /// - Donations to the vault on markets that are expected to be supplied to during reallocation.
    /// - Withdrawals from markets that are expected to be withdrawn from during reallocation.
    /// @dev Sender is expected to pass `assets = type(uint256).max` with the last MarketAllocation of `allocations` to
    /// supply all the remaining withdrawn liquidity, which would ensure that `totalWithdrawn` = `totalSupplied`.
    /// @dev A supply in a reallocation step will make the reallocation revert if the amount is greater than the net
    /// amount from previous steps (i.e. total withdrawn minus total supplied).
    function reallocate(MarketAllocation[] calldata allocations) external;
}

/// @dev This interface is inherited by MetaMorpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMetaMorpho interface instead of this one.
interface IMetaMorphoStaticTyping is IMetaMorphoBase {
    /// @notice Returns the current configuration of each market.
    function config(Id) external view returns (uint184 cap, bool enabled, uint64 removableAt);

    /// @notice Returns the pending guardian.
    function pendingGuardian() external view returns (address guardian, uint64 validAt);

    /// @notice Returns the pending cap for each market.
    function pendingCap(Id) external view returns (uint192 value, uint64 validAt);

    /// @notice Returns the pending timelock.
    function pendingTimelock() external view returns (uint192 value, uint64 validAt);
}

/// @title IMetaMorpho
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for MetaMorpho to have access to all the functions with the appropriate function signatures.
interface IMetaMorpho is IMetaMorphoBase, IERC4626, IERC20Permit, IOwnable, IMulticall {
    /// @notice Returns the current configuration of each market.
    function config(Id) external view returns (MarketConfig memory);

    /// @notice Returns the pending guardian.
    function pendingGuardian() external view returns (PendingAddress memory);

    /// @notice Returns the pending cap for each market.
    function pendingCap(Id) external view returns (PendingUint192 memory);

    /// @notice Returns the pending timelock.
    function pendingTimelock() external view returns (PendingUint192 memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) _roles;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;

    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
        assembly {
            $.slot := AccessControlStorageLocation
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        AccessControlStorage storage $ = _getAccessControlStorage();
        bytes32 previousAdminRole = getRoleAdmin(role);
        $._roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (!hasRole(role, account)) {
            $._roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (hasRole(role, account)) {
            $._roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165Upgradeable is Initializable, IERC165 {
    function __ERC165_init() internal onlyInitializing {
    }

    function __ERC165_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is the interface that {BeaconProxy} expects of its beacon.
 */
interface IBeacon {
    /**
     * @dev Must return an address that can be used as a delegate call target.
     *
     * {UpgradeableBeacon} will check that this address is a contract.
     */
    function implementation() external view returns (address);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import {MarketParams, Market} from "./IMorpho.sol";

/// @title IIrm
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);

    /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
    /// storage.
    /// @dev Assumes that `market` corresponds to `marketParams`.
    function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

struct MarketConfig {
    /// @notice The maximum amount of assets that can be allocated to the market.
    uint184 cap;
    /// @notice Whether the market is in the withdraw queue.
    bool enabled;
    /// @notice The timestamp at which the market can be instantly removed from the withdraw queue.
    uint64 removableAt;
}

struct PendingUint192 {
    /// @notice The pending value to set.
    uint192 value;
    /// @notice The timestamp at which the pending value becomes valid.
    uint64 validAt;
}

struct PendingAddress {
    /// @notice The pending value to set.
    address value;
    /// @notice The timestamp at which the pending value becomes valid.
    uint64 validAt;
}

/// @title PendingLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to manage pending values and their validity timestamp.
library PendingLib {
    /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp.
    /// @dev Assumes `timelock` <= `MAX_TIMELOCK`.
    function update(PendingUint192 storage pending, uint184 newValue, uint256 timelock) internal {
        pending.value = newValue;
        // Safe "unchecked" cast because timelock <= MAX_TIMELOCK.
        pending.validAt = uint64(block.timestamp + timelock);
    }

    /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp.
    /// @dev Assumes `timelock` <= `MAX_TIMELOCK`.
    function update(PendingAddress storage pending, address newValue, uint256 timelock) internal {
        pending.value = newValue;
        // Safe "unchecked" cast because timelock <= MAX_TIMELOCK.
        pending.validAt = uint64(block.timestamp + timelock);
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Id, MarketParams, Market, IMorpho} from "../../interfaces/IMorpho.sol";
import {IIrm} from "../../interfaces/IIrm.sol";

import {MathLib} from "../MathLib.sol";
import {UtilsLib} from "../UtilsLib.sol";
import {MorphoLib} from "./MorphoLib.sol";
import {SharesMathLib} from "../SharesMathLib.sol";
import {MarketParamsLib} from "../MarketParamsLib.sol";

/// @title MorphoBalancesLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters with the expected value after interest accrual.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply
/// to it. The value can be queried directly on Morpho using `totalBorrowShares`.
library MorphoBalancesLib {
    using MathLib for uint256;
    using MathLib for uint128;
    using UtilsLib for uint256;
    using MorphoLib for IMorpho;
    using SharesMathLib for uint256;
    using MarketParamsLib for MarketParams;

    /// @notice Returns the expected market balances of a market after having accrued interest.
    /// @return The expected total supply assets.
    /// @return The expected total supply shares.
    /// @return The expected total borrow assets.
    /// @return The expected total borrow shares.
    function expectedMarketBalances(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256, uint256, uint256, uint256)
    {
        Id id = marketParams.id();
        Market memory market = morpho.market(id);

        uint256 elapsed = block.timestamp - market.lastUpdate;

        // Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0).
        if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) {
            uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market);
            uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
            market.totalBorrowAssets += interest.toUint128();
            market.totalSupplyAssets += interest.toUint128();

            if (market.fee != 0) {
                uint256 feeAmount = interest.wMulDown(market.fee);
                // The fee amount is subtracted from the total supply in this calculation to compensate for the fact
                // that total supply is already updated.
                uint256 feeShares =
                    feeAmount.toSharesDown(market.totalSupplyAssets - feeAmount, market.totalSupplyShares);
                market.totalSupplyShares += feeShares.toUint128();
            }
        }

        return (market.totalSupplyAssets, market.totalSupplyShares, market.totalBorrowAssets, market.totalBorrowShares);
    }

    /// @notice Returns the expected total supply assets of a market after having accrued interest.
    function expectedTotalSupplyAssets(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalSupplyAssets)
    {
        (totalSupplyAssets,,,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected total borrow assets of a market after having accrued interest.
    function expectedTotalBorrowAssets(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalBorrowAssets)
    {
        (,, totalBorrowAssets,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected total supply shares of a market after having accrued interest.
    function expectedTotalSupplyShares(IMorpho morpho, MarketParams memory marketParams)
        internal
        view
        returns (uint256 totalSupplyShares)
    {
        (, totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);
    }

    /// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest.
    /// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account.
    /// @dev Warning: Withdrawing using the expected supply assets can lead to a revert due to conversion roundings from
    /// assets to shares.
    function expectedSupplyAssets(IMorpho morpho, MarketParams memory marketParams, address user)
        internal
        view
        returns (uint256)
    {
        Id id = marketParams.id();
        uint256 supplyShares = morpho.supplyShares(id, user);
        (uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);

        return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares);
    }

    /// @notice Returns the expected borrow assets balance of `user` on a market after having accrued interest.
    /// @dev Warning: The expected balance is rounded up, so it may be greater than the market's expected total borrow
    /// assets.
    function expectedBorrowAssets(IMorpho morpho, MarketParams memory marketParams, address user)
        internal
        view
        returns (uint256)
    {
        Id id = marketParams.id();
        uint256 borrowShares = morpho.borrowShares(id, user);
        (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = expectedMarketBalances(morpho, marketParams);

        return borrowShares.toAssetsUp(totalBorrowAssets, totalBorrowShares);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {Id, MarketParams} from "../interfaces/IMorpho.sol";

/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to convert a market to its id.
library MarketParamsLib {
    /// @notice The length of the data used to compute the id of a market.
    /// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
    uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;

    /// @notice Returns the id of the market `marketParams`.
    function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
        assembly ("memory-safe") {
            marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
        }
    }
}

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

type Id is bytes32;

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
}

/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
    uint256 supplyShares;
    uint128 borrowShares;
    uint128 collateral;
}

/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
    uint128 totalSupplyAssets;
    uint128 totalSupplyShares;
    uint128 totalBorrowAssets;
    uint128 totalBorrowShares;
    uint128 lastUpdate;
    uint128 fee;
}

struct Authorization {
    address authorizer;
    address authorized;
    bool isAuthorized;
    uint256 nonce;
    uint256 deadline;
}

struct Signature {
    uint8 v;
    bytes32 r;
    bytes32 s;
}

/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
    /// @notice The EIP-712 domain separator.
    /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing
    /// the same chain id because the domain separator would be the same.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice The owner of the contract.
    /// @dev It has the power to change the owner.
    /// @dev It has the power to set fees on markets and set the fee recipient.
    /// @dev It has the power to enable but not disable IRMs and LLTVs.
    function owner() external view returns (address);

    /// @notice The fee recipient of all markets.
    /// @dev The recipient receives the fees of a given market through a supply position on that market.
    function feeRecipient() external view returns (address);

    /// @notice Whether the `irm` is enabled.
    function isIrmEnabled(address irm) external view returns (bool);

    /// @notice Whether the `lltv` is enabled.
    function isLltvEnabled(uint256 lltv) external view returns (bool);

    /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
    /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
    function isAuthorized(address authorizer, address authorized) external view returns (bool);

    /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
    function nonce(address authorizer) external view returns (uint256);

    /// @notice Sets `newOwner` as `owner` of the contract.
    /// @dev Warning: No two-step transfer ownership.
    /// @dev Warning: The owner can be set to the zero address.
    function setOwner(address newOwner) external;

    /// @notice Enables `irm` as a possible IRM for market creation.
    /// @dev Warning: It is not possible to disable an IRM.
    function enableIrm(address irm) external;

    /// @notice Enables `lltv` as a possible LLTV for market creation.
    /// @dev Warning: It is not possible to disable a LLTV.
    function enableLltv(uint256 lltv) external;

    /// @notice Sets the `newFee` for the given market `marketParams`.
    /// @param newFee The new fee, scaled by WAD.
    /// @dev Warning: The recipient can be the zero address.
    function setFee(MarketParams memory marketParams, uint256 newFee) external;

    /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
    /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
    /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
    /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Creates the market `marketParams`.
    /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
    /// Morpho behaves as expected:
    /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
    /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
    /// burn functions are not supported.
    /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
    /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
    /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
    /// - The IRM should not re-enter Morpho.
    /// - The oracle should return a price with the correct scaling.
    /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties
    /// (funds could get stuck):
    /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue.
    /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and
    /// `toSharesDown` overflow.
    /// - The IRM can revert on `borrowRate`.
    /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest`
    /// overflow.
    /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and
    /// `liquidate` from being used under certain market conditions.
    /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or
    /// the computation of `assetsRepaid` in `liquidate` overflow.
    /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
    /// the point where `totalBorrowShares` is very large and borrowing overflows.
    function createMarket(MarketParams memory marketParams) external;

    /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupply` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
    /// amount of shares is given for full compatibility and precision.
    /// @dev Supplying a large amount can revert for overflow.
    /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to supply assets to.
    /// @param assets The amount of assets to supply.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased supply position.
    /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
    /// @return assetsSupplied The amount of assets supplied.
    /// @return sharesSupplied The amount of shares minted.
    function supply(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);

    /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
    /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
    /// conversion roundings between shares and assets.
    /// @param marketParams The market to withdraw assets from.
    /// @param assets The amount of assets to withdraw.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the supply position.
    /// @param receiver The address that will receive the withdrawn assets.
    /// @return assetsWithdrawn The amount of assets withdrawn.
    /// @return sharesWithdrawn The amount of shares burned.
    function withdraw(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);

    /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
    /// given for full compatibility and precision.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Borrowing a large amount can revert for overflow.
    /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to borrow assets from.
    /// @param assets The amount of assets to borrow.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased borrow position.
    /// @param receiver The address that will receive the borrowed assets.
    /// @return assetsBorrowed The amount of assets borrowed.
    /// @return sharesBorrowed The amount of shares minted.
    function borrow(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

    /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoReplay` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
    /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
    /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
    /// roundings between shares and assets.
    /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
    /// @param marketParams The market to repay assets to.
    /// @param assets The amount of assets to repay.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the debt position.
    /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
    /// @return assetsRepaid The amount of assets repaid.
    /// @return sharesRepaid The amount of shares burned.
    function repay(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

    /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupplyCollateral` function with the given `data`.
    /// @dev Interest are not accrued since it's not required and it saves gas.
    /// @dev Supplying a large amount can revert for overflow.
    /// @param marketParams The market to supply collateral to.
    /// @param assets The amount of collateral to supply.
    /// @param onBehalf The address that will own the increased collateral position.
    /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
    function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
        external;

    /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
    /// @param marketParams The market to withdraw collateral from.
    /// @param assets The amount of collateral to withdraw.
    /// @param onBehalf The address of the owner of the collateral position.
    /// @param receiver The address that will receive the collateral assets.
    function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
        external;

    /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
    /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
    /// `onMorphoLiquidate` function with the given `data`.
    /// @dev Either `seizedAssets` or `repaidShares` should be zero.
    /// @dev Seizing more than the collateral balance will underflow and revert without any error message.
    /// @dev Repaying more than the borrow balance will underflow and revert without any error message.
    /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
    /// @param marketParams The market of the position.
    /// @param borrower The owner of the position.
    /// @param seizedAssets The amount of collateral to seize.
    /// @param repaidShares The amount of shares to repay.
    /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
    /// @return The amount of assets seized.
    /// @return The amount of assets repaid.
    function liquidate(
        MarketParams memory marketParams,
        address borrower,
        uint256 seizedAssets,
        uint256 repaidShares,
        bytes memory data
    ) external returns (uint256, uint256);

    /// @notice Executes a flash loan.
    /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
    /// markets combined, plus donations).
    /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
    /// - `flashFee` is zero.
    /// - `maxFlashLoan` is the token's balance of this contract.
    /// - The receiver of `assets` is the caller.
    /// @param token The token to flash loan.
    /// @param assets The amount of assets to flash loan.
    /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
    function flashLoan(address token, uint256 assets, bytes calldata data) external;

    /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
    /// @param authorized The authorized address.
    /// @param newIsAuthorized The new authorization status.
    function setAuthorization(address authorized, bool newIsAuthorized) external;

    /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
    /// @dev Warning: Reverts if the signature has already been submitted.
    /// @dev The signature is malleable, but it has no impact on the security here.
    /// @dev The nonce is passed as argument to be able to revert with a different error message.
    /// @param authorization The `Authorization` struct.
    /// @param signature The signature.
    function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;

    /// @notice Accrues interest for the given market `marketParams`.
    function accrueInterest(MarketParams memory marketParams) external;

    /// @notice Returns the data stored on the different `slots`.
    function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}

/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user)
        external
        view
        returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
    /// accrual.
    function market(Id id)
        external
        view
        returns (
            uint128 totalSupplyAssets,
            uint128 totalSupplyShares,
            uint128 totalBorrowAssets,
            uint128 totalBorrowShares,
            uint128 lastUpdate,
            uint128 fee
        );

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id)
        external
        view
        returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
}

/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user) external view returns (Position memory p);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
    /// interest accrual.
    function market(Id id) external view returns (Market memory m);

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id) external view returns (MarketParams memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

File 32 of 33 : ConstantsLib.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @dev The maximum fee a market can have (25%).
uint256 constant MAX_FEE = 0.25e18;

/// @dev Oracle price scale.
uint256 constant ORACLE_PRICE_SCALE = 1e36;

/// @dev Liquidation cursor.
uint256 constant LIQUIDATION_CURSOR = 0.3e18;

/// @dev Max liquidation incentive factor.
uint256 constant MAX_LIQUIDATION_INCENTIVE_FACTOR = 1.15e18;

/// @dev The EIP-712 typeHash for EIP712Domain.
bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");

/// @dev The EIP-712 typeHash for Authorization.
bytes32 constant AUTHORIZATION_TYPEHASH =
    keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)");

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        if (address(this).balance < amount) {
            revert AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason or custom error, it is bubbled
     * up by this function (like regular Solidity function calls). However, if
     * the call reverted with no returned reason, this function reverts with a
     * {FailedInnerCall} error.
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

    /**
     * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
     */
    function _revert(bytes memory returndata) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

Settings
{
  "evmVersion": "shanghai",
  "libraries": {},
  "metadata": {
    "appendCBOR": true,
    "bytecodeHash": "ipfs",
    "useLiteralContent": false
  },
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "ds-auth/=lib/dss-psm/lib/dss/lib/ds-token/lib/ds-auth/src/",
    "ds-math/=lib/dss-psm/lib/dss/lib/ds-token/lib/ds-math/src/",
    "ds-note/=lib/dss-psm/lib/dss/lib/ds-value/lib/ds-thing/lib/ds-note/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "ds-thing/=lib/dss-psm/lib/dss/lib/ds-value/lib/ds-thing/src/",
    "ds-token/=lib/dss-psm/lib/dss/lib/ds-token/src/",
    "ds-value/=lib/dss-psm/lib/dss/lib/ds-value/src/",
    "dss-interfaces/=lib/dss-psm/lib/dss-interfaces/src/",
    "dss-psm/=lib/dss-psm/src/",
    "dss/=lib/dss-psm/lib/dss/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "morpho-blue-oracles/=lib/morpho-blue-oracles/src/",
    "@morpho-blue-oracles/=lib/morpho-blue-oracles/",
    "morpho-blue/=lib/morpho-blue/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@uniswap/v3-core/=lib/v3-core/",
    "@slipstream/=lib/slipstream/",
    "@ensdomains/=lib/slipstream/node_modules/@ensdomains/",
    "@nomad-xyz/=lib/slipstream/lib/ExcessivelySafeCall/",
    "@solidity-parser/=lib/slipstream/node_modules/solhint/node_modules/@solidity-parser/",
    "ExcessivelySafeCall/=lib/slipstream/lib/ExcessivelySafeCall/src/",
    "base64-sol/=lib/slipstream/lib/base64/",
    "base64/=lib/slipstream/lib/base64/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
    "hardhat/=lib/slipstream/node_modules/hardhat/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "slipstream/=lib/slipstream/",
    "solidity-lib/=lib/slipstream/lib/solidity-lib/contracts/",
    "v3-core/=lib/v3-core/",
    "v3-periphery/=lib/v3-periphery/contracts/"
  ],
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessControlBadConfirmation","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bytes32","name":"neededRole","type":"bytes32"}],"name":"AccessControlUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"UUPSUnauthorizedCallContext","type":"error"},{"inputs":[{"internalType":"bytes32","name":"slot","type":"bytes32"}],"name":"UUPSUnsupportedProxiableUUID","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OPERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"UPGRADE_INTERFACE_VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"getMarketData","outputs":[{"components":[{"internalType":"uint256","name":"totalSupplyAssets","type":"uint256"},{"internalType":"uint256","name":"totalSupplyShares","type":"uint256"},{"internalType":"uint256","name":"totalBorrowAssets","type":"uint256"},{"internalType":"uint256","name":"totalBorrowShares","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"uint256","name":"utilization","type":"uint256"},{"internalType":"uint256","name":"supplyRate","type":"uint256"},{"internalType":"uint256","name":"borrowRate","type":"uint256"}],"internalType":"struct MarketDataExt","name":"marketData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"},{"internalType":"address","name":"user","type":"address"}],"name":"getPosition","outputs":[{"components":[{"internalType":"uint256","name":"suppliedShares","type":"uint256"},{"internalType":"uint256","name":"suppliedAssets","type":"uint256"},{"internalType":"uint256","name":"borrowedShares","type":"uint256"},{"internalType":"uint256","name":"borrowedAssets","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"collateralValue","type":"uint256"},{"internalType":"uint256","name":"ltv","type":"uint256"},{"internalType":"uint256","name":"healthFactor","type":"uint256"}],"internalType":"struct PositionExt","name":"position","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"initialAdmin","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"marketParams","type":"tuple"}],"name":"marketParamsToId","outputs":[{"internalType":"Id","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"morpho","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"components":[{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"market","type":"tuple"},{"internalType":"int256","name":"amount","type":"int256"}],"internalType":"struct MorphoHelper.Withdrawal[]","name":"withdrawals","type":"tuple[]"},{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"destinationMarket","type":"tuple"}],"name":"move","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"internalType":"Id","name":"sourceMarketId","type":"bytes32"},{"internalType":"Id","name":"destinationMarketId","type":"bytes32"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"move","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"components":[{"internalType":"Id","name":"marketId","type":"bytes32"},{"internalType":"int256","name":"amount","type":"int256"}],"internalType":"struct MorphoHelper.WithdrawalById[]","name":"withdrawals","type":"tuple[]"},{"internalType":"Id","name":"destinationMarketId","type":"bytes32"}],"name":"move","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"sourceMarket","type":"tuple"},{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"destinationMarket","type":"tuple"},{"internalType":"int256","name":"amount","type":"int256"}],"name":"move","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"newAdmin","type":"address"}],"name":"pa_setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"pa_setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"components":[{"internalType":"Id","name":"id","type":"bytes32"},{"components":[{"internalType":"uint128","name":"maxIn","type":"uint128"},{"internalType":"uint128","name":"maxOut","type":"uint128"}],"internalType":"struct FlowCaps","name":"caps","type":"tuple"}],"internalType":"struct FlowCapsConfig[]","name":"flowCaps","type":"tuple[]"}],"name":"pa_setFlowCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address payable","name":"feeRecipient","type":"address"}],"name":"pa_transferFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"proxiableUUID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"public_allocator","outputs":[{"internalType":"contract IPublicAllocator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"components":[{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"marketParams","type":"tuple"},{"internalType":"uint256","name":"assets","type":"uint256"}],"internalType":"struct MarketAllocation[]","name":"allocations","type":"tuple[]"}],"name":"reallocate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"callerConfirmation","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"morpho_","type":"address"}],"name":"setMorpho","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"grant","type":"bool"}],"name":"setOperatorRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"publicAllocator_","type":"address"}],"name":"setPublicAllocator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"market","type":"tuple"}],"name":"vaultPosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"internalType":"Id[]","name":"newSupplyQueue","type":"bytes32[]"}],"name":"vault_setSupplyQueue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IMetaMorpho","name":"vault","type":"address"},{"internalType":"uint256[]","name":"indexes","type":"uint256[]"}],"name":"vault_updateWithdrawQueue","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a060405234620000445762000014620001e3565b6200001e6200004a565b615ea7620006558239608051818181611baf01528181611c1b0152611c5d0152615ea790f35b62000050565b60405190565b5f80fd5b90565b90565b90565b62000076620000706200007c9262000057565b6200005a565b62000054565b90565b90565b6200009b62000095620000a1926200007f565b6200005a565b62000054565b90565b90565b620000c0620000ba620000c692620000a4565b6200005a565b62000054565b90565b90565b620000e5620000df620000eb92620000c9565b6200005a565b62000054565b90565b60018060a01b031690565b620001126200010c6200011892620000ee565b6200005a565b620000ee565b90565b6200012690620000f9565b90565b62000134906200011b565b90565b5f1b90565b906200014f60018060a01b039162000137565b9181191691161790565b62000164906200011b565b90565b90565b90620001846200017e6200018c9262000159565b62000167565b82546200013c565b9055565b6200019b90620000f9565b90565b620001a99062000190565b90565b620001b79062000190565b90565b90565b90620001d7620001d1620001df92620001ac565b620001ba565b82546200013c565b9055565b620001ed620003ea565b4662000205620001fe60016200005d565b9162000054565b145f146200026f57620002376200023073bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb62000129565b5f6200016a565b620002626200025a73fd32fa2ca22c76dd6e550706ad913fc6ce91c75d6200019e565b6001620001bd565b5b6200026d6200055a565b565b46620002886200028161210562000082565b9162000054565b145f14620002ec57620002ba620002b373bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb62000129565b5f6200016a565b620002e5620002dd73a090dd1a701408df1d4d0b85b716c87565f904676200019e565b6001620001bd565b5b62000263565b4662000304620002fd6082620000a7565b9162000054565b145f146200036857620003366200032f738f5ae9cddb9f68de460c77730b018ae7e04a140a62000129565b5f6200016a565b620003616200035973b0c9a107fa17c779b3378210a7a593e88938c7c96200019e565b6001620001bd565b5b620002e6565b46620003826200037b620b67d2620000cc565b9162000054565b146200038f575b62000362565b620003b9620003b273d50f2dfffd62f94ee4aed9ca05c61d0753268abc62000129565b5f6200016a565b620003e4620003dc7339eb6da5e88194c82b13491df2e8b3e213ed24126200019e565b6001620001bd565b62000389565b620003f4620003f6565b565b6200040062000402565b565b6200040c6200040e565b565b620004186200041a565b565b6200042462000442565b565b6200043190620000f9565b90565b6200043f9062000426565b90565b6200044d3062000434565b608052565b60401c90565b60ff1690565b6200046d620004739162000452565b62000458565b90565b6200048290546200045e565b90565b5f0190565b5f1c90565b60018060401b031690565b620004a9620004af916200048a565b6200048f565b90565b620004be90546200049a565b90565b60018060401b031690565b90620004df60018060401b039162000137565b9181191691161790565b62000502620004fc6200050892620004c1565b6200005a565b620004c1565b90565b90565b9062000528620005226200053092620004e9565b6200050b565b8254620004cc565b9055565b6200053f90620004c1565b9052565b919062000558905f6020850194019062000534565b565b6200056462000630565b620005715f820162000476565b6200060957620005835f8201620004b2565b6200059f6200059860018060401b03620004c1565b91620004c1565b03620005a9575b50565b620005be905f60018060401b0391016200050e565b60018060401b03620005ff7fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d291620005f56200004a565b9182918262000543565b0390a15f620005a6565b620006136200004a565b63f92ee8a960e01b8152806200062c6004820162000485565b0390fd5b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009056fe60806040526004361015610013575b6116a1565b61001d5f3561022b565b8062fb0d821461022657806301ffc9a71461022157806312440f5a1461021c5780631da98c9214610217578063248a9ca3146102125780632f2ff15d1461020d57806330c9d4ac1461020857806330f4f4bb14610203578063364f5732146101fe57806336568abe146101f957806344fe5295146101f45780634844da13146101ef5780634f1ef286146101ea57806352d1902d146101e55780635c388821146101e057806367756192146101db578063784980b8146101d6578063919b56f6146101d157806391d14854146101cc57806393512617146101c75780639388e8f3146101c2578063a217fddf146101bd578063ab6a48d9146101b8578063ad3cb1cc146101b3578063afe0e35a146101ae578063b3f57300146101a9578063c4d66de8146101a4578063d547741f1461019f578063d8fbc8331461019a578063db35ce8414610195578063eb7499cf14610190578063f5b541a61461018b5763f72c0d8b0361000e5761166c565b611608565b6115a5565b6114ec565b611432565b611392565b61135f565b61132b565b6112c8565b611244565b6110fc565b61106b565b610ff9565b610f7d565b610ef7565b610ec0565b610df7565b610d58565b610d22565b610c1a565b610bf0565b610aec565b610a30565b610953565b61091f565b61082a565b610701565b610621565b6105bf565b610529565b6104d7565b610470565b6103c7565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b9061026f90610247565b810190811067ffffffffffffffff82111761028957604052565b610251565b906102a161029a610231565b9283610265565b565b60018060a01b031690565b6102b7906102a3565b90565b6102c3816102ae565b036102ca57565b5f80fd5b905035906102db826102ba565b565b90565b6102e9816102dd565b036102f057565b5f80fd5b90503590610301826102e0565b565b919060a0838203126103735761036c9061031d60a061028e565b9361032a825f83016102ce565b5f86015261033b82602083016102ce565b602086015261034d82604083016102ce565b604086015261035f82606083016102ce565b60608601526080016102f4565b6080830152565b610243565b9060a0828203126103915761038e915f01610303565b90565b61023b565b90565b6103a290610396565b90565b6103ae90610399565b9052565b91906103c5905f602085019401906103a5565b565b346103f7576103f36103e26103dd366004610378565b6150ce565b6103ea610231565b918291826103b2565b0390f35b610237565b63ffffffff60e01b1690565b610411816103fc565b0361041857565b5f80fd5b9050359061042982610408565b565b9060208282031261044457610441915f0161041c565b90565b61023b565b151590565b61045790610449565b9052565b919061046e905f6020850194019061044e565b565b346104a05761049c61048b61048636600461042b565b6116a9565b610493610231565b9182918261045b565b0390f35b610237565b91906040838203126104cd57806104c16104ca925f86016102ce565b936020016102ce565b90565b61023b565b5f0190565b34610506576104f06104ea3660046104a5565b906150c2565b6104f8610231565b80610502816104d2565b0390f35b610237565b9060208282031261052457610521915f016102ce565b90565b61023b565b346105575761054161053c36600461050b565b6126ff565b610549610231565b80610553816104d2565b0390f35b610237565b61056581610396565b0361056c57565b5f80fd5b9050359061057d8261055c565b565b9060208282031261059857610595915f01610570565b90565b61023b565b6105a690610396565b9052565b91906105bd905f6020850194019061059d565b565b346105ef576105eb6105da6105d536600461057f565b6117cb565b6105e2610231565b918291826105aa565b0390f35b610237565b919060408382031261061c5780610610610619925f8601610570565b936020016102ce565b90565b61023b565b346106505761063a6106343660046105f4565b9061181f565b610642610231565b8061064c816104d2565b0390f35b610237565b5f91031261065f57565b61023b565b1c90565b60018060a01b031690565b6106839060086106889302610664565b610668565b90565b906106969154610673565b90565b6106a560015f9061068b565b90565b90565b6106bf6106ba6106c4926102a3565b6106a8565b6102a3565b90565b6106d0906106ab565b90565b6106dc906106c7565b90565b6106e8906106d3565b9052565b91906106ff905f602085019401906106df565b565b3461073157610711366004610655565b61072d61071c610699565b610724610231565b918291826106ec565b0390f35b610237565b61073f81610396565b0361074657565b5f80fd5b9050359061075782610736565b565b906020828203126107725761076f915f0161074a565b90565b61023b565b610780906102dd565b9052565b9060e0806108129361079c5f8201515f860190610777565b6107ae60208201516020860190610777565b6107c060408201516040860190610777565b6107d260608201516060860190610777565b6107e460808201516080860190610777565b6107f660a082015160a0860190610777565b61080860c082015160c0860190610777565b0151910190610777565b565b9190610828905f6101008501940190610784565b565b3461085a57610856610845610840366004610759565b6151e5565b61084d610231565b91829182610814565b0390f35b610237565b610868906102ae565b90565b6108748161085f565b0361087b57565b5f80fd5b9050359061088c8261086b565b565b5f80fd5b5f80fd5b5f80fd5b909182601f830112156108d45781359167ffffffffffffffff83116108cf5760200192602083028401116108ca57565b610896565b610892565b61088e565b91909160408184031261091a576108f2835f830161087f565b92602082013567ffffffffffffffff811161091557610911920161089a565b9091565b61023f565b61023b565b3461094e576109386109323660046108d9565b91612e46565b610940610231565b8061094a816104d2565b0390f35b610237565b346109825761096c6109663660046105f4565b906118e7565b610974610231565b8061097e816104d2565b0390f35b610237565b909182601f830112156109c15781359167ffffffffffffffff83116109bc576020019260c083028401116109b757565b610896565b610892565b61088e565b5f80fd5b908160a09103126109d85790565b6109c6565b9060e082820312610a2b576109f4815f840161087f565b92602083013567ffffffffffffffff8111610a2657610a1883610a23928601610987565b9390946040016109ca565b90565b61023f565b61023b565b34610a6257610a4c610a433660046109dd565b92919091614548565b610a54610231565b80610a5e816104d2565b0390f35b610237565b909182601f83011215610aa15781359167ffffffffffffffff8311610a9c576020019260608302840111610a9757565b610896565b610892565b61088e565b919091604081840312610ae757610abf835f830161087f565b92602082013567ffffffffffffffff8111610ae257610ade9201610a67565b9091565b61023f565b61023b565b34610b1b57610b05610aff366004610aa6565b91614d84565b610b0d610231565b80610b17816104d2565b0390f35b610237565b5f80fd5b67ffffffffffffffff8111610b4257610b3e602091610247565b0190565b610251565b90825f939282370152565b90929192610b67610b6282610b24565b61028e565b93818552602085019082840111610b8357610b8192610b47565b565b610b20565b9080601f83011215610ba657816020610ba393359101610b52565b90565b61088e565b919091604081840312610beb57610bc4835f83016102ce565b92602082013567ffffffffffffffff8111610be657610be39201610b88565b90565b61023f565b61023b565b610c04610bfe366004610bab565b90611b86565b610c0c610231565b80610c16816104d2565b0390f35b34610c4a57610c2a366004610655565b610c46610c35611b4a565b610c3d610231565b918291826105aa565b0390f35b610237565b9190604083820312610c775780610c6b610c74925f860161074a565b936020016102ce565b90565b61023b565b9060e080610d0a93610c945f8201515f860190610777565b610ca660208201516020860190610777565b610cb860408201516040860190610777565b610cca60608201516060860190610777565b610cdc60808201516080860190610777565b610cee60a082015160a0860190610777565b610d0060c082015160c0860190610777565b0151910190610777565b565b9190610d20905f6101008501940190610c7c565b565b34610d5357610d4f610d3e610d38366004610c4f565b906158ab565b610d46610231565b91829182610d0c565b0390f35b610237565b34610d8657610d70610d6b36600461050b565b6127b4565b610d78610231565b80610d82816104d2565b0390f35b610237565b90565b610d9781610d8b565b03610d9e57565b5f80fd5b90503590610daf82610d8e565b565b608081830312610df257610dc7825f830161087f565b92610def610dd8846020850161074a565b93610de6816040860161074a565b93606001610da2565b90565b61023b565b34610e2957610e13610e0a366004610db1565b929190916143f7565b610e1b610231565b80610e25816104d2565b0390f35b610237565b909182601f83011215610e685781359167ffffffffffffffff8311610e63576020019260408302840111610e5e57565b610896565b610892565b61088e565b90606082820312610ebb57610e84815f840161087f565b92602083013567ffffffffffffffff8111610eb657610ea883610eb3928601610e2e565b93909460400161074a565b90565b61023f565b61023b565b34610ef257610edc610ed3366004610e6d565b929190916148af565b610ee4610231565b80610eee816104d2565b0390f35b610237565b34610f2857610f24610f13610f0d3660046105f4565b9061174d565b610f1b610231565b9182918261045b565b0390f35b610237565b610f3681610449565b03610f3d57565b5f80fd5b90503590610f4e82610f2d565b565b9190604083820312610f785780610f6c610f75925f86016102ce565b93602001610f41565b90565b61023b565b34610fac57610f96610f90366004610f50565b90612841565b610f9e610231565b80610fa8816104d2565b0390f35b610237565b61018081830312610ff457610fc8825f830161087f565b92610ff1610fd98460208501610303565b93610fe78160c08601610303565b9361016001610da2565b90565b61023b565b3461102b5761101561100c366004610fb1565b929190916141d9565b61101d610231565b80611027816104d2565b0390f35b610237565b90565b5f1b90565b61104c61104761105192611030565b611033565b610396565b90565b61105d5f611038565b90565b611068611054565b90565b3461109b5761107b366004610655565b611097611086611060565b61108e610231565b918291826105aa565b0390f35b610237565b6110a9906102a3565b90565b6110b5816110a0565b036110bc57565b5f80fd5b905035906110cd826110ac565b565b91906040838203126110f757806110eb6110f4925f86016102ce565b936020016110c0565b90565b61023b565b3461112b5761111561110f3660046110cf565b90614fb2565b61111d610231565b80611127816104d2565b0390f35b610237565b67ffffffffffffffff811161114e5761114a602091610247565b0190565b610251565b9061116561116083611130565b61028e565b918252565b5f7f352e302e30000000000000000000000000000000000000000000000000000000910152565b61119b6005611153565b906111a86020830161116a565b565b6111b2611191565b90565b6111bd6111aa565b90565b6111c86111b5565b90565b5190565b60209181520190565b5f5b8381106111ea575050905f910152565b8060209183015181850152016111da565b61121a61122360209361122893611211816111cb565b938480936111cf565b958691016111d8565b610247565b0190565b6112419160208201915f8184039101526111fb565b90565b3461127457611254366004610655565b61127061125f6111c0565b611267610231565b9182918261122c565b0390f35b610237565b919060c0838203126112a1578061129561129e925f860161087f565b93602001610303565b90565b61023b565b6112af906102dd565b9052565b91906112c6905f602085019401906112a6565b565b346112f9576112f56112e46112de366004611279565b90612e78565b6112ec610231565b918291826112b3565b0390f35b610237565b9190604083820312611326578061131a611323925f86016102ce565b936020016102f4565b90565b61023b565b3461135a5761134461133e3660046112fe565b90614e95565b61134c610231565b80611356816104d2565b0390f35b610237565b3461138d5761137761137236600461050b565b6124ae565b61137f610231565b80611389816104d2565b0390f35b610237565b346113c1576113ab6113a53660046105f4565b906118db565b6113b3610231565b806113bd816104d2565b0390f35b610237565b60018060a01b031690565b6113e19060086113e69302610664565b6113c6565b90565b906113f491546113d1565b90565b6114015f806113e9565b90565b61140d906106c7565b90565b61141990611404565b9052565b9190611430905f60208501940190611410565b565b3461146257611442366004610655565b61145e61144d6113f7565b611455610231565b9182918261141d565b0390f35b610237565b909182601f830112156114a15781359167ffffffffffffffff831161149c57602001926020830284011161149757565b610896565b610892565b61088e565b9190916040818403126114e7576114bf835f830161087f565b92602082013567ffffffffffffffff81116114e2576114de9201611467565b9091565b61023f565b61023b565b3461151b576115056114ff3660046114a6565b91612d01565b61150d610231565b80611517816104d2565b0390f35b610237565b909182601f8301121561155a5781359167ffffffffffffffff8311611555576020019260c0830284011161155057565b610896565b610892565b61088e565b9190916040818403126115a057611578835f830161087f565b92602082013567ffffffffffffffff811161159b576115979201611520565b9091565b61023f565b61023b565b346115d4576115be6115b836600461155f565b91612b6b565b6115c6610231565b806115d0816104d2565b0390f35b610237565b7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92990565b6116056115d9565b90565b3461163857611618366004610655565b6116346116236115fd565b61162b610231565b918291826105aa565b0390f35b610237565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e390565b61166961163d565b90565b3461169c5761167c366004610655565b611698611687611661565b61168f610231565b918291826105aa565b0390f35b610237565b5f80fd5b5f90565b6116b16116a5565b50806116cc6116c6637965db0b60e01b6103fc565b916103fc565b149081156116d9575b5090565b6116e39150611e4e565b5f6116d5565b906116f390610399565b5f5260205260405f2090565b611708906106c7565b90565b90611715906116ff565b5f5260205260405f2090565b5f1c90565b60ff1690565b61173861173d91611721565b611726565b90565b61174a905461172c565b90565b61177c915f611771611777936117616116a5565b508261176b61177f565b016116e9565b0161170b565b611740565b90565b7f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680090565b5f90565b90565b6117b66117bb91611721565b6117a7565b90565b6117c890546117aa565b90565b60016117ec6117f2926117dc6117a3565b505f6117e661177f565b016116e9565b016117be565b90565b906118109161180b611806826117cb565b61182b565b611812565b565b9061181c9161197b565b50565b90611829916117f5565b565b61183d90611837611e41565b9061186f565b565b611848906102ae565b9052565b91602061186d92949361186660408201965f83019061183f565b019061059d565b565b9061188461187e83839061174d565b15610449565b61188c575050565b6118ad611897610231565b92839263e2517d3f60e01b84526004840161184c565b0390fd5b906118cc916118c76118c2826117cb565b61182b565b6118ce565b565b906118d891611a32565b50565b906118e5916118b1565b565b90806119026118fc6118f7611e41565b6102ae565b916102ae565b036119135761191091611a32565b50565b61191b610231565b63334bd91960e11b815280611932600482016104d2565b0390fd5b9061194260ff91611033565b9181191691161790565b61195590610449565b90565b90565b9061197061196b6119779261194c565b611958565b8254611936565b9055565b6119836116a5565b5061198c61177f565b6119a061199a83859061174d565b15610449565b5f14611a2b576119ca906119c55f6119bd816001940186906116e9565b01859061170b565b61195b565b906119d3611e41565b90611a10611a0a611a047f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d95610399565b926116ff565b926116ff565b92611a19610231565b80611a23816104d2565b0390a4600190565b5050505f90565b611a3a6116a5565b50611a4361177f565b611a4e82849061174d565b5f14611ad857611a7790611a725f611a6a8180940186906116e9565b01859061170b565b61195b565b90611a80611e41565b90611abd611ab7611ab17ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b95610399565b926116ff565b926116ff565b92611ac6610231565b80611ad0816104d2565b0390a4600190565b5050505f90565b611af090611aeb611c4c565b611b3e565b90565b90565b611b0a611b05611b0f92611af3565b611033565b610396565b90565b611b3b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc611af6565b90565b50611b47611b12565b90565b611b5a611b556117a3565b611adf565b90565b90611b6f91611b6a611b9e565b611b71565b565b90611b8491611b7f8161264b565b611d31565b565b90611b9091611b5d565b565b611b9b906106c7565b90565b611ba730611b92565b611bd9611bd37f00000000000000000000000000000000000000000000000000000000000000006102ae565b916102ae565b148015611c0a575b611be757565b611bef610231565b63703e46dd60e11b815280611c06600482016104d2565b0390fd5b50611c13611ea0565b611c45611c3f7f00000000000000000000000000000000000000000000000000000000000000006102ae565b916102ae565b1415611be1565b611c5530611b92565b611c87611c817f00000000000000000000000000000000000000000000000000000000000000006102ae565b916102ae565b03611c8e57565b611c96610231565b63703e46dd60e11b815280611cad600482016104d2565b0390fd5b611cba906106ab565b90565b611cc690611cb1565b90565b611cd2906106c7565b90565b5f80fd5b60e01b90565b90505190611cec8261055c565b565b90602082820312611d0757611d04915f01611cdf565b90565b61023b565b611d14610231565b3d5f823e3d90fd5b9190611d2f905f6020850194019061183f565b565b9190611d5f6020611d49611d4486611cbd565b611cc9565b6352d1902d90611d57610231565b938492611cd9565b82528180611d6f600482016104d2565b03915afa80915f92611e0d575b50155f14611db7575050906001611d9157505b565b611db390611d9d610231565b918291634c9c8ce360e01b835260048301611d1c565b0390fd5b9283611dd2611dcc611dc7611b12565b610396565b91610396565b03611de757611de2929350611ee6565b611d8f565b611e0984611df3610231565b918291632a87526960e21b8352600483016105aa565b0390fd5b611e2f91925060203d8111611e36575b611e278183610265565b810190611cee565b905f611d7c565b503d611e1d565b5f90565b611e49611e3d565b503390565b611e566116a5565b50611e70611e6a6301ffc9a760e01b6103fc565b916103fc565b1490565b60018060a01b031690565b611e8b611e9091611721565b611e74565b90565b611e9d9054611e7f565b90565b611ea8611e3d565b50611ec35f611ebd611eb8611b12565b612192565b01611e93565b90565b5190565b611ede611ed9611ee392611030565b6106a8565b6102dd565b90565b90611ef082611fad565b81611f1b7fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b916116ff565b90611f24610231565b80611f2e816104d2565b0390a2611f3a81611ec6565b611f4c611f465f611eca565b916102dd565b115f14611f6057611f5c9161208b565b505b565b5050611f6a612009565b611f5e565b90611f8060018060a01b0391611033565b9181191691161790565b90565b90611fa2611f9d611fa9926116ff565b611f8a565b8254611f6f565b9055565b803b611fc1611fbb5f611eca565b916102dd565b14611fe357611fe1905f611fdb611fd6611b12565b612192565b01611f8d565b565b61200590611fef610231565b918291634c9c8ce360e01b835260048301611d1c565b0390fd5b3461201c6120165f611eca565b916102dd565b1161202357565b61202b610231565b63b398979f60e01b815280612042600482016104d2565b0390fd5b606090565b9061205d61205883610b24565b61028e565b918252565b3d5f1461207d576120723d61204b565b903d5f602084013e5b565b612085612046565b9061207b565b5f806120b793612099612046565b508390602081019051915af4906120ae612062565b909190916120ba565b90565b906120ce906120c7612046565b5015610449565b5f146120da5750612145565b6120e382611ec6565b6120f56120ef5f611eca565b916102dd565b148061212a575b612104575090565b61212690612110610231565b918291639996b31560e01b835260048301611d1c565b0390fd5b50803b61213f6121395f611eca565b916102dd565b146120fc565b61214e81611ec6565b61216061215a5f611eca565b916102dd565b115f1461216f57805190602001fd5b612177610231565b630a12f52160e11b81528061218e600482016104d2565b0390fd5b90565b60401c90565b6121a76121ac91612195565b611726565b90565b6121b9905461219b565b90565b67ffffffffffffffff1690565b6121d56121da91611721565b6121bc565b90565b6121e790546121c9565b90565b67ffffffffffffffff1690565b61220b61220661221092611030565b6106a8565b6121ea565b90565b90565b61222a61222561222f92612213565b6106a8565b6121ea565b90565b61223b906106c7565b90565b9061225167ffffffffffffffff91611033565b9181191691161790565b61226f61226a612274926121ea565b6106a8565b6121ea565b90565b90565b9061228f61228a6122969261225b565b612277565b825461223e565b9055565b60401b90565b906122b468ff00000000000000009161229a565b9181191691161790565b906122d36122ce6122da9261194c565b611958565b82546122a0565b9055565b6122e790612216565b9052565b91906122fe905f602085019401906122de565b565b612308612525565b9061231d6123175f84016121af565b15610449565b906123295f84016121dd565b8061233c6123365f6121f7565b916121ea565b148061245d575b906123576123516001612216565b916121ea565b1480612435575b612369909115610449565b9081612424575b50612401576123999061238e6123866001612216565b5f860161227a565b826123ef575b612464565b6123a1575b50565b6123ae905f8091016122be565b60016123e67fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2916123dd610231565b918291826122eb565b0390a15f61239e565b6123fc60015f86016122be565b612394565b612409610231565b63f92ee8a960e01b815280612420600482016104d2565b0390fd5b61242f915015610449565b5f612370565b5061236961244230612232565b3b61245561244f5f611eca565b916102dd565b14905061235e565b5082612343565b6124ab906124706124c3565b612478612553565b61248a612483611054565b829061197b565b5061249d61249661163d565b829061197b565b506124a66115d9565b61197b565b50565b6124b790612300565b565b6124c16124cd565b565b6124cb6124b9565b565b6124de6124d8612507565b15610449565b6124e457565b6124ec610231565b631afcd79f60e31b815280612503600482016104d2565b0390fd5b61250f6116a5565b506125225f61251c612525565b016121af565b90565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b6125516124cd565b565b61255b612549565b565b60207f7067726164657200000000000000000000000000000000000000000000000000917f4d6f7270686f48656c7065723a2043616c6c6572206973206e6f7420616e20755f8201520152565b6125b760276040926111cf565b6125c08161255d565b0190565b6125d99060208101905f8183039101526125aa565b90565b156125e357565b6125eb610231565b62461bcd60e51b815280612601600482016125c4565b0390fd5b50612631612611611e41565b61262361261c61163d565b829061174d565b908115612633575b506125dc565b565b6126459150612640611054565b61174d565b5f61262b565b61265490612605565b565b61268a90612685612665611e41565b61267761267061163d565b829061174d565b90811561268c575b506125dc565b6126eb565b565b61269e9150612699611054565b61174d565b5f61267f565b6126ad906106ab565b90565b6126b9906126a4565b90565b6126c5906126a4565b90565b90565b906126e06126db6126e7926126bc565b6126c8565b8254611f6f565b9055565b6126f76126fd916126b0565b5f6126cb565b565b61270890612656565b565b61273e90612739612719611e41565b61272b61272461163d565b829061174d565b908115612740575b506125dc565b61279f565b565b612752915061274d611054565b61174d565b5f612733565b612761906106ab565b90565b61276d90612758565b90565b61277990612758565b90565b90565b9061279461278f61279b92612770565b61277c565b8254611f6f565b9055565b6127ab6127b291612764565b600161277f565b565b6127bd9061270a565b565b906127f4916127ef6127cf611e41565b6127e16127da61163d565b829061174d565b9081156127f6575b506125dc565b61280e565b565b6128089150612803611054565b61174d565b5f6127e9565b905f1461282a57612826906128216115d9565b61197b565b505b565b61283b906128366115d9565b611a32565b50612828565b9061284b916127bf565b565b60207f70657261746f7200000000000000000000000000000000000000000000000000917f4d6f7270686f48656c7065723a2043616c6c6572206973206e6f7420616e206f5f8201520152565b6128a760276040926111cf565b6128b08161284d565b0190565b6128c99060208101905f81830391015261289a565b90565b156128d357565b6128db610231565b62461bcd60e51b8152806128f1600482016128b4565b0390fd5b9061292b9291612926612906611e41565b6129186129116115d9565b829061174d565b90811561292d575b506128cc565b612ae2565b565b61293f915061293a611054565b61174d565b5f612920565b61294e906106c7565b90565b5f91031261295b57565b61023b565b60209181520190565b90565b5090565b5061297f9060208101906102ce565b90565b61298b906102ae565b9052565b5061299e9060208101906102f4565b90565b906080612a1a612a22936129c36129ba5f830183612970565b5f860190612982565b6129dd6129d36020830183612970565b6020860190612982565b6129f76129ed6040830183612970565b6040860190612982565b612a11612a076060830183612970565b6060860190612982565b8281019061298f565b910190610777565b565b9060a0612a4f612a5793612a46612a3d5f83018361296c565b5f8601906129a1565b8281019061298f565b910190610777565b565b90612a668160c093612a24565b0190565b5090565b60c00190565b91612a8282612a8892612960565b92612969565b90815f905b828210612a9b575050505090565b90919293612abd612ab7600192612ab28886612a6a565b612a59565b95612a6e565b920190929192612a8d565b9091612adf9260208301925f818503910152612a74565b90565b612aeb90612945565b91637299aa31919092803b15612b6657612b185f8094612b23612b0c610231565b97889687958694611cd9565b845260048401612ac8565b03925af18015612b6157612b35575b50565b612b54905f3d8111612b5a575b612b4c8183610265565b810190612951565b5f612b32565b503d612b42565b611d0c565b611cd5565b90612b7692916128f5565b565b90612bae9291612ba9612b89611e41565b612b9b612b946115d9565b829061174d565b908115612bb0575b506128cc565b612c78565b565b612bc29150612bbd611054565b61174d565b5f612ba3565b60209181520190565b90565b612bdd90610399565b9052565b90612bee81602093612bd4565b0190565b50612c0190602081019061074a565b90565b60200190565b91612c1882612c1e92612bc8565b92612bd1565b90815f905b828210612c31575050505090565b90919293612c53612c4d600192612c488886612bf2565b612be1565b95612c04565b920190929192612c23565b9091612c759260208301925f818503910152612c0a565b90565b612c8190612945565b91632acc56f9919092803b15612cfc57612cae5f8094612cb9612ca2610231565b97889687958694611cd9565b845260048401612c5e565b03925af18015612cf757612ccb575b50565b612cea905f3d8111612cf0575b612ce28183610265565b810190612951565b5f612cc8565b503d612cd8565b611d0c565b611cd5565b90612d0c9291612b78565b565b90612d449291612d3f612d1f611e41565b612d31612d2a6115d9565b829061174d565b908115612d46575b506128cc565b612dbd565b565b612d589150612d53611054565b61174d565b5f612d39565b60209181520190565b5f80fd5b9037565b909182612d7b91612d5e565b9160018060fb1b038111612d9e5782916020612d9a9202938491612d6b565b0190565b612d67565b9091612dba9260208301925f818503910152612d6f565b90565b612dc690612945565b916341b67833919092803b15612e4157612df35f8094612dfe612de7610231565b97889687958694611cd9565b845260048401612da3565b03925af18015612e3c57612e10575b50565b612e2f905f3d8111612e35575b612e278183610265565b810190612951565b5f612e0d565b503d612e1d565b611d0c565b611cd5565b90612e519291612d0e565b565b5f90565b612e63612e6891611721565b6113c6565b90565b612e759054612e57565b90565b90612e9f91612e85612e53565b50612e99612e925f612e6b565b9291612945565b91612ea2565b90565b90612ed090612eca612ede94612eb6612e53565b50612ec083612ee5565b9085919091613aa2565b9261334e565b505091909190919091612f85565b90565b5f90565b60a090612ef0612ee1565b502090565b612f09612f04612f0e92612213565b6106a8565b6102dd565b90565b612f1b6001612ef5565b90565b634e487b7160e01b5f52601160045260245ffd5b612f41612f47919392936102dd565b926102dd565b8201809211612f5257565b612f1e565b90565b612f6e612f69612f7392612f57565b6106a8565b6102dd565b90565b612f82620f4240612f5a565b90565b91612fb7612fa8612fbd94612f98612e53565b5093612fa2612f11565b90612f32565b91612fb1612f76565b90612f32565b9161302b565b90565b612fcf612fd5919392936102dd565b926102dd565b91612fe18382026102dd565b928184041490151715612ff057565b612f1e565b634e487b7160e01b5f52601260045260245ffd5b61301561301b916102dd565b916102dd565b908115613026570490565b612ff5565b61304792916130429161303c612e53565b50612fc0565b613009565b90565b6fffffffffffffffffffffffffffffffff1690565b6130688161304a565b0361306f57565b5f80fd5b905051906130808261305f565b565b919060c083820312613104576130fd9061309c60c061028e565b936130a9825f8301613073565b5f8601526130ba8260208301613073565b60208601526130cc8260408301613073565b60408601526130de8260608301613073565b60608601526130f08260808301613073565b608086015260a001613073565b60a0830152565b610243565b9060c0828203126131225761311f915f01613082565b90565b61023b565b613131905161304a565b90565b61314861314361314d9261304a565b6106a8565b6102dd565b90565b61315f613165919392936102dd565b926102dd565b820391821161317057565b612f1e565b61318961318461318e92611030565b6106a8565b61304a565b90565b61319b90516102ae565b90565b6131b26131ad6131b792611030565b6106a8565b6102a3565b90565b6131c39061319e565b90565b6131cf906106ab565b90565b6131db906131c6565b90565b6131e7906106c7565b90565b905051906131f7826102e0565b565b906020828203126132125761320f915f016131ea565b90565b61023b565b9060808061326f9361322f5f8201515f860190612982565b61324160208201516020860190612982565b61325360408201516040860190612982565b61326560608201516060860190612982565b0151910190610777565b565b61327a9061304a565b9052565b9060a0806132e8936132965f8201515f860190613271565b6132a860208201516020860190613271565b6132ba60408201516040860190613271565b6132cc60608201516060860190613271565b6132de60808201516080860190613271565b0151910190613271565b565b9160a061330c9294936133056101608201965f830190613217565b019061327e565b565b61331a6133209161304a565b9161304a565b01906fffffffffffffffffffffffffffffffff821161333b57565b612f1e565b9061334a9061304a565b9052565b60c06133b19161335c612e53565b50613365612e53565b5061336e612e53565b50613377612e53565b5061338a61338485612ee5565b91611404565b6133a6635c60e39a61339a610231565b95869485938493611cd9565b8352600483016103b2565b03915afa90811561369c575f9161366e575b50906133e3426133dd6133d860808601613127565b613134565b90613150565b806133f66133f05f611eca565b916102dd565b141580613648575b8061361a575b613462575b50506134165f8201613127565b61342260208301613127565b9261345f61345961345361344d613447606061344060408a01613127565b9801613127565b95613134565b96613134565b94613134565b92613134565b90565b602061348061347b61347660608601613191565b6131d2565b6131de565b638c00bf6b93906134a386956134ae613497610231565b97889586948594611cd9565b8452600484016132ea565b03915afa918215613615576134ea926134e4915f916135e7575b50916134de6134d960408701613127565b613134565b92613732565b906136d4565b6135126134f682613884565b61350c604085019161350783613127565b61330e565b90613340565b61353961351e82613884565b6135335f85019161352e83613127565b61330e565b90613340565b61354560a08301613127565b6135576135515f613175565b9161304a565b03613563575b80613409565b6135c56135c061358a6135e19361358461357f60a08801613127565b613134565b906136d4565b6135a6816135a161359c5f8901613127565b613134565b613150565b6135ba6135b560208801613127565b613134565b916137a5565b613884565b6135db60208401916135d683613127565b61330e565b90613340565b5f61355d565b613608915060203d811161360e575b6136008183610265565b8101906131f9565b5f6134c8565b503d6135f6565b611d0c565b5061362760608301613191565b61364161363b6136365f6131ba565b6102ae565b916102ae565b1415613404565b5061365560408401613127565b6136676136615f613175565b9161304a565b14156133fe565b61368f915060c03d8111613695575b6136878183610265565b810190613109565b5f6133c3565b503d61367d565b611d0c565b90565b6136b86136b36136bd926136a1565b6106a8565b6102dd565b90565b6136d1670de0b6b3a76400006136a4565b90565b906136f1916136e1612e53565b50906136eb6136c0565b9161302b565b90565b90565b61370b613706613710926136f4565b6106a8565b6102dd565b90565b90565b61372a61372561372f92613713565b6106a8565b6102dd565b90565b6137a29161374891613742612e53565b50612fc0565b61379d613771828361376b60026137666137606136c0565b916136f7565b612fc0565b9161302b565b6137978184613791600361378c6137866136c0565b91613716565b612fc0565b9161302b565b92612f32565b612f32565b90565b906137d86137c96137de94936137b9612e53565b50936137c3612f76565b90612f32565b916137d2612f11565b90612f32565b9161302b565b90565b5f90565b5f7f6d61782075696e74313238206578636565646564000000000000000000000000910152565b6138166014611153565b90613823602083016137e5565b565b61382d61380c565b90565b613838613825565b90565b156138435750565b6138649061384f610231565b91829162461bcd60e51b83526004830161122c565b0390fd5b61387c613877613881926102dd565b6106a8565b61304a565b90565b6138cc906138906137e1565b506138c7816138b76138b16fffffffffffffffffffffffffffffffff613134565b916102dd565b11156138c1613830565b9061383b565b613868565b90565b67ffffffffffffffff81116138e75760208091020190565b610251565b909291926139016138fc826138cf565b61028e565b938185526020808601920283019281841161393e57915b8383106139255750505050565b602080916139338486611cdf565b815201920191613918565b610896565b9080601f830112156139615781602061395e935191016138ec565b90565b61088e565b90602082820312613996575f82015167ffffffffffffffff81116139915761398e9201613943565b90565b61023f565b61023b565b5190565b60200190565b6139ae90610396565b9052565b906139bf816020936139a5565b0190565b60200190565b906139e66139e06139d98461399b565b8093612bc8565b9261399f565b905f5b8181106139f65750505090565b909192613a0f613a0960019286516139b2565b946139c3565b91019190916139e9565b613a2e9160208201915f8184039101526139c9565b90565b634e487b7160e01b5f52603260045260245ffd5b90613a4f8261399b565b811015613a60576020809102010190565b613a31565b613a6f9051610396565b90565b613a86613a81613a8b926102dd565b6106a8565b6102dd565b90565b613a9a613a9f91611721565b613a72565b90565b613aca613ac4613abf613af1955f95613ab9612e53565b50613c3f565b613ba7565b91611404565b613ae6637784c685613ada610231565b95869485938493611cd9565b835260048301613a19565b03915afa8015613b4c57613b1d613b2291613b27935f91613b2a575b50613b175f611eca565b90613a45565b613a65565b613a8e565b90565b613b4691503d805f833e613b3e8183610265565b810190613966565b5f613b0d565b611d0c565b606090565b90613b68613b63836138cf565b61028e565b918252565b369037565b90613b97613b7f83613b56565b92602080613b8d86936138cf565b9201910390613b6d565b565b90613ba390610396565b9052565b613baf613b51565b50613bde613bc5613bc06001612ef5565b613b72565b91613bd983613bd35f611eca565b90613a45565b613b99565b90565b613beb60026136f7565b90565b916020613c0f929493613c0860408201965f8301906103a5565b01906112a6565b565b60200190565b613c205f611eca565b90565b613c37613c32613c3c926102dd565b611033565b610396565b90565b613cdb613ce991613cc3613cee94613c556117a3565b5091613c89613c62613be1565b91613c7a613c6e610231565b93849260208401613bee565b60208201810382520382610265565b613c9b613c9582611ec6565b91613c11565b2091613cb4613ca8610231565b9384926020840161184c565b60208201810382520382610265565b613cd5613ccf82611ec6565b91613c11565b20613a8e565b613ce3613c17565b90612f32565b613c23565b90565b90613d28939291613d23613d03611e41565b613d15613d0e6115d9565b829061174d565b908115613d2a575b506128cc565b614004565b565b613d3c9150613d37611054565b61174d565b5f613d1d565b67ffffffffffffffff8111613d5a5760208091020190565b610251565b90613d71613d6c83613d42565b61028e565b918252565b613d80604061028e565b90565b613d8d60a061028e565b90565b5f90565b5f90565b613da0613d83565b9060208080808086613db0613d90565b815201613dbb613d90565b815201613dc6613d90565b815201613dd1613d90565b815201613ddc613d94565b81525050565b613dea613d98565b90565b613df5613d76565b9060208083613e02613de2565b815201613e0d613d94565b81525050565b613e1b613ded565b90565b5f5b828110613e2c57505050565b602090613e37613e13565b8184015201613e20565b90613e66613e4e83613d5f565b92602080613e5c8693613d42565b9201910390613e1e565b565b613e7c613e77613e8192611030565b6106a8565b610d8b565b90565b613e98613e93613e9d92610d8b565b6106a8565b6102dd565b90565b613ea990610d8b565b600160ff1b8114613eb9575f0390565b612f1e565b613ec8604061028e565b90565b52565b90613ed8906102dd565b9052565b5190565b90613eea82613edc565b811015613efb576020809102010190565b613a31565b60200190565b90608080613f5e93613f1e5f8201515f860190612982565b613f3060208201516020860190612982565b613f4260408201516040860190612982565b613f5460608201516060860190612982565b0151910190610777565b565b9060a06020613f8393613f795f8201515f860190613f06565b0151910190610777565b565b90613f928160c093613f60565b0190565b60200190565b90613fb9613fb3613fac84613edc565b8093612960565b92613f00565b905f5b818110613fc95750505090565b909192613fe2613fdc6001928651613f85565b94613f96565b9101919091613fbc565b6140019160208201915f818403910152613f9c565b90565b90614106926140b56140ff9261402261401d60026136f7565b613e41565b9661402e868390612e78565b614036612e53565b508161404a6140445f613e68565b91610d8b565b125f14614185576140909161406a6140656140709392613ea0565b613e84565b90612f32565b915b9161408761407e613ebe565b935f8501613ecb565b60208301613ece565b6140af87915f906140a96140a383611eca565b85613ee0565b52611eca565b90613ee0565b51506140d95f196140d06140c7613ebe565b935f8501613ecb565b60208301613ece565b6140f985916001906140f36140ed83612ef5565b85613ee0565b52612ef5565b90613ee0565b5150612945565b90637299aa3190823b156141805761413d926141325f8094614126610231565b96879586948593611cd9565b835260048301613fec565b03925af1801561417b5761414f575b50565b61416e905f3d8111614174575b6141668183610265565b810190612951565b5f61414c565b503d61415c565b611d0c565b611cd5565b61418e82613e84565b6141a061419a836102dd565b916102dd565b11155f146141c657614090916141b96141bf9291613e84565b90613150565b5b91614072565b50506140906141d45f611eca565b6141c0565b906141e5939291613cf1565b565b9061421e9392916142196141f9611e41565b61420b6142046115d9565b829061174d565b908115614220575b506128cc565b6142da565b565b614232915061422d611054565b61174d565b5f614213565b90505190614245826102ba565b565b919060a0838203126142b7576142b09061426160a061028e565b9361426e825f8301614238565b5f86015261427f8260208301614238565b60208601526142918260408301614238565b60408601526142a38260608301614238565b60608601526080016131ea565b6080830152565b610243565b9060a0828203126142d5576142d2915f01614247565b90565b61023b565b9160a061431a94926142f36142ee5f612e6b565b611404565b61430f632c3c9157614303610231565b98899485938493611cd9565b8352600483016103b2565b03915afa9081156143f25761436c945f926143c0575b5060a0906143456143405f612e6b565b611404565b614361632c3c9157614355610231565b98899485938493611cd9565b8352600483016103b2565b03915afa9182156143bb57614389945f9361438b575b50926141d9565b565b6143ad91935060a03d81116143b4575b6143a58183610265565b8101906142bc565b915f614382565b503d61439b565b611d0c565b60a09192506143e490823d81116143eb575b6143dc8183610265565b8101906142bc565b9190614330565b503d6143d2565b611d0c565b906144039392916141e7565b565b9061443c939291614437614417611e41565b6144296144226115d9565b829061174d565b90811561443e575b506128cc565b614527565b565b614450915061444b611054565b61174d565b5f614431565b67ffffffffffffffff811161446e5760208091020190565b610251565b919060c0838203126144ad576144a69061448d604061028e565b9361449a825f8301610303565b5f86015260a001610da2565b6020830152565b610243565b909291926144c76144c282614456565b61028e565b9381855260c060208601920283019281841161450657915b8383106144ec5750505050565b602060c0916144fb8486614473565b8152019201916144df565b610896565b6145169136916144b2565b90565b614524903690610303565b90565b61453a614546946145409294909361450b565b91614519565b916148ca565b565b90614554939291614405565b565b9061458d939291614588614568611e41565b61457a6145736115d9565b829061174d565b90811561458f575b506128cc565b6146d8565b565b6145a1915061459c611054565b61174d565b5f614582565b5090565b906145bd6145b883614456565b61028e565b918252565b6145cc604061028e565b90565b5f90565b6145db6145c2565b90602080836145e8613de2565b8152016145f36145cf565b81525050565b6146016145d3565b90565b5f5b82811061461257505050565b60209061461d6145f9565b8184015201614606565b9061464c614634836145ab565b926020806146428693614456565b9201910390614604565b565b614657906102dd565b5f1981146146655760010190565b612f1e565b919081101561467a576040020190565b613a31565b3561468981610736565b90565b3561469681610d8e565b90565b6146a3604061028e565b90565b906146b090610d8b565b9052565b5190565b906146c2826146b4565b8110156146d3576020809102010190565b613a31565b9290936146ee6146e98685906145a7565b614627565b916146f85f611eca565b5b8061471661471061470b8a89906145a7565b6102dd565b916102dd565b1015614816576147729060a08861473461472f5f612e6b565b611404565b6147676147525f61474c632c3c9157958d899161466a565b0161467f565b9261475b610231565b96879485938493611cd9565b8352600483016103b2565b03915afa918215614811576147de926147d7915f916147e3575b506147c46147a760206147a18d8c889161466a565b0161468c565b6147bb6147b2614699565b935f8501613ecb565b602083016146a6565b8683916147d183836146b8565b526146b8565b515061464e565b6146f9565b614804915060a03d811161480a575b6147fc8183610265565b8101906142bc565b5f61478c565b503d6147f2565b611d0c565b50919250935060a061485c9392916148356148305f612e6b565b611404565b614851632c3c9157614845610231565b97889485938493611cd9565b8352600483016103b2565b03915afa9182156148aa57614878935f9361487a575b506148ca565b565b61489c91935060a03d81116148a3575b6148948183610265565b8101906142bc565b915f614872565b503d61488a565b611d0c565b906148bb939291614556565b565b6148c79051610d8b565b90565b9290926148f16148ec6148dc866146b4565b6148e66001612ef5565b90612f32565b613e41565b916148fb5f611eca565b5b8061491761491161490c896146b4565b6102dd565b916102dd565b1015614a76576149f49061493a845f6149318a85906146b8565b51015190612e78565b614942612e53565b508761495b60206149548386906146b8565b51016148bd565b61496d6149675f613e68565b91610d8b565b125f146149f9576149a9906149a361499e61499960206149926149ed979589906146b8565b51016148bd565b613ea0565b613e84565b90612f32565b5b6149da5f6149b98b86906146b8565b510151916149d16149c8613ebe565b935f8501613ecb565b60208301613ece565b8683916149e78383613ee0565b52613ee0565b515061464e565b6148fc565b614a136020614a0c614a189386906146b8565b51016148bd565b613e84565b614a2a614a24836102dd565b916102dd565b11155f14614a6457614a5e6149ed91614a58614a536020614a4c8d88906146b8565b51016148bd565b613e84565b90613150565b5b6149aa565b506149ed614a715f611eca565b614a5f565b5093614abf90614ac6939490614aa45f19614a9b614a92613ebe565b945f8601613ecb565b60208401613ece565b614aae86916146b4565b91614ab98383613ee0565b52613ee0565b5150612945565b90637299aa3190823b15614b4057614afd92614af25f8094614ae6610231565b96879586948593611cd9565b835260048301613fec565b03925af18015614b3b57614b0f575b50565b614b2e905f3d8111614b34575b614b268183610265565b810190612951565b5f614b0c565b503d614b1c565b611d0c565b611cd5565b90614b7b9291614b76614b56611e41565b614b68614b616115d9565b829061174d565b908115614b7d575b506128cc565b614ce8565b565b614b8f9150614b8a611054565b61174d565b5f614b70565b614ba1614ba691611721565b610668565b90565b614bb39054614b95565b90565b60209181520190565b90565b5090565b90503590614bd38261305f565b565b50614be4906020810190614bc6565b90565b906020614c12614c1a93614c09614c005f830183614bd5565b5f860190613271565b82810190614bd5565b910190613271565b565b906020614c47614c4f93614c3e614c355f830183612bf2565b5f860190612bd4565b82810190614bc2565b910190614be7565b565b90614c5e81606093614c1c565b0190565b5090565b60600190565b91614c7a82614c8092614bb6565b92614bbf565b90815f905b828210614c93575050505090565b90919293614cb5614caf600192614caa8886614c62565b614c51565b95614c66565b920190929192614c85565b91614ce5939192614cd860408201945f83019061183f565b6020818503910152614c6c565b90565b90614cfb614cf66001614ba9565b6106d3565b614d0963f461804693612945565b919392813b15614d7f575f614d3191614d3c8296614d25610231565b98899788968795611cd9565b855260048501614cc0565b03925af18015614d7a57614d4e575b50565b614d6d905f3d8111614d73575b614d658183610265565b810190612951565b5f614d4b565b503d614d5b565b611d0c565b611cd5565b90614d8f9291614b45565b565b90614dc691614dc1614da1611e41565b614db3614dac6115d9565b829061174d565b908115614dc8575b506128cc565b614e03565b565b614dda9150614dd5611054565b61174d565b5f614dbb565b916020614e01929493614dfa60408201965f83019061183f565b01906112a6565b565b614e15614e106001614ba9565b6106d3565b9163e55156b5919092803b15614e9057614e425f8094614e4d614e36610231565b97889687958694611cd9565b845260048401614de0565b03925af18015614e8b57614e5f575b50565b614e7e905f3d8111614e84575b614e768183610265565b810190612951565b5f614e5c565b503d614e6c565b611d0c565b611cd5565b90614e9f91614d91565b565b90614ed691614ed1614eb1611e41565b614ec3614ebc6115d9565b829061174d565b908115614ed8575b506128cc565b614f20565b565b614eea9150614ee5611054565b61174d565b5f614ecb565b614ef9906110a0565b9052565b916020614f1e929493614f1760408201965f83019061183f565b0190614ef0565b565b614f32614f2d6001614ba9565b6106d3565b91630e4eecf8919092803b15614fad57614f5f5f8094614f6a614f53610231565b97889687958694611cd9565b845260048401614efd565b03925af18015614fa857614f7c575b50565b614f9b905f3d8111614fa1575b614f938183610265565b810190612951565b5f614f79565b503d614f89565b611d0c565b611cd5565b90614fbc91614ea1565b565b90614ff391614fee614fce611e41565b614fe0614fd961163d565b829061174d565b908115614ff5575b506125dc565b615030565b565b6150079150615002611054565b61174d565b5f614fe8565b91602061502e92949361502760408201965f83019061183f565b019061183f565b565b61504261503d6001614ba9565b6106d3565b9163c55b6bb7919092803b156150bd5761506f5f809461507a615063610231565b97889687958694611cd9565b84526004840161500d565b03925af180156150b85761508c575b50565b6150ab905f3d81116150b1575b6150a38183610265565b810190612951565b5f615089565b503d615099565b611d0c565b611cd5565b906150cc91614fbe565b565b6150e0906150da612ee1565b50612ee5565b90565b6150ee61010061028e565b90565b6150f96150e3565b906020808080808080808961510c613d94565b815201615117613d94565b815201615122613d94565b81520161512d613d94565b815201615138613d94565b815201615143613d94565b81520161514e613d94565b815201615159613d94565b81525050565b6151676150f1565b90565b90565b61518161517c6151869261516a565b6106a8565b6102dd565b90565b61519390516102dd565b90565b6151aa6151a56151af926136a1565b6106a8565b61304a565b90565b6151be6151c49161304a565b9161304a565b9003906fffffffffffffffffffffffffffffffff82116151e057565b612f1e565b906151ee61515f565b9161522f60c06152056152005f612e6b565b611404565b635c60e39a906152248592615218610231565b95869485938493611cd9565b8352600483016103b2565b03915afa908115615536575f91615508575b509061528360a06152596152545f612e6b565b611404565b632c3c915790615278859261526c610231565b95869485938493611cd9565b8352600483016103b2565b03915afa90811561550357615301916152f8915f916154d5575b50926152ea6152e46152b86152b15f612e6b565b879061334e565b9093916152de8d5f8101966152d860208301956060604085019401613ece565b90613ece565b90613ece565b90613ece565b6152f35f612e6b565b6155e4565b60808601613ece565b61531661530d5f611eca565b60e08601613ece565b61532260608201613191565b61533c6153366153315f6131ba565b6102ae565b916102ae565b0361540d575b506153da6153c86153e3926153585f8701615189565b61536a6153645f611eca565b916102dd565b145f146153e55761538761537d5f611eca565b5b60a08801613ece565b6153c26153bd61539960e08901615189565b926153b86153b260a0670de0b6b3a76400009301613127565b91615196565b6151b2565b613134565b906136d4565b6153d460a08601615189565b906136d4565b60c08401613ece565b565b6153876154086153f760408901615189565b6154025f8a01615189565b9061553b565b61537e565b602061542b61542661542160608501613191565b6131d2565b6131de565b638c00bf6b929061544e8594615459615442610231565b96879586948594611cd9565b8452600484016132ea565b03915afa9081156154d0576153e39261549a6154916153da946153c8945f916154a2575b5061548b6301e1338061516d565b90613732565b60e08801613ece565b925050615342565b6154c3915060203d81116154c9575b6154bb8183610265565b8101906131f9565b5f61547d565b503d6154b1565b611d0c565b6154f6915060a03d81116154fc575b6154ee8183610265565b8101906142bc565b5f61529d565b503d6154e4565b611d0c565b615529915060c03d811161552f575b6155218183610265565b810190613109565b5f615241565b503d615517565b611d0c565b9061555991615548612e53565b50906155526136c0565b909161555c565b90565b615576615595939261559092615570612e53565b50612fc0565b61558a836155846001612ef5565b90613150565b90612f32565b613009565b90565b90565b60ff1690565b6155b56155b06155ba92615598565b6106a8565b61559b565b90565b6155dc906155d66155d06155e19461559b565b91610396565b90610664565b610396565b90565b5f9061560c615606615601615633956155fb612e53565b506156c1565b613ba7565b91611404565b615628637784c68561561c610231565b95869485938493611cd9565b835260048301613a19565b03915afa9081156156a25761566861566361567d93615678935f91615680575b5061565d5f611eca565b90613a45565b613a65565b61567260806155a1565b906155bd565b613a8e565b90565b61569c91503d805f833e6156948183610265565b810190613966565b5f615653565b611d0c565b6156b16003613716565b90565b6156be60026136f7565b90565b61572c61571e615731926156d36117a3565b506157066156df6156a7565b916156f76156eb610231565b93849260208401613bee565b60208201810382520382610265565b61571861571282611ec6565b91613c11565b20613a8e565b6157266156b4565b90612f32565b613c23565b90565b61573f61010061028e565b90565b61574a615734565b906020808080808080808961575d613d94565b815201615768613d94565b815201615773613d94565b81520161577e613d94565b815201615789613d94565b815201615794613d94565b81520161579f613d94565b8152016157aa613d94565b81525050565b6157b8615742565b90565b919060608382031261580757615800906157d5606061028e565b936157e2825f83016131ea565b5f8601526157f38260208301613073565b6020860152604001613073565b6040830152565b610243565b9060608282031261582557615822915f016157bb565b90565b61023b565b91602061584b92949361584460408201965f8301906103a5565b019061183f565b565b615856906106ab565b90565b6158629061584d565b90565b61586e906106c7565b90565b90565b61588861588361588d92615871565b6106a8565b6102dd565b90565b6158a86ec097ce7bc90715b34b9f1000000000615874565b90565b91906158b56157b0565b926158f660a06158cc6158c75f612e6b565b611404565b632c3c9157906158eb85926158df610231565b95869485938493611cd9565b8352600483016103b2565b03915afa908115615c52575f91615c24575b5090606061591d6159185f612e6b565b611404565b6393c520629290615940869461594b615934610231565b96879586948594611cd9565b84526004840161582a565b03915afa908115615c1f575f91615bf1575b5061596a60408301613191565b61598461597e6159795f6131ba565b6102ae565b916102ae565b145f14615b41576080615ab6615ac394615a61615a595f615abd96615a538c6020615a4c8c6159b287611eca565b5b996159d36159cb6159c660408a01613127565b613134565b8e8701613ece565b6159fc6159f36159e48f8801615189565b8d6159ed615890565b9161302b565b60a08701613ece565b615a1c615a13615a0b8a612e6b565b848491615c78565b60608701613ece565b615a3b615a32615a2d868a01613127565b613134565b60408701613ece565b615a4488612e6b565b919091612ea2565b9101613ece565b01615189565b5f8a01613ece565b615a6d60a08901615189565b615a7f615a795f611eca565b916102dd565b145f14615b1857615a9c615a925f611eca565b5b60c08a01613ece565b615aa7838901615189565b90615ab0615890565b9161302b565b9201615189565b906136d4565b615acf60608401615189565b615ae1615adb5f611eca565b916102dd565b145f14615afa5750615af85f195b60e08401613ece565b565b615b13615af891615b0d60608601615189565b90615c57565b615aef565b615a9c615b3c615b2a60608b01615189565b615b3660a08c01615189565b9061553b565b615a93565b615b786020615b62615b5d615b5860408701613191565b615859565b615865565b63a035b1fe90615b70610231565b938492611cd9565b82528180615b88600482016104d2565b03915afa8015615bec57615ac394615a61615a595f615abd96615a538c6020615a4c8c615ab69a60809c8891615bbf575b506159b3565b615bdf9150843d8111615be5575b615bd78183610265565b8101906131f9565b5f615bb9565b503d615bcd565b611d0c565b615c12915060603d8111615c18575b615c0a8183610265565b81019061580c565b5f61595d565b503d615c00565b611d0c565b615c45915060a03d8111615c4b575b615c3d8183610265565b8101906142bc565b5f615908565b503d615c33565b611d0c565b90615c7591615c64612e53565b5090615c6e6136c0565b909161302b565b90565b90615ca690615ca0615cb594615c8c612e53565b50615c9683612ee5565b9085919091615cf3565b9261334e565b93925090509190919091615cb8565b90565b91615cea615cdb615cf094615ccb612e53565b5093615cd5612f11565b90612f32565b91615ce4612f76565b90612f32565b9161555c565b90565b615d1b615d15615d10615d42955f95615d0a612e53565b50615dbf565b613ba7565b91611404565b615d37637784c685615d2b610231565b95869485938493611cd9565b835260048301613a19565b03915afa8015615dad57615d7e615d79615d74615d8393615d88955f91615d8b575b50615d6e5f611eca565b90613a45565b613a65565b613a8e565b613868565b613134565b90565b615da791503d805f833e615d9f8183610265565b810190613966565b5f615d64565b611d0c565b615dbc6001612ef5565b90565b615e5b615e6991615e43615e6e94615dd56117a3565b5091615e09615de2613be1565b91615dfa615dee610231565b93849260208401613bee565b60208201810382520382610265565b615e1b615e1582611ec6565b91613c11565b2091615e34615e28610231565b9384926020840161184c565b60208201810382520382610265565b615e55615e4f82611ec6565b91613c11565b20613a8e565b615e63615db2565b90612f32565b613c23565b9056fea2646970667358221220a22232bfdc3a9e0decdaa178b9a6155dd02be7b15e95d1b327ebda4fda5a702e64736f6c63430008150033

Deployed Bytecode

0x60806040526004361015610013575b6116a1565b61001d5f3561022b565b8062fb0d821461022657806301ffc9a71461022157806312440f5a1461021c5780631da98c9214610217578063248a9ca3146102125780632f2ff15d1461020d57806330c9d4ac1461020857806330f4f4bb14610203578063364f5732146101fe57806336568abe146101f957806344fe5295146101f45780634844da13146101ef5780634f1ef286146101ea57806352d1902d146101e55780635c388821146101e057806367756192146101db578063784980b8146101d6578063919b56f6146101d157806391d14854146101cc57806393512617146101c75780639388e8f3146101c2578063a217fddf146101bd578063ab6a48d9146101b8578063ad3cb1cc146101b3578063afe0e35a146101ae578063b3f57300146101a9578063c4d66de8146101a4578063d547741f1461019f578063d8fbc8331461019a578063db35ce8414610195578063eb7499cf14610190578063f5b541a61461018b5763f72c0d8b0361000e5761166c565b611608565b6115a5565b6114ec565b611432565b611392565b61135f565b61132b565b6112c8565b611244565b6110fc565b61106b565b610ff9565b610f7d565b610ef7565b610ec0565b610df7565b610d58565b610d22565b610c1a565b610bf0565b610aec565b610a30565b610953565b61091f565b61082a565b610701565b610621565b6105bf565b610529565b6104d7565b610470565b6103c7565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b9061026f90610247565b810190811067ffffffffffffffff82111761028957604052565b610251565b906102a161029a610231565b9283610265565b565b60018060a01b031690565b6102b7906102a3565b90565b6102c3816102ae565b036102ca57565b5f80fd5b905035906102db826102ba565b565b90565b6102e9816102dd565b036102f057565b5f80fd5b90503590610301826102e0565b565b919060a0838203126103735761036c9061031d60a061028e565b9361032a825f83016102ce565b5f86015261033b82602083016102ce565b602086015261034d82604083016102ce565b604086015261035f82606083016102ce565b60608601526080016102f4565b6080830152565b610243565b9060a0828203126103915761038e915f01610303565b90565b61023b565b90565b6103a290610396565b90565b6103ae90610399565b9052565b91906103c5905f602085019401906103a5565b565b346103f7576103f36103e26103dd366004610378565b6150ce565b6103ea610231565b918291826103b2565b0390f35b610237565b63ffffffff60e01b1690565b610411816103fc565b0361041857565b5f80fd5b9050359061042982610408565b565b9060208282031261044457610441915f0161041c565b90565b61023b565b151590565b61045790610449565b9052565b919061046e905f6020850194019061044e565b565b346104a05761049c61048b61048636600461042b565b6116a9565b610493610231565b9182918261045b565b0390f35b610237565b91906040838203126104cd57806104c16104ca925f86016102ce565b936020016102ce565b90565b61023b565b5f0190565b34610506576104f06104ea3660046104a5565b906150c2565b6104f8610231565b80610502816104d2565b0390f35b610237565b9060208282031261052457610521915f016102ce565b90565b61023b565b346105575761054161053c36600461050b565b6126ff565b610549610231565b80610553816104d2565b0390f35b610237565b61056581610396565b0361056c57565b5f80fd5b9050359061057d8261055c565b565b9060208282031261059857610595915f01610570565b90565b61023b565b6105a690610396565b9052565b91906105bd905f6020850194019061059d565b565b346105ef576105eb6105da6105d536600461057f565b6117cb565b6105e2610231565b918291826105aa565b0390f35b610237565b919060408382031261061c5780610610610619925f8601610570565b936020016102ce565b90565b61023b565b346106505761063a6106343660046105f4565b9061181f565b610642610231565b8061064c816104d2565b0390f35b610237565b5f91031261065f57565b61023b565b1c90565b60018060a01b031690565b6106839060086106889302610664565b610668565b90565b906106969154610673565b90565b6106a560015f9061068b565b90565b90565b6106bf6106ba6106c4926102a3565b6106a8565b6102a3565b90565b6106d0906106ab565b90565b6106dc906106c7565b90565b6106e8906106d3565b9052565b91906106ff905f602085019401906106df565b565b3461073157610711366004610655565b61072d61071c610699565b610724610231565b918291826106ec565b0390f35b610237565b61073f81610396565b0361074657565b5f80fd5b9050359061075782610736565b565b906020828203126107725761076f915f0161074a565b90565b61023b565b610780906102dd565b9052565b9060e0806108129361079c5f8201515f860190610777565b6107ae60208201516020860190610777565b6107c060408201516040860190610777565b6107d260608201516060860190610777565b6107e460808201516080860190610777565b6107f660a082015160a0860190610777565b61080860c082015160c0860190610777565b0151910190610777565b565b9190610828905f6101008501940190610784565b565b3461085a57610856610845610840366004610759565b6151e5565b61084d610231565b91829182610814565b0390f35b610237565b610868906102ae565b90565b6108748161085f565b0361087b57565b5f80fd5b9050359061088c8261086b565b565b5f80fd5b5f80fd5b5f80fd5b909182601f830112156108d45781359167ffffffffffffffff83116108cf5760200192602083028401116108ca57565b610896565b610892565b61088e565b91909160408184031261091a576108f2835f830161087f565b92602082013567ffffffffffffffff811161091557610911920161089a565b9091565b61023f565b61023b565b3461094e576109386109323660046108d9565b91612e46565b610940610231565b8061094a816104d2565b0390f35b610237565b346109825761096c6109663660046105f4565b906118e7565b610974610231565b8061097e816104d2565b0390f35b610237565b909182601f830112156109c15781359167ffffffffffffffff83116109bc576020019260c083028401116109b757565b610896565b610892565b61088e565b5f80fd5b908160a09103126109d85790565b6109c6565b9060e082820312610a2b576109f4815f840161087f565b92602083013567ffffffffffffffff8111610a2657610a1883610a23928601610987565b9390946040016109ca565b90565b61023f565b61023b565b34610a6257610a4c610a433660046109dd565b92919091614548565b610a54610231565b80610a5e816104d2565b0390f35b610237565b909182601f83011215610aa15781359167ffffffffffffffff8311610a9c576020019260608302840111610a9757565b610896565b610892565b61088e565b919091604081840312610ae757610abf835f830161087f565b92602082013567ffffffffffffffff8111610ae257610ade9201610a67565b9091565b61023f565b61023b565b34610b1b57610b05610aff366004610aa6565b91614d84565b610b0d610231565b80610b17816104d2565b0390f35b610237565b5f80fd5b67ffffffffffffffff8111610b4257610b3e602091610247565b0190565b610251565b90825f939282370152565b90929192610b67610b6282610b24565b61028e565b93818552602085019082840111610b8357610b8192610b47565b565b610b20565b9080601f83011215610ba657816020610ba393359101610b52565b90565b61088e565b919091604081840312610beb57610bc4835f83016102ce565b92602082013567ffffffffffffffff8111610be657610be39201610b88565b90565b61023f565b61023b565b610c04610bfe366004610bab565b90611b86565b610c0c610231565b80610c16816104d2565b0390f35b34610c4a57610c2a366004610655565b610c46610c35611b4a565b610c3d610231565b918291826105aa565b0390f35b610237565b9190604083820312610c775780610c6b610c74925f860161074a565b936020016102ce565b90565b61023b565b9060e080610d0a93610c945f8201515f860190610777565b610ca660208201516020860190610777565b610cb860408201516040860190610777565b610cca60608201516060860190610777565b610cdc60808201516080860190610777565b610cee60a082015160a0860190610777565b610d0060c082015160c0860190610777565b0151910190610777565b565b9190610d20905f6101008501940190610c7c565b565b34610d5357610d4f610d3e610d38366004610c4f565b906158ab565b610d46610231565b91829182610d0c565b0390f35b610237565b34610d8657610d70610d6b36600461050b565b6127b4565b610d78610231565b80610d82816104d2565b0390f35b610237565b90565b610d9781610d8b565b03610d9e57565b5f80fd5b90503590610daf82610d8e565b565b608081830312610df257610dc7825f830161087f565b92610def610dd8846020850161074a565b93610de6816040860161074a565b93606001610da2565b90565b61023b565b34610e2957610e13610e0a366004610db1565b929190916143f7565b610e1b610231565b80610e25816104d2565b0390f35b610237565b909182601f83011215610e685781359167ffffffffffffffff8311610e63576020019260408302840111610e5e57565b610896565b610892565b61088e565b90606082820312610ebb57610e84815f840161087f565b92602083013567ffffffffffffffff8111610eb657610ea883610eb3928601610e2e565b93909460400161074a565b90565b61023f565b61023b565b34610ef257610edc610ed3366004610e6d565b929190916148af565b610ee4610231565b80610eee816104d2565b0390f35b610237565b34610f2857610f24610f13610f0d3660046105f4565b9061174d565b610f1b610231565b9182918261045b565b0390f35b610237565b610f3681610449565b03610f3d57565b5f80fd5b90503590610f4e82610f2d565b565b9190604083820312610f785780610f6c610f75925f86016102ce565b93602001610f41565b90565b61023b565b34610fac57610f96610f90366004610f50565b90612841565b610f9e610231565b80610fa8816104d2565b0390f35b610237565b61018081830312610ff457610fc8825f830161087f565b92610ff1610fd98460208501610303565b93610fe78160c08601610303565b9361016001610da2565b90565b61023b565b3461102b5761101561100c366004610fb1565b929190916141d9565b61101d610231565b80611027816104d2565b0390f35b610237565b90565b5f1b90565b61104c61104761105192611030565b611033565b610396565b90565b61105d5f611038565b90565b611068611054565b90565b3461109b5761107b366004610655565b611097611086611060565b61108e610231565b918291826105aa565b0390f35b610237565b6110a9906102a3565b90565b6110b5816110a0565b036110bc57565b5f80fd5b905035906110cd826110ac565b565b91906040838203126110f757806110eb6110f4925f86016102ce565b936020016110c0565b90565b61023b565b3461112b5761111561110f3660046110cf565b90614fb2565b61111d610231565b80611127816104d2565b0390f35b610237565b67ffffffffffffffff811161114e5761114a602091610247565b0190565b610251565b9061116561116083611130565b61028e565b918252565b5f7f352e302e30000000000000000000000000000000000000000000000000000000910152565b61119b6005611153565b906111a86020830161116a565b565b6111b2611191565b90565b6111bd6111aa565b90565b6111c86111b5565b90565b5190565b60209181520190565b5f5b8381106111ea575050905f910152565b8060209183015181850152016111da565b61121a61122360209361122893611211816111cb565b938480936111cf565b958691016111d8565b610247565b0190565b6112419160208201915f8184039101526111fb565b90565b3461127457611254366004610655565b61127061125f6111c0565b611267610231565b9182918261122c565b0390f35b610237565b919060c0838203126112a1578061129561129e925f860161087f565b93602001610303565b90565b61023b565b6112af906102dd565b9052565b91906112c6905f602085019401906112a6565b565b346112f9576112f56112e46112de366004611279565b90612e78565b6112ec610231565b918291826112b3565b0390f35b610237565b9190604083820312611326578061131a611323925f86016102ce565b936020016102f4565b90565b61023b565b3461135a5761134461133e3660046112fe565b90614e95565b61134c610231565b80611356816104d2565b0390f35b610237565b3461138d5761137761137236600461050b565b6124ae565b61137f610231565b80611389816104d2565b0390f35b610237565b346113c1576113ab6113a53660046105f4565b906118db565b6113b3610231565b806113bd816104d2565b0390f35b610237565b60018060a01b031690565b6113e19060086113e69302610664565b6113c6565b90565b906113f491546113d1565b90565b6114015f806113e9565b90565b61140d906106c7565b90565b61141990611404565b9052565b9190611430905f60208501940190611410565b565b3461146257611442366004610655565b61145e61144d6113f7565b611455610231565b9182918261141d565b0390f35b610237565b909182601f830112156114a15781359167ffffffffffffffff831161149c57602001926020830284011161149757565b610896565b610892565b61088e565b9190916040818403126114e7576114bf835f830161087f565b92602082013567ffffffffffffffff81116114e2576114de9201611467565b9091565b61023f565b61023b565b3461151b576115056114ff3660046114a6565b91612d01565b61150d610231565b80611517816104d2565b0390f35b610237565b909182601f8301121561155a5781359167ffffffffffffffff8311611555576020019260c0830284011161155057565b610896565b610892565b61088e565b9190916040818403126115a057611578835f830161087f565b92602082013567ffffffffffffffff811161159b576115979201611520565b9091565b61023f565b61023b565b346115d4576115be6115b836600461155f565b91612b6b565b6115c6610231565b806115d0816104d2565b0390f35b610237565b7f97667070c54ef182b0f5858b034beac1b6f3089aa2d3188bb1e8929f4fa9b92990565b6116056115d9565b90565b3461163857611618366004610655565b6116346116236115fd565b61162b610231565b918291826105aa565b0390f35b610237565b7f189ab7a9244df0848122154315af71fe140f3db0fe014031783b0946b8c9d2e390565b61166961163d565b90565b3461169c5761167c366004610655565b611698611687611661565b61168f610231565b918291826105aa565b0390f35b610237565b5f80fd5b5f90565b6116b16116a5565b50806116cc6116c6637965db0b60e01b6103fc565b916103fc565b149081156116d9575b5090565b6116e39150611e4e565b5f6116d5565b906116f390610399565b5f5260205260405f2090565b611708906106c7565b90565b90611715906116ff565b5f5260205260405f2090565b5f1c90565b60ff1690565b61173861173d91611721565b611726565b90565b61174a905461172c565b90565b61177c915f611771611777936117616116a5565b508261176b61177f565b016116e9565b0161170b565b611740565b90565b7f02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b62680090565b5f90565b90565b6117b66117bb91611721565b6117a7565b90565b6117c890546117aa565b90565b60016117ec6117f2926117dc6117a3565b505f6117e661177f565b016116e9565b016117be565b90565b906118109161180b611806826117cb565b61182b565b611812565b565b9061181c9161197b565b50565b90611829916117f5565b565b61183d90611837611e41565b9061186f565b565b611848906102ae565b9052565b91602061186d92949361186660408201965f83019061183f565b019061059d565b565b9061188461187e83839061174d565b15610449565b61188c575050565b6118ad611897610231565b92839263e2517d3f60e01b84526004840161184c565b0390fd5b906118cc916118c76118c2826117cb565b61182b565b6118ce565b565b906118d891611a32565b50565b906118e5916118b1565b565b90806119026118fc6118f7611e41565b6102ae565b916102ae565b036119135761191091611a32565b50565b61191b610231565b63334bd91960e11b815280611932600482016104d2565b0390fd5b9061194260ff91611033565b9181191691161790565b61195590610449565b90565b90565b9061197061196b6119779261194c565b611958565b8254611936565b9055565b6119836116a5565b5061198c61177f565b6119a061199a83859061174d565b15610449565b5f14611a2b576119ca906119c55f6119bd816001940186906116e9565b01859061170b565b61195b565b906119d3611e41565b90611a10611a0a611a047f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d95610399565b926116ff565b926116ff565b92611a19610231565b80611a23816104d2565b0390a4600190565b5050505f90565b611a3a6116a5565b50611a4361177f565b611a4e82849061174d565b5f14611ad857611a7790611a725f611a6a8180940186906116e9565b01859061170b565b61195b565b90611a80611e41565b90611abd611ab7611ab17ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b95610399565b926116ff565b926116ff565b92611ac6610231565b80611ad0816104d2565b0390a4600190565b5050505f90565b611af090611aeb611c4c565b611b3e565b90565b90565b611b0a611b05611b0f92611af3565b611033565b610396565b90565b611b3b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc611af6565b90565b50611b47611b12565b90565b611b5a611b556117a3565b611adf565b90565b90611b6f91611b6a611b9e565b611b71565b565b90611b8491611b7f8161264b565b611d31565b565b90611b9091611b5d565b565b611b9b906106c7565b90565b611ba730611b92565b611bd9611bd37f000000000000000000000000eafabd9dcc2a8106ed87a03cd150af08752883806102ae565b916102ae565b148015611c0a575b611be757565b611bef610231565b63703e46dd60e11b815280611c06600482016104d2565b0390fd5b50611c13611ea0565b611c45611c3f7f000000000000000000000000eafabd9dcc2a8106ed87a03cd150af08752883806102ae565b916102ae565b1415611be1565b611c5530611b92565b611c87611c817f000000000000000000000000eafabd9dcc2a8106ed87a03cd150af08752883806102ae565b916102ae565b03611c8e57565b611c96610231565b63703e46dd60e11b815280611cad600482016104d2565b0390fd5b611cba906106ab565b90565b611cc690611cb1565b90565b611cd2906106c7565b90565b5f80fd5b60e01b90565b90505190611cec8261055c565b565b90602082820312611d0757611d04915f01611cdf565b90565b61023b565b611d14610231565b3d5f823e3d90fd5b9190611d2f905f6020850194019061183f565b565b9190611d5f6020611d49611d4486611cbd565b611cc9565b6352d1902d90611d57610231565b938492611cd9565b82528180611d6f600482016104d2565b03915afa80915f92611e0d575b50155f14611db7575050906001611d9157505b565b611db390611d9d610231565b918291634c9c8ce360e01b835260048301611d1c565b0390fd5b9283611dd2611dcc611dc7611b12565b610396565b91610396565b03611de757611de2929350611ee6565b611d8f565b611e0984611df3610231565b918291632a87526960e21b8352600483016105aa565b0390fd5b611e2f91925060203d8111611e36575b611e278183610265565b810190611cee565b905f611d7c565b503d611e1d565b5f90565b611e49611e3d565b503390565b611e566116a5565b50611e70611e6a6301ffc9a760e01b6103fc565b916103fc565b1490565b60018060a01b031690565b611e8b611e9091611721565b611e74565b90565b611e9d9054611e7f565b90565b611ea8611e3d565b50611ec35f611ebd611eb8611b12565b612192565b01611e93565b90565b5190565b611ede611ed9611ee392611030565b6106a8565b6102dd565b90565b90611ef082611fad565b81611f1b7fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b916116ff565b90611f24610231565b80611f2e816104d2565b0390a2611f3a81611ec6565b611f4c611f465f611eca565b916102dd565b115f14611f6057611f5c9161208b565b505b565b5050611f6a612009565b611f5e565b90611f8060018060a01b0391611033565b9181191691161790565b90565b90611fa2611f9d611fa9926116ff565b611f8a565b8254611f6f565b9055565b803b611fc1611fbb5f611eca565b916102dd565b14611fe357611fe1905f611fdb611fd6611b12565b612192565b01611f8d565b565b61200590611fef610231565b918291634c9c8ce360e01b835260048301611d1c565b0390fd5b3461201c6120165f611eca565b916102dd565b1161202357565b61202b610231565b63b398979f60e01b815280612042600482016104d2565b0390fd5b606090565b9061205d61205883610b24565b61028e565b918252565b3d5f1461207d576120723d61204b565b903d5f602084013e5b565b612085612046565b9061207b565b5f806120b793612099612046565b508390602081019051915af4906120ae612062565b909190916120ba565b90565b906120ce906120c7612046565b5015610449565b5f146120da5750612145565b6120e382611ec6565b6120f56120ef5f611eca565b916102dd565b148061212a575b612104575090565b61212690612110610231565b918291639996b31560e01b835260048301611d1c565b0390fd5b50803b61213f6121395f611eca565b916102dd565b146120fc565b61214e81611ec6565b61216061215a5f611eca565b916102dd565b115f1461216f57805190602001fd5b612177610231565b630a12f52160e11b81528061218e600482016104d2565b0390fd5b90565b60401c90565b6121a76121ac91612195565b611726565b90565b6121b9905461219b565b90565b67ffffffffffffffff1690565b6121d56121da91611721565b6121bc565b90565b6121e790546121c9565b90565b67ffffffffffffffff1690565b61220b61220661221092611030565b6106a8565b6121ea565b90565b90565b61222a61222561222f92612213565b6106a8565b6121ea565b90565b61223b906106c7565b90565b9061225167ffffffffffffffff91611033565b9181191691161790565b61226f61226a612274926121ea565b6106a8565b6121ea565b90565b90565b9061228f61228a6122969261225b565b612277565b825461223e565b9055565b60401b90565b906122b468ff00000000000000009161229a565b9181191691161790565b906122d36122ce6122da9261194c565b611958565b82546122a0565b9055565b6122e790612216565b9052565b91906122fe905f602085019401906122de565b565b612308612525565b9061231d6123175f84016121af565b15610449565b906123295f84016121dd565b8061233c6123365f6121f7565b916121ea565b148061245d575b906123576123516001612216565b916121ea565b1480612435575b612369909115610449565b9081612424575b50612401576123999061238e6123866001612216565b5f860161227a565b826123ef575b612464565b6123a1575b50565b6123ae905f8091016122be565b60016123e67fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2916123dd610231565b918291826122eb565b0390a15f61239e565b6123fc60015f86016122be565b612394565b612409610231565b63f92ee8a960e01b815280612420600482016104d2565b0390fd5b61242f915015610449565b5f612370565b5061236961244230612232565b3b61245561244f5f611eca565b916102dd565b14905061235e565b5082612343565b6124ab906124706124c3565b612478612553565b61248a612483611054565b829061197b565b5061249d61249661163d565b829061197b565b506124a66115d9565b61197b565b50565b6124b790612300565b565b6124c16124cd565b565b6124cb6124b9565b565b6124de6124d8612507565b15610449565b6124e457565b6124ec610231565b631afcd79f60e31b815280612503600482016104d2565b0390fd5b61250f6116a5565b506125225f61251c612525565b016121af565b90565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0090565b6125516124cd565b565b61255b612549565b565b60207f7067726164657200000000000000000000000000000000000000000000000000917f4d6f7270686f48656c7065723a2043616c6c6572206973206e6f7420616e20755f8201520152565b6125b760276040926111cf565b6125c08161255d565b0190565b6125d99060208101905f8183039101526125aa565b90565b156125e357565b6125eb610231565b62461bcd60e51b815280612601600482016125c4565b0390fd5b50612631612611611e41565b61262361261c61163d565b829061174d565b908115612633575b506125dc565b565b6126459150612640611054565b61174d565b5f61262b565b61265490612605565b565b61268a90612685612665611e41565b61267761267061163d565b829061174d565b90811561268c575b506125dc565b6126eb565b565b61269e9150612699611054565b61174d565b5f61267f565b6126ad906106ab565b90565b6126b9906126a4565b90565b6126c5906126a4565b90565b90565b906126e06126db6126e7926126bc565b6126c8565b8254611f6f565b9055565b6126f76126fd916126b0565b5f6126cb565b565b61270890612656565b565b61273e90612739612719611e41565b61272b61272461163d565b829061174d565b908115612740575b506125dc565b61279f565b565b612752915061274d611054565b61174d565b5f612733565b612761906106ab565b90565b61276d90612758565b90565b61277990612758565b90565b90565b9061279461278f61279b92612770565b61277c565b8254611f6f565b9055565b6127ab6127b291612764565b600161277f565b565b6127bd9061270a565b565b906127f4916127ef6127cf611e41565b6127e16127da61163d565b829061174d565b9081156127f6575b506125dc565b61280e565b565b6128089150612803611054565b61174d565b5f6127e9565b905f1461282a57612826906128216115d9565b61197b565b505b565b61283b906128366115d9565b611a32565b50612828565b9061284b916127bf565b565b60207f70657261746f7200000000000000000000000000000000000000000000000000917f4d6f7270686f48656c7065723a2043616c6c6572206973206e6f7420616e206f5f8201520152565b6128a760276040926111cf565b6128b08161284d565b0190565b6128c99060208101905f81830391015261289a565b90565b156128d357565b6128db610231565b62461bcd60e51b8152806128f1600482016128b4565b0390fd5b9061292b9291612926612906611e41565b6129186129116115d9565b829061174d565b90811561292d575b506128cc565b612ae2565b565b61293f915061293a611054565b61174d565b5f612920565b61294e906106c7565b90565b5f91031261295b57565b61023b565b60209181520190565b90565b5090565b5061297f9060208101906102ce565b90565b61298b906102ae565b9052565b5061299e9060208101906102f4565b90565b906080612a1a612a22936129c36129ba5f830183612970565b5f860190612982565b6129dd6129d36020830183612970565b6020860190612982565b6129f76129ed6040830183612970565b6040860190612982565b612a11612a076060830183612970565b6060860190612982565b8281019061298f565b910190610777565b565b9060a0612a4f612a5793612a46612a3d5f83018361296c565b5f8601906129a1565b8281019061298f565b910190610777565b565b90612a668160c093612a24565b0190565b5090565b60c00190565b91612a8282612a8892612960565b92612969565b90815f905b828210612a9b575050505090565b90919293612abd612ab7600192612ab28886612a6a565b612a59565b95612a6e565b920190929192612a8d565b9091612adf9260208301925f818503910152612a74565b90565b612aeb90612945565b91637299aa31919092803b15612b6657612b185f8094612b23612b0c610231565b97889687958694611cd9565b845260048401612ac8565b03925af18015612b6157612b35575b50565b612b54905f3d8111612b5a575b612b4c8183610265565b810190612951565b5f612b32565b503d612b42565b611d0c565b611cd5565b90612b7692916128f5565b565b90612bae9291612ba9612b89611e41565b612b9b612b946115d9565b829061174d565b908115612bb0575b506128cc565b612c78565b565b612bc29150612bbd611054565b61174d565b5f612ba3565b60209181520190565b90565b612bdd90610399565b9052565b90612bee81602093612bd4565b0190565b50612c0190602081019061074a565b90565b60200190565b91612c1882612c1e92612bc8565b92612bd1565b90815f905b828210612c31575050505090565b90919293612c53612c4d600192612c488886612bf2565b612be1565b95612c04565b920190929192612c23565b9091612c759260208301925f818503910152612c0a565b90565b612c8190612945565b91632acc56f9919092803b15612cfc57612cae5f8094612cb9612ca2610231565b97889687958694611cd9565b845260048401612c5e565b03925af18015612cf757612ccb575b50565b612cea905f3d8111612cf0575b612ce28183610265565b810190612951565b5f612cc8565b503d612cd8565b611d0c565b611cd5565b90612d0c9291612b78565b565b90612d449291612d3f612d1f611e41565b612d31612d2a6115d9565b829061174d565b908115612d46575b506128cc565b612dbd565b565b612d589150612d53611054565b61174d565b5f612d39565b60209181520190565b5f80fd5b9037565b909182612d7b91612d5e565b9160018060fb1b038111612d9e5782916020612d9a9202938491612d6b565b0190565b612d67565b9091612dba9260208301925f818503910152612d6f565b90565b612dc690612945565b916341b67833919092803b15612e4157612df35f8094612dfe612de7610231565b97889687958694611cd9565b845260048401612da3565b03925af18015612e3c57612e10575b50565b612e2f905f3d8111612e35575b612e278183610265565b810190612951565b5f612e0d565b503d612e1d565b611d0c565b611cd5565b90612e519291612d0e565b565b5f90565b612e63612e6891611721565b6113c6565b90565b612e759054612e57565b90565b90612e9f91612e85612e53565b50612e99612e925f612e6b565b9291612945565b91612ea2565b90565b90612ed090612eca612ede94612eb6612e53565b50612ec083612ee5565b9085919091613aa2565b9261334e565b505091909190919091612f85565b90565b5f90565b60a090612ef0612ee1565b502090565b612f09612f04612f0e92612213565b6106a8565b6102dd565b90565b612f1b6001612ef5565b90565b634e487b7160e01b5f52601160045260245ffd5b612f41612f47919392936102dd565b926102dd565b8201809211612f5257565b612f1e565b90565b612f6e612f69612f7392612f57565b6106a8565b6102dd565b90565b612f82620f4240612f5a565b90565b91612fb7612fa8612fbd94612f98612e53565b5093612fa2612f11565b90612f32565b91612fb1612f76565b90612f32565b9161302b565b90565b612fcf612fd5919392936102dd565b926102dd565b91612fe18382026102dd565b928184041490151715612ff057565b612f1e565b634e487b7160e01b5f52601260045260245ffd5b61301561301b916102dd565b916102dd565b908115613026570490565b612ff5565b61304792916130429161303c612e53565b50612fc0565b613009565b90565b6fffffffffffffffffffffffffffffffff1690565b6130688161304a565b0361306f57565b5f80fd5b905051906130808261305f565b565b919060c083820312613104576130fd9061309c60c061028e565b936130a9825f8301613073565b5f8601526130ba8260208301613073565b60208601526130cc8260408301613073565b60408601526130de8260608301613073565b60608601526130f08260808301613073565b608086015260a001613073565b60a0830152565b610243565b9060c0828203126131225761311f915f01613082565b90565b61023b565b613131905161304a565b90565b61314861314361314d9261304a565b6106a8565b6102dd565b90565b61315f613165919392936102dd565b926102dd565b820391821161317057565b612f1e565b61318961318461318e92611030565b6106a8565b61304a565b90565b61319b90516102ae565b90565b6131b26131ad6131b792611030565b6106a8565b6102a3565b90565b6131c39061319e565b90565b6131cf906106ab565b90565b6131db906131c6565b90565b6131e7906106c7565b90565b905051906131f7826102e0565b565b906020828203126132125761320f915f016131ea565b90565b61023b565b9060808061326f9361322f5f8201515f860190612982565b61324160208201516020860190612982565b61325360408201516040860190612982565b61326560608201516060860190612982565b0151910190610777565b565b61327a9061304a565b9052565b9060a0806132e8936132965f8201515f860190613271565b6132a860208201516020860190613271565b6132ba60408201516040860190613271565b6132cc60608201516060860190613271565b6132de60808201516080860190613271565b0151910190613271565b565b9160a061330c9294936133056101608201965f830190613217565b019061327e565b565b61331a6133209161304a565b9161304a565b01906fffffffffffffffffffffffffffffffff821161333b57565b612f1e565b9061334a9061304a565b9052565b60c06133b19161335c612e53565b50613365612e53565b5061336e612e53565b50613377612e53565b5061338a61338485612ee5565b91611404565b6133a6635c60e39a61339a610231565b95869485938493611cd9565b8352600483016103b2565b03915afa90811561369c575f9161366e575b50906133e3426133dd6133d860808601613127565b613134565b90613150565b806133f66133f05f611eca565b916102dd565b141580613648575b8061361a575b613462575b50506134165f8201613127565b61342260208301613127565b9261345f61345961345361344d613447606061344060408a01613127565b9801613127565b95613134565b96613134565b94613134565b92613134565b90565b602061348061347b61347660608601613191565b6131d2565b6131de565b638c00bf6b93906134a386956134ae613497610231565b97889586948594611cd9565b8452600484016132ea565b03915afa918215613615576134ea926134e4915f916135e7575b50916134de6134d960408701613127565b613134565b92613732565b906136d4565b6135126134f682613884565b61350c604085019161350783613127565b61330e565b90613340565b61353961351e82613884565b6135335f85019161352e83613127565b61330e565b90613340565b61354560a08301613127565b6135576135515f613175565b9161304a565b03613563575b80613409565b6135c56135c061358a6135e19361358461357f60a08801613127565b613134565b906136d4565b6135a6816135a161359c5f8901613127565b613134565b613150565b6135ba6135b560208801613127565b613134565b916137a5565b613884565b6135db60208401916135d683613127565b61330e565b90613340565b5f61355d565b613608915060203d811161360e575b6136008183610265565b8101906131f9565b5f6134c8565b503d6135f6565b611d0c565b5061362760608301613191565b61364161363b6136365f6131ba565b6102ae565b916102ae565b1415613404565b5061365560408401613127565b6136676136615f613175565b9161304a565b14156133fe565b61368f915060c03d8111613695575b6136878183610265565b810190613109565b5f6133c3565b503d61367d565b611d0c565b90565b6136b86136b36136bd926136a1565b6106a8565b6102dd565b90565b6136d1670de0b6b3a76400006136a4565b90565b906136f1916136e1612e53565b50906136eb6136c0565b9161302b565b90565b90565b61370b613706613710926136f4565b6106a8565b6102dd565b90565b90565b61372a61372561372f92613713565b6106a8565b6102dd565b90565b6137a29161374891613742612e53565b50612fc0565b61379d613771828361376b60026137666137606136c0565b916136f7565b612fc0565b9161302b565b6137978184613791600361378c6137866136c0565b91613716565b612fc0565b9161302b565b92612f32565b612f32565b90565b906137d86137c96137de94936137b9612e53565b50936137c3612f76565b90612f32565b916137d2612f11565b90612f32565b9161302b565b90565b5f90565b5f7f6d61782075696e74313238206578636565646564000000000000000000000000910152565b6138166014611153565b90613823602083016137e5565b565b61382d61380c565b90565b613838613825565b90565b156138435750565b6138649061384f610231565b91829162461bcd60e51b83526004830161122c565b0390fd5b61387c613877613881926102dd565b6106a8565b61304a565b90565b6138cc906138906137e1565b506138c7816138b76138b16fffffffffffffffffffffffffffffffff613134565b916102dd565b11156138c1613830565b9061383b565b613868565b90565b67ffffffffffffffff81116138e75760208091020190565b610251565b909291926139016138fc826138cf565b61028e565b938185526020808601920283019281841161393e57915b8383106139255750505050565b602080916139338486611cdf565b815201920191613918565b610896565b9080601f830112156139615781602061395e935191016138ec565b90565b61088e565b90602082820312613996575f82015167ffffffffffffffff81116139915761398e9201613943565b90565b61023f565b61023b565b5190565b60200190565b6139ae90610396565b9052565b906139bf816020936139a5565b0190565b60200190565b906139e66139e06139d98461399b565b8093612bc8565b9261399f565b905f5b8181106139f65750505090565b909192613a0f613a0960019286516139b2565b946139c3565b91019190916139e9565b613a2e9160208201915f8184039101526139c9565b90565b634e487b7160e01b5f52603260045260245ffd5b90613a4f8261399b565b811015613a60576020809102010190565b613a31565b613a6f9051610396565b90565b613a86613a81613a8b926102dd565b6106a8565b6102dd565b90565b613a9a613a9f91611721565b613a72565b90565b613aca613ac4613abf613af1955f95613ab9612e53565b50613c3f565b613ba7565b91611404565b613ae6637784c685613ada610231565b95869485938493611cd9565b835260048301613a19565b03915afa8015613b4c57613b1d613b2291613b27935f91613b2a575b50613b175f611eca565b90613a45565b613a65565b613a8e565b90565b613b4691503d805f833e613b3e8183610265565b810190613966565b5f613b0d565b611d0c565b606090565b90613b68613b63836138cf565b61028e565b918252565b369037565b90613b97613b7f83613b56565b92602080613b8d86936138cf565b9201910390613b6d565b565b90613ba390610396565b9052565b613baf613b51565b50613bde613bc5613bc06001612ef5565b613b72565b91613bd983613bd35f611eca565b90613a45565b613b99565b90565b613beb60026136f7565b90565b916020613c0f929493613c0860408201965f8301906103a5565b01906112a6565b565b60200190565b613c205f611eca565b90565b613c37613c32613c3c926102dd565b611033565b610396565b90565b613cdb613ce991613cc3613cee94613c556117a3565b5091613c89613c62613be1565b91613c7a613c6e610231565b93849260208401613bee565b60208201810382520382610265565b613c9b613c9582611ec6565b91613c11565b2091613cb4613ca8610231565b9384926020840161184c565b60208201810382520382610265565b613cd5613ccf82611ec6565b91613c11565b20613a8e565b613ce3613c17565b90612f32565b613c23565b90565b90613d28939291613d23613d03611e41565b613d15613d0e6115d9565b829061174d565b908115613d2a575b506128cc565b614004565b565b613d3c9150613d37611054565b61174d565b5f613d1d565b67ffffffffffffffff8111613d5a5760208091020190565b610251565b90613d71613d6c83613d42565b61028e565b918252565b613d80604061028e565b90565b613d8d60a061028e565b90565b5f90565b5f90565b613da0613d83565b9060208080808086613db0613d90565b815201613dbb613d90565b815201613dc6613d90565b815201613dd1613d90565b815201613ddc613d94565b81525050565b613dea613d98565b90565b613df5613d76565b9060208083613e02613de2565b815201613e0d613d94565b81525050565b613e1b613ded565b90565b5f5b828110613e2c57505050565b602090613e37613e13565b8184015201613e20565b90613e66613e4e83613d5f565b92602080613e5c8693613d42565b9201910390613e1e565b565b613e7c613e77613e8192611030565b6106a8565b610d8b565b90565b613e98613e93613e9d92610d8b565b6106a8565b6102dd565b90565b613ea990610d8b565b600160ff1b8114613eb9575f0390565b612f1e565b613ec8604061028e565b90565b52565b90613ed8906102dd565b9052565b5190565b90613eea82613edc565b811015613efb576020809102010190565b613a31565b60200190565b90608080613f5e93613f1e5f8201515f860190612982565b613f3060208201516020860190612982565b613f4260408201516040860190612982565b613f5460608201516060860190612982565b0151910190610777565b565b9060a06020613f8393613f795f8201515f860190613f06565b0151910190610777565b565b90613f928160c093613f60565b0190565b60200190565b90613fb9613fb3613fac84613edc565b8093612960565b92613f00565b905f5b818110613fc95750505090565b909192613fe2613fdc6001928651613f85565b94613f96565b9101919091613fbc565b6140019160208201915f818403910152613f9c565b90565b90614106926140b56140ff9261402261401d60026136f7565b613e41565b9661402e868390612e78565b614036612e53565b508161404a6140445f613e68565b91610d8b565b125f14614185576140909161406a6140656140709392613ea0565b613e84565b90612f32565b915b9161408761407e613ebe565b935f8501613ecb565b60208301613ece565b6140af87915f906140a96140a383611eca565b85613ee0565b52611eca565b90613ee0565b51506140d95f196140d06140c7613ebe565b935f8501613ecb565b60208301613ece565b6140f985916001906140f36140ed83612ef5565b85613ee0565b52612ef5565b90613ee0565b5150612945565b90637299aa3190823b156141805761413d926141325f8094614126610231565b96879586948593611cd9565b835260048301613fec565b03925af1801561417b5761414f575b50565b61416e905f3d8111614174575b6141668183610265565b810190612951565b5f61414c565b503d61415c565b611d0c565b611cd5565b61418e82613e84565b6141a061419a836102dd565b916102dd565b11155f146141c657614090916141b96141bf9291613e84565b90613150565b5b91614072565b50506140906141d45f611eca565b6141c0565b906141e5939291613cf1565b565b9061421e9392916142196141f9611e41565b61420b6142046115d9565b829061174d565b908115614220575b506128cc565b6142da565b565b614232915061422d611054565b61174d565b5f614213565b90505190614245826102ba565b565b919060a0838203126142b7576142b09061426160a061028e565b9361426e825f8301614238565b5f86015261427f8260208301614238565b60208601526142918260408301614238565b60408601526142a38260608301614238565b60608601526080016131ea565b6080830152565b610243565b9060a0828203126142d5576142d2915f01614247565b90565b61023b565b9160a061431a94926142f36142ee5f612e6b565b611404565b61430f632c3c9157614303610231565b98899485938493611cd9565b8352600483016103b2565b03915afa9081156143f25761436c945f926143c0575b5060a0906143456143405f612e6b565b611404565b614361632c3c9157614355610231565b98899485938493611cd9565b8352600483016103b2565b03915afa9182156143bb57614389945f9361438b575b50926141d9565b565b6143ad91935060a03d81116143b4575b6143a58183610265565b8101906142bc565b915f614382565b503d61439b565b611d0c565b60a09192506143e490823d81116143eb575b6143dc8183610265565b8101906142bc565b9190614330565b503d6143d2565b611d0c565b906144039392916141e7565b565b9061443c939291614437614417611e41565b6144296144226115d9565b829061174d565b90811561443e575b506128cc565b614527565b565b614450915061444b611054565b61174d565b5f614431565b67ffffffffffffffff811161446e5760208091020190565b610251565b919060c0838203126144ad576144a69061448d604061028e565b9361449a825f8301610303565b5f86015260a001610da2565b6020830152565b610243565b909291926144c76144c282614456565b61028e565b9381855260c060208601920283019281841161450657915b8383106144ec5750505050565b602060c0916144fb8486614473565b8152019201916144df565b610896565b6145169136916144b2565b90565b614524903690610303565b90565b61453a614546946145409294909361450b565b91614519565b916148ca565b565b90614554939291614405565b565b9061458d939291614588614568611e41565b61457a6145736115d9565b829061174d565b90811561458f575b506128cc565b6146d8565b565b6145a1915061459c611054565b61174d565b5f614582565b5090565b906145bd6145b883614456565b61028e565b918252565b6145cc604061028e565b90565b5f90565b6145db6145c2565b90602080836145e8613de2565b8152016145f36145cf565b81525050565b6146016145d3565b90565b5f5b82811061461257505050565b60209061461d6145f9565b8184015201614606565b9061464c614634836145ab565b926020806146428693614456565b9201910390614604565b565b614657906102dd565b5f1981146146655760010190565b612f1e565b919081101561467a576040020190565b613a31565b3561468981610736565b90565b3561469681610d8e565b90565b6146a3604061028e565b90565b906146b090610d8b565b9052565b5190565b906146c2826146b4565b8110156146d3576020809102010190565b613a31565b9290936146ee6146e98685906145a7565b614627565b916146f85f611eca565b5b8061471661471061470b8a89906145a7565b6102dd565b916102dd565b1015614816576147729060a08861473461472f5f612e6b565b611404565b6147676147525f61474c632c3c9157958d899161466a565b0161467f565b9261475b610231565b96879485938493611cd9565b8352600483016103b2565b03915afa918215614811576147de926147d7915f916147e3575b506147c46147a760206147a18d8c889161466a565b0161468c565b6147bb6147b2614699565b935f8501613ecb565b602083016146a6565b8683916147d183836146b8565b526146b8565b515061464e565b6146f9565b614804915060a03d811161480a575b6147fc8183610265565b8101906142bc565b5f61478c565b503d6147f2565b611d0c565b50919250935060a061485c9392916148356148305f612e6b565b611404565b614851632c3c9157614845610231565b97889485938493611cd9565b8352600483016103b2565b03915afa9182156148aa57614878935f9361487a575b506148ca565b565b61489c91935060a03d81116148a3575b6148948183610265565b8101906142bc565b915f614872565b503d61488a565b611d0c565b906148bb939291614556565b565b6148c79051610d8b565b90565b9290926148f16148ec6148dc866146b4565b6148e66001612ef5565b90612f32565b613e41565b916148fb5f611eca565b5b8061491761491161490c896146b4565b6102dd565b916102dd565b1015614a76576149f49061493a845f6149318a85906146b8565b51015190612e78565b614942612e53565b508761495b60206149548386906146b8565b51016148bd565b61496d6149675f613e68565b91610d8b565b125f146149f9576149a9906149a361499e61499960206149926149ed979589906146b8565b51016148bd565b613ea0565b613e84565b90612f32565b5b6149da5f6149b98b86906146b8565b510151916149d16149c8613ebe565b935f8501613ecb565b60208301613ece565b8683916149e78383613ee0565b52613ee0565b515061464e565b6148fc565b614a136020614a0c614a189386906146b8565b51016148bd565b613e84565b614a2a614a24836102dd565b916102dd565b11155f14614a6457614a5e6149ed91614a58614a536020614a4c8d88906146b8565b51016148bd565b613e84565b90613150565b5b6149aa565b506149ed614a715f611eca565b614a5f565b5093614abf90614ac6939490614aa45f19614a9b614a92613ebe565b945f8601613ecb565b60208401613ece565b614aae86916146b4565b91614ab98383613ee0565b52613ee0565b5150612945565b90637299aa3190823b15614b4057614afd92614af25f8094614ae6610231565b96879586948593611cd9565b835260048301613fec565b03925af18015614b3b57614b0f575b50565b614b2e905f3d8111614b34575b614b268183610265565b810190612951565b5f614b0c565b503d614b1c565b611d0c565b611cd5565b90614b7b9291614b76614b56611e41565b614b68614b616115d9565b829061174d565b908115614b7d575b506128cc565b614ce8565b565b614b8f9150614b8a611054565b61174d565b5f614b70565b614ba1614ba691611721565b610668565b90565b614bb39054614b95565b90565b60209181520190565b90565b5090565b90503590614bd38261305f565b565b50614be4906020810190614bc6565b90565b906020614c12614c1a93614c09614c005f830183614bd5565b5f860190613271565b82810190614bd5565b910190613271565b565b906020614c47614c4f93614c3e614c355f830183612bf2565b5f860190612bd4565b82810190614bc2565b910190614be7565b565b90614c5e81606093614c1c565b0190565b5090565b60600190565b91614c7a82614c8092614bb6565b92614bbf565b90815f905b828210614c93575050505090565b90919293614cb5614caf600192614caa8886614c62565b614c51565b95614c66565b920190929192614c85565b91614ce5939192614cd860408201945f83019061183f565b6020818503910152614c6c565b90565b90614cfb614cf66001614ba9565b6106d3565b614d0963f461804693612945565b919392813b15614d7f575f614d3191614d3c8296614d25610231565b98899788968795611cd9565b855260048501614cc0565b03925af18015614d7a57614d4e575b50565b614d6d905f3d8111614d73575b614d658183610265565b810190612951565b5f614d4b565b503d614d5b565b611d0c565b611cd5565b90614d8f9291614b45565b565b90614dc691614dc1614da1611e41565b614db3614dac6115d9565b829061174d565b908115614dc8575b506128cc565b614e03565b565b614dda9150614dd5611054565b61174d565b5f614dbb565b916020614e01929493614dfa60408201965f83019061183f565b01906112a6565b565b614e15614e106001614ba9565b6106d3565b9163e55156b5919092803b15614e9057614e425f8094614e4d614e36610231565b97889687958694611cd9565b845260048401614de0565b03925af18015614e8b57614e5f575b50565b614e7e905f3d8111614e84575b614e768183610265565b810190612951565b5f614e5c565b503d614e6c565b611d0c565b611cd5565b90614e9f91614d91565b565b90614ed691614ed1614eb1611e41565b614ec3614ebc6115d9565b829061174d565b908115614ed8575b506128cc565b614f20565b565b614eea9150614ee5611054565b61174d565b5f614ecb565b614ef9906110a0565b9052565b916020614f1e929493614f1760408201965f83019061183f565b0190614ef0565b565b614f32614f2d6001614ba9565b6106d3565b91630e4eecf8919092803b15614fad57614f5f5f8094614f6a614f53610231565b97889687958694611cd9565b845260048401614efd565b03925af18015614fa857614f7c575b50565b614f9b905f3d8111614fa1575b614f938183610265565b810190612951565b5f614f79565b503d614f89565b611d0c565b611cd5565b90614fbc91614ea1565b565b90614ff391614fee614fce611e41565b614fe0614fd961163d565b829061174d565b908115614ff5575b506125dc565b615030565b565b6150079150615002611054565b61174d565b5f614fe8565b91602061502e92949361502760408201965f83019061183f565b019061183f565b565b61504261503d6001614ba9565b6106d3565b9163c55b6bb7919092803b156150bd5761506f5f809461507a615063610231565b97889687958694611cd9565b84526004840161500d565b03925af180156150b85761508c575b50565b6150ab905f3d81116150b1575b6150a38183610265565b810190612951565b5f615089565b503d615099565b611d0c565b611cd5565b906150cc91614fbe565b565b6150e0906150da612ee1565b50612ee5565b90565b6150ee61010061028e565b90565b6150f96150e3565b906020808080808080808961510c613d94565b815201615117613d94565b815201615122613d94565b81520161512d613d94565b815201615138613d94565b815201615143613d94565b81520161514e613d94565b815201615159613d94565b81525050565b6151676150f1565b90565b90565b61518161517c6151869261516a565b6106a8565b6102dd565b90565b61519390516102dd565b90565b6151aa6151a56151af926136a1565b6106a8565b61304a565b90565b6151be6151c49161304a565b9161304a565b9003906fffffffffffffffffffffffffffffffff82116151e057565b612f1e565b906151ee61515f565b9161522f60c06152056152005f612e6b565b611404565b635c60e39a906152248592615218610231565b95869485938493611cd9565b8352600483016103b2565b03915afa908115615536575f91615508575b509061528360a06152596152545f612e6b565b611404565b632c3c915790615278859261526c610231565b95869485938493611cd9565b8352600483016103b2565b03915afa90811561550357615301916152f8915f916154d5575b50926152ea6152e46152b86152b15f612e6b565b879061334e565b9093916152de8d5f8101966152d860208301956060604085019401613ece565b90613ece565b90613ece565b90613ece565b6152f35f612e6b565b6155e4565b60808601613ece565b61531661530d5f611eca565b60e08601613ece565b61532260608201613191565b61533c6153366153315f6131ba565b6102ae565b916102ae565b0361540d575b506153da6153c86153e3926153585f8701615189565b61536a6153645f611eca565b916102dd565b145f146153e55761538761537d5f611eca565b5b60a08801613ece565b6153c26153bd61539960e08901615189565b926153b86153b260a0670de0b6b3a76400009301613127565b91615196565b6151b2565b613134565b906136d4565b6153d460a08601615189565b906136d4565b60c08401613ece565b565b6153876154086153f760408901615189565b6154025f8a01615189565b9061553b565b61537e565b602061542b61542661542160608501613191565b6131d2565b6131de565b638c00bf6b929061544e8594615459615442610231565b96879586948594611cd9565b8452600484016132ea565b03915afa9081156154d0576153e39261549a6154916153da946153c8945f916154a2575b5061548b6301e1338061516d565b90613732565b60e08801613ece565b925050615342565b6154c3915060203d81116154c9575b6154bb8183610265565b8101906131f9565b5f61547d565b503d6154b1565b611d0c565b6154f6915060a03d81116154fc575b6154ee8183610265565b8101906142bc565b5f61529d565b503d6154e4565b611d0c565b615529915060c03d811161552f575b6155218183610265565b810190613109565b5f615241565b503d615517565b611d0c565b9061555991615548612e53565b50906155526136c0565b909161555c565b90565b615576615595939261559092615570612e53565b50612fc0565b61558a836155846001612ef5565b90613150565b90612f32565b613009565b90565b90565b60ff1690565b6155b56155b06155ba92615598565b6106a8565b61559b565b90565b6155dc906155d66155d06155e19461559b565b91610396565b90610664565b610396565b90565b5f9061560c615606615601615633956155fb612e53565b506156c1565b613ba7565b91611404565b615628637784c68561561c610231565b95869485938493611cd9565b835260048301613a19565b03915afa9081156156a25761566861566361567d93615678935f91615680575b5061565d5f611eca565b90613a45565b613a65565b61567260806155a1565b906155bd565b613a8e565b90565b61569c91503d805f833e6156948183610265565b810190613966565b5f615653565b611d0c565b6156b16003613716565b90565b6156be60026136f7565b90565b61572c61571e615731926156d36117a3565b506157066156df6156a7565b916156f76156eb610231565b93849260208401613bee565b60208201810382520382610265565b61571861571282611ec6565b91613c11565b20613a8e565b6157266156b4565b90612f32565b613c23565b90565b61573f61010061028e565b90565b61574a615734565b906020808080808080808961575d613d94565b815201615768613d94565b815201615773613d94565b81520161577e613d94565b815201615789613d94565b815201615794613d94565b81520161579f613d94565b8152016157aa613d94565b81525050565b6157b8615742565b90565b919060608382031261580757615800906157d5606061028e565b936157e2825f83016131ea565b5f8601526157f38260208301613073565b6020860152604001613073565b6040830152565b610243565b9060608282031261582557615822915f016157bb565b90565b61023b565b91602061584b92949361584460408201965f8301906103a5565b019061183f565b565b615856906106ab565b90565b6158629061584d565b90565b61586e906106c7565b90565b90565b61588861588361588d92615871565b6106a8565b6102dd565b90565b6158a86ec097ce7bc90715b34b9f1000000000615874565b90565b91906158b56157b0565b926158f660a06158cc6158c75f612e6b565b611404565b632c3c9157906158eb85926158df610231565b95869485938493611cd9565b8352600483016103b2565b03915afa908115615c52575f91615c24575b5090606061591d6159185f612e6b565b611404565b6393c520629290615940869461594b615934610231565b96879586948594611cd9565b84526004840161582a565b03915afa908115615c1f575f91615bf1575b5061596a60408301613191565b61598461597e6159795f6131ba565b6102ae565b916102ae565b145f14615b41576080615ab6615ac394615a61615a595f615abd96615a538c6020615a4c8c6159b287611eca565b5b996159d36159cb6159c660408a01613127565b613134565b8e8701613ece565b6159fc6159f36159e48f8801615189565b8d6159ed615890565b9161302b565b60a08701613ece565b615a1c615a13615a0b8a612e6b565b848491615c78565b60608701613ece565b615a3b615a32615a2d868a01613127565b613134565b60408701613ece565b615a4488612e6b565b919091612ea2565b9101613ece565b01615189565b5f8a01613ece565b615a6d60a08901615189565b615a7f615a795f611eca565b916102dd565b145f14615b1857615a9c615a925f611eca565b5b60c08a01613ece565b615aa7838901615189565b90615ab0615890565b9161302b565b9201615189565b906136d4565b615acf60608401615189565b615ae1615adb5f611eca565b916102dd565b145f14615afa5750615af85f195b60e08401613ece565b565b615b13615af891615b0d60608601615189565b90615c57565b615aef565b615a9c615b3c615b2a60608b01615189565b615b3660a08c01615189565b9061553b565b615a93565b615b786020615b62615b5d615b5860408701613191565b615859565b615865565b63a035b1fe90615b70610231565b938492611cd9565b82528180615b88600482016104d2565b03915afa8015615bec57615ac394615a61615a595f615abd96615a538c6020615a4c8c615ab69a60809c8891615bbf575b506159b3565b615bdf9150843d8111615be5575b615bd78183610265565b8101906131f9565b5f615bb9565b503d615bcd565b611d0c565b615c12915060603d8111615c18575b615c0a8183610265565b81019061580c565b5f61595d565b503d615c00565b611d0c565b615c45915060a03d8111615c4b575b615c3d8183610265565b8101906142bc565b5f615908565b503d615c33565b611d0c565b90615c7591615c64612e53565b5090615c6e6136c0565b909161302b565b90565b90615ca690615ca0615cb594615c8c612e53565b50615c9683612ee5565b9085919091615cf3565b9261334e565b93925090509190919091615cb8565b90565b91615cea615cdb615cf094615ccb612e53565b5093615cd5612f11565b90612f32565b91615ce4612f76565b90612f32565b9161555c565b90565b615d1b615d15615d10615d42955f95615d0a612e53565b50615dbf565b613ba7565b91611404565b615d37637784c685615d2b610231565b95869485938493611cd9565b835260048301613a19565b03915afa8015615dad57615d7e615d79615d74615d8393615d88955f91615d8b575b50615d6e5f611eca565b90613a45565b613a65565b613a8e565b613868565b613134565b90565b615da791503d805f833e615d9f8183610265565b810190613966565b5f615d64565b611d0c565b615dbc6001612ef5565b90565b615e5b615e6991615e43615e6e94615dd56117a3565b5091615e09615de2613be1565b91615dfa615dee610231565b93849260208401613bee565b60208201810382520382610265565b615e1b615e1582611ec6565b91613c11565b2091615e34615e28610231565b9384926020840161184c565b60208201810382520382610265565b615e55615e4f82611ec6565b91613c11565b20613a8e565b615e63615db2565b90612f32565b613c23565b9056fea2646970667358221220a22232bfdc3a9e0decdaa178b9a6155dd02be7b15e95d1b327ebda4fda5a702e64736f6c63430008150033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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