HYPE Price: $37.16 (-3.72%)

Contract

0x517505be22D9068687334e69ae7a02fC77edf4Fc

Overview

HYPE Balance

HyperEvm LogoHyperEvm LogoHyperEvm Logo0 HYPE

HYPE Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Flow Caps74371802025-07-03 16:57:564 days ago1751561876IN
0x517505be...C77edf4Fc
0 HYPE0.000013670.1
Set Flow Caps74364682025-07-03 16:46:154 days ago1751561175IN
0x517505be...C77edf4Fc
0 HYPE0.000021170.19811306
Reallocate To65426812025-06-23 12:24:0614 days ago1750681446IN
0x517505be...C77edf4Fc
0 HYPE0.000026730.3
Reallocate To65421072025-06-23 12:14:4214 days ago1750680882IN
0x517505be...C77edf4Fc
0 HYPE0.000064160.72
Reallocate To65419892025-06-23 12:12:4614 days ago1750680766IN
0x517505be...C77edf4Fc
0 HYPE0.000027620.31
Reallocate To61952512025-06-19 13:24:3718 days ago1750339477IN
0x517505be...C77edf4Fc
0.0001 HYPE0.000003620.13
Reallocate To61951792025-06-19 13:23:2618 days ago1750339406IN
0x517505be...C77edf4Fc
0 HYPE0.00013281.49
Reallocate To61950672025-06-19 13:21:3618 days ago1750339296IN
0x517505be...C77edf4Fc
0.1 HYPE0.000111454
Reallocate To61949382025-06-19 13:19:2918 days ago1750339169IN
0x517505be...C77edf4Fc
0.1 HYPE0.000004730.17
Reallocate To61943692025-06-19 13:10:0918 days ago1750338609IN
0x517505be...C77edf4Fc
0.1 HYPE0.0003594312.9
Reallocate To61940922025-06-19 13:05:3718 days ago1750338337IN
0x517505be...C77edf4Fc
0.1 HYPE0.000013930.5
Reallocate To61937982025-06-19 13:00:4818 days ago1750338048IN
0x517505be...C77edf4Fc
0.1 HYPE0.00000390.14
Reallocate To61878952025-06-19 11:24:0118 days ago1750332241IN
0x517505be...C77edf4Fc
0 HYPE0.000004840.12
Set Flow Caps61141172025-06-18 15:14:3319 days ago1750259673IN
0x517505be...C77edf4Fc
0 HYPE0.000062420.35283025
Set Admin55082312025-06-11 17:39:3826 days ago1749663578IN
0x517505be...C77edf4Fc
0 HYPE0.000007660.14545155
Set Flow Caps53250632025-06-09 15:36:5228 days ago1749483412IN
0x517505be...C77edf4Fc
0 HYPE0.000010690.1
Set Flow Caps45371672025-05-30 19:34:2038 days ago1748633660IN
0x517505be...C77edf4Fc
0 HYPE0.000089760.90566458
Set Flow Caps45370922025-05-30 19:31:5638 days ago1748633516IN
0x517505be...C77edf4Fc
0 HYPE0.000010170.10075373
Set Flow Caps45370152025-05-30 19:29:2638 days ago1748633366IN
0x517505be...C77edf4Fc
0 HYPE0.00000830.13013828
Set Flow Caps45369782025-05-30 19:28:1438 days ago1748633294IN
0x517505be...C77edf4Fc
0 HYPE0.000008360.1
Set Flow Caps43948112025-05-27 15:02:1241 days ago1748358132IN
0x517505be...C77edf4Fc
0 HYPE0.01657891306.41553148
Set Flow Caps43947932025-05-27 15:01:3841 days ago1748358098IN
0x517505be...C77edf4Fc
0 HYPE0.01746825392.49205607
Set Flow Caps43947752025-05-27 15:01:0241 days ago1748358062IN
0x517505be...C77edf4Fc
0 HYPE0.01122283237.23923002
Set Flow Caps43947672025-05-27 15:00:4841 days ago1748358048IN
0x517505be...C77edf4Fc
0 HYPE0.00948778200.5620104
Set Flow Caps41869622025-05-22 23:08:3246 days ago1747955312IN
0x517505be...C77edf4Fc
0 HYPE0.00000890.10488614
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
19884292025-04-03 16:52:0095 days ago1743699120  Contract Creation0 HYPE
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PublicAllocator

Compiler Version
v0.8.24+commit.e11b9ed9

Optimization Enabled:
Yes with 999999 runs

Other Settings:
paris EvmVersion
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.24;

import {
    FlowCaps,
    FlowCapsConfig,
    Withdrawal,
    MAX_SETTABLE_FLOW_CAP,
    IPublicAllocatorStaticTyping,
    IPublicAllocatorBase
} from "./interfaces/IPublicAllocator.sol";
import {
    Id, IMorpho, IMetaMorpho, MarketAllocation, MarketParams
} from "../lib/metamorpho/src/interfaces/IMetaMorpho.sol";
import {Market} from "../lib/metamorpho/lib/morpho-blue/src/interfaces/IMorpho.sol";

import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {EventsLib} from "./libraries/EventsLib.sol";
import {UtilsLib} from "../lib/metamorpho/lib/morpho-blue/src/libraries/UtilsLib.sol";
import {MarketParamsLib} from "../lib/metamorpho/lib/morpho-blue/src/libraries/MarketParamsLib.sol";
import {MorphoBalancesLib} from "../lib/metamorpho/lib/morpho-blue/src/libraries/periphery/MorphoBalancesLib.sol";

/// @title PublicAllocator
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Publicly callable allocator for MetaMorpho vaults.
contract PublicAllocator is IPublicAllocatorStaticTyping {
    using MorphoBalancesLib for IMorpho;
    using MarketParamsLib for MarketParams;
    using UtilsLib for uint256;

    /* CONSTANTS */

    /// @inheritdoc IPublicAllocatorBase
    IMorpho public immutable MORPHO;

    /* STORAGE */

    /// @inheritdoc IPublicAllocatorBase
    mapping(address => address) public admin;
    /// @inheritdoc IPublicAllocatorBase
    mapping(address => uint256) public fee;
    /// @inheritdoc IPublicAllocatorBase
    mapping(address => uint256) public accruedFee;
    /// @inheritdoc IPublicAllocatorStaticTyping
    mapping(address => mapping(Id => FlowCaps)) public flowCaps;

    /* MODIFIER */

    /// @dev Reverts if the caller is not the admin nor the owner of this vault.
    modifier onlyAdminOrVaultOwner(address vault) {
        if (msg.sender != admin[vault] && msg.sender != IMetaMorpho(vault).owner()) {
            revert ErrorsLib.NotAdminNorVaultOwner();
        }
        _;
    }

    /* CONSTRUCTOR */

    /// @dev Initializes the contract.
    constructor(address morpho) {
        MORPHO = IMorpho(morpho);
    }

    /* ADMIN OR VAULT OWNER ONLY */

    /// @inheritdoc IPublicAllocatorBase
    function setAdmin(address vault, address newAdmin) external onlyAdminOrVaultOwner(vault) {
        if (admin[vault] == newAdmin) revert ErrorsLib.AlreadySet();
        admin[vault] = newAdmin;
        emit EventsLib.SetAdmin(msg.sender, vault, newAdmin);
    }

    /// @inheritdoc IPublicAllocatorBase
    function setFee(address vault, uint256 newFee) external onlyAdminOrVaultOwner(vault) {
        if (fee[vault] == newFee) revert ErrorsLib.AlreadySet();
        fee[vault] = newFee;
        emit EventsLib.SetFee(msg.sender, vault, newFee);
    }

    /// @inheritdoc IPublicAllocatorBase
    function setFlowCaps(address vault, FlowCapsConfig[] calldata config) external onlyAdminOrVaultOwner(vault) {
        for (uint256 i = 0; i < config.length; i++) {
            Id id = config[i].id;
            if (!IMetaMorpho(vault).config(id).enabled && (config[i].caps.maxIn > 0 || config[i].caps.maxOut > 0)) {
                revert ErrorsLib.MarketNotEnabled(id);
            }
            if (config[i].caps.maxIn > MAX_SETTABLE_FLOW_CAP || config[i].caps.maxOut > MAX_SETTABLE_FLOW_CAP) {
                revert ErrorsLib.MaxSettableFlowCapExceeded();
            }
            flowCaps[vault][id] = config[i].caps;
        }

        emit EventsLib.SetFlowCaps(msg.sender, vault, config);
    }

    /// @inheritdoc IPublicAllocatorBase
    function transferFee(address vault, address payable feeRecipient) external onlyAdminOrVaultOwner(vault) {
        uint256 claimed = accruedFee[vault];
        accruedFee[vault] = 0;
        feeRecipient.transfer(claimed);
        emit EventsLib.TransferFee(msg.sender, vault, claimed, feeRecipient);
    }

    /* PUBLIC */

    /// @inheritdoc IPublicAllocatorBase
    function reallocateTo(address vault, Withdrawal[] calldata withdrawals, MarketParams calldata supplyMarketParams)
        external
        payable
    {
        if (msg.value != fee[vault]) revert ErrorsLib.IncorrectFee();
        if (msg.value > 0) accruedFee[vault] += msg.value;

        if (withdrawals.length == 0) revert ErrorsLib.EmptyWithdrawals();

        Id supplyMarketId = supplyMarketParams.id();
        if (!IMetaMorpho(vault).config(supplyMarketId).enabled) revert ErrorsLib.MarketNotEnabled(supplyMarketId);

        MarketAllocation[] memory allocations = new MarketAllocation[](withdrawals.length + 1);
        uint128 totalWithdrawn;

        Id id;
        Id prevId;
        for (uint256 i = 0; i < withdrawals.length; i++) {
            prevId = id;
            id = withdrawals[i].marketParams.id();
            if (!IMetaMorpho(vault).config(id).enabled) revert ErrorsLib.MarketNotEnabled(id);
            uint128 withdrawnAssets = withdrawals[i].amount;
            if (withdrawnAssets == 0) revert ErrorsLib.WithdrawZero(id);

            if (Id.unwrap(id) <= Id.unwrap(prevId)) revert ErrorsLib.InconsistentWithdrawals();
            if (Id.unwrap(id) == Id.unwrap(supplyMarketId)) revert ErrorsLib.DepositMarketInWithdrawals();

            MORPHO.accrueInterest(withdrawals[i].marketParams);
            uint256 assets = MORPHO.expectedSupplyAssets(withdrawals[i].marketParams, address(vault));

            if (flowCaps[vault][id].maxOut < withdrawnAssets) revert ErrorsLib.MaxOutflowExceeded(id);
            if (assets < withdrawnAssets) revert ErrorsLib.NotEnoughSupply(id);

            flowCaps[vault][id].maxIn += withdrawnAssets;
            flowCaps[vault][id].maxOut -= withdrawnAssets;
            allocations[i].marketParams = withdrawals[i].marketParams;
            allocations[i].assets = assets - withdrawnAssets;

            totalWithdrawn += withdrawnAssets;

            emit EventsLib.PublicWithdrawal(msg.sender, vault, id, withdrawnAssets);
        }

        if (flowCaps[vault][supplyMarketId].maxIn < totalWithdrawn) revert ErrorsLib.MaxInflowExceeded(supplyMarketId);

        flowCaps[vault][supplyMarketId].maxIn -= totalWithdrawn;
        flowCaps[vault][supplyMarketId].maxOut += totalWithdrawn;
        allocations[withdrawals.length].marketParams = supplyMarketParams;
        allocations[withdrawals.length].assets = type(uint256).max;

        IMetaMorpho(vault).reallocate(allocations);

        emit EventsLib.PublicReallocateTo(msg.sender, vault, supplyMarketId, totalWithdrawn);
    }
}

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

File 4 of 20 : 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: 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.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: 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: 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))
        }
    }
}

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

File 16 of 20 : 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 Funds can be recovered by enabling this market again and withdrawing from it (using `reallocate`),
    /// but funds will be distributed pro-rata to the shares at the time of withdrawal, not at the time of removal.
    /// @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.
    /// @notice The allocator can withdraw from any market, even if it's not in the withdraw queue, as long as the loan
    /// token of the market is the same as the vault's asset.
    /// @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`.
    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: 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.5.0;

import {
    IMetaMorpho,
    IMorpho,
    MarketAllocation,
    Id,
    MarketParams
} from "../../lib/metamorpho/src/interfaces/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);
}

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

import {Id} from "../../lib/metamorpho/src/interfaces/IMetaMorpho.sol";

/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing error messages.
library ErrorsLib {
    /// @notice Thrown when the `msg.sender` is not the admin nor the owner of the vault.
    error NotAdminNorVaultOwner();

    /// @notice Thrown when the reallocation fee given is wrong.
    error IncorrectFee();

    /// @notice Thrown when the value is already set.
    error AlreadySet();

    /// @notice Thrown when `withdrawals` is empty.
    error EmptyWithdrawals();

    /// @notice Thrown when `withdrawals` contains a duplicate or is not sorted.
    error InconsistentWithdrawals();

    /// @notice Thrown when the deposit market is in `withdrawals`.
    error DepositMarketInWithdrawals();

    /// @notice Thrown when attempting to reallocate or set flows to non-zero values for a non-enabled market.
    error MarketNotEnabled(Id id);

    /// @notice Thrown when attempting to withdraw zero of a market.
    error WithdrawZero(Id id);

    /// @notice Thrown when attempting to set max inflow/outflow above the MAX_SETTABLE_FLOW_CAP.
    error MaxSettableFlowCapExceeded();

    /// @notice Thrown when attempting to withdraw more than the available supply of a market.
    error NotEnoughSupply(Id id);

    /// @notice Thrown when attempting to withdraw more than the max outflow of a market.
    error MaxOutflowExceeded(Id id);

    /// @notice Thrown when attempting to supply more than the max inflow of a market.
    error MaxInflowExceeded(Id id);
}

File 20 of 20 : EventsLib.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {FlowCapsConfig, Id} from "../interfaces/IPublicAllocator.sol";

/// @title EventsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing events.
library EventsLib {
    /// @notice Emitted during a public reallocation for each withdrawn-from market.
    event PublicWithdrawal(address indexed sender, address indexed vault, Id indexed id, uint256 withdrawnAssets);

    /// @notice Emitted at the end of a public reallocation.
    event PublicReallocateTo(
        address indexed sender, address indexed vault, Id indexed supplyMarketId, uint256 suppliedAssets
    );

    /// @notice Emitted when the admin is set for a vault.
    event SetAdmin(address indexed sender, address indexed vault, address admin);

    /// @notice Emitted when the fee is set for a vault.
    event SetFee(address indexed sender, address indexed vault, uint256 fee);

    /// @notice Emitted when the fee is transfered for a vault.
    event TransferFee(address indexed sender, address indexed vault, uint256 amount, address indexed feeRecipient);

    /// @notice Emitted when the flow caps are set for a vault.
    event SetFlowCaps(address indexed sender, address indexed vault, FlowCapsConfig[] config);
}

Settings
{
  "evmVersion": "paris",
  "libraries": {},
  "metadata": {
    "appendCBOR": true,
    "bytecodeHash": "none",
    "useLiteralContent": false
  },
  "optimizer": {
    "enabled": true,
    "runs": 999999
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "remappings": [
    "solmate/=lib/bundler3/lib/permit2/lib/solmate/",
    "@openzeppelin/contracts/=lib/metamorpho-1.1/lib/openzeppelin-contracts/contracts/",
    "bundler3/=lib/bundler3/",
    "ds-test/=lib/metamorpho-1.1/lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/metamorpho-1.1/lib/erc4626-tests/",
    "forge-gas-snapshot/=lib/bundler3/lib/permit2/lib/forge-gas-snapshot/src/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/morpho-blue/lib/halmos-cheatcodes/src/",
    "metamorpho-1.1/=lib/metamorpho-1.1/",
    "metamorpho/=lib/public-allocator/lib/metamorpho/",
    "morpho-blue-irm/=lib/morpho-blue-irm/src/",
    "morpho-blue-oracles/=lib/morpho-blue-oracles/src/",
    "morpho-blue/=lib/morpho-blue/",
    "murky/=lib/universal-rewards-distributor/lib/murky/src/",
    "openzeppelin-contracts/=lib/metamorpho-1.1/lib/openzeppelin-contracts/",
    "openzeppelin/=lib/universal-rewards-distributor/lib/openzeppelin-contracts/contracts/",
    "permit2/=lib/bundler3/lib/permit2/",
    "pre-liquidation/=lib/pre-liquidation/src/",
    "public-allocator/=lib/public-allocator/src/",
    "safe-smart-account/=lib/safe-smart-account/",
    "universal-rewards-distributor/=lib/universal-rewards-distributor/"
  ],
  "viaIR": true
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"morpho","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadySet","type":"error"},{"inputs":[],"name":"DepositMarketInWithdrawals","type":"error"},{"inputs":[],"name":"EmptyWithdrawals","type":"error"},{"inputs":[],"name":"InconsistentWithdrawals","type":"error"},{"inputs":[],"name":"IncorrectFee","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"MarketNotEnabled","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"MaxInflowExceeded","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"MaxOutflowExceeded","type":"error"},{"inputs":[],"name":"MaxSettableFlowCapExceeded","type":"error"},{"inputs":[],"name":"NotAdminNorVaultOwner","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"NotEnoughSupply","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"WithdrawZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"Id","name":"supplyMarketId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"suppliedAssets","type":"uint256"}],"name":"PublicReallocateTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"Id","name":"id","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"withdrawnAssets","type":"uint256"}],"name":"PublicWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","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"}],"indexed":false,"internalType":"struct FlowCapsConfig[]","name":"config","type":"tuple[]"}],"name":"SetFlowCaps","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"TransferFee","type":"event"},{"inputs":[],"name":"MORPHO","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accruedFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"Id","name":"","type":"bytes32"}],"name":"flowCaps","outputs":[{"internalType":"uint128","name":"maxIn","type":"uint128"},{"internalType":"uint128","name":"maxOut","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","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":"uint128","name":"amount","type":"uint128"}],"internalType":"struct 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":"supplyMarketParams","type":"tuple"}],"name":"reallocateTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","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":"config","type":"tuple[]"}],"name":"setFlowCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address payable","name":"feeRecipient","type":"address"}],"name":"transferFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a03461008857601f6122ce38819003918201601f19168301916001600160401b0383118484101761008d5780849260209460405283398101031261008857516001600160a01b038116908190036100885760805260405161222a90816100a48239608051818181610e1801528181610f2501528181611053015281816110dd0152611baa0152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe608080604052600436101561001357600080fd5b60003560e01c9081630e4eecf814611bce575080633acb562414611b5f57806363a846f814611af95780636fcca69b14611a94578063833947fd1461089757806391b114b2146108325780639dbcd5b9146107a6578063c55b6bb71461064b578063e55156b5146104f55763f46180461461008d57600080fd5b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f0576100c4611d1b565b6024906024359067ffffffffffffffff8083116104f057366023840112156104f05782600401359081116104f0576024830192606090602436918385020101116104f05773ffffffffffffffffffffffffffffffffffffffff80931694856000526020936000855280604060002054163314159081610479575b5061044f576000939291935b8281106101e95750506040519281838501848652526040840194926000905b83821061019a5787337f709e1cb4b0ac458eb1c1a9c708e841ee963b229247afbf1437bd39e01ae4aa14888a0389a3005b90919293958380600192893581526fffffffffffffffffffffffffffffffff806101c5878d016120ac565b16868301526101d660408c016120ac565b1660408201520197019493920190610169565b6101f88184889795969761209c565b356040517fcc718f76000000000000000000000000000000000000000000000000000000008152816004820152848185818c5afa8015610443578791600091610416575b50015115806103c8575b610398576fffffffffffffffffffffffffffffffff91906f7fffffffffffffffffffffffffffffff80846102858a61027f878c8f61209c565b0161200b565b1611908484898c8515610378575b505050505061034e576001926103046040610344936102b3868b8e61209c565b908d60005260038c5282600020906000528b5281600020936102d68c830161200b565b167fffffffffffffffffffffffffffffffff000000000000000000000000000000008554161784550161200b565b6fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b019392919361014a565b60046040517fb9860d64000000000000000000000000000000000000000000000000000000008152fd5b61038c939495509161027f9160409361209c565b1611388484898c610293565b9050604051907f6113d8c70000000000000000000000000000000000000000000000000000000082526004820152fd5b506fffffffffffffffffffffffffffffffff806103ea8861027f868a8d61209c565b1615908115916103fb575b50610246565b905061040d604061027f85898c61209c565b161515386103f5565b6104369150863d881161043c575b61042e8183611dc7565b810190611f3b565b3861023c565b503d610424565b6040513d6000823e3d90fd5b60046040517f26d3a7bf000000000000000000000000000000000000000000000000000000008152fd5b90506040517f8da5cb5b00000000000000000000000000000000000000000000000000000000815285816004818b5afa908115610443576000916104c3575b50163314153861013e565b6104e39150863d88116104e9575b6104db8183611dc7565b810190611e08565b386104b8565b503d6104d1565b600080fd5b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05761052c611d1b565b60243573ffffffffffffffffffffffffffffffffffffffff809216918260005260209060008252806040600020541633141590816105e4575b5061044f57826000526001815281604060002054146105ba577f44a6d70a601a6f8a85c075467e9d7245897140cbf6dd505c9d9d764459f5fb64908360005260018152826040600020556040519283523392a3005b60046040517fa741a045000000000000000000000000000000000000000000000000000000008152fd5b90506040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481885afa9081156104435760009161062e575b501633141584610565565b6106459150833d85116104e9576104db8183611dc7565b85610623565b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057610682611d1b565b60243573ffffffffffffffffffffffffffffffffffffffff918282168092036104f0578216918260005260209060008252806040600020541633141580610740575b61044f578290846000526000835260406000205416146105ba577fc51248b3e510a1244e01043dffdc0132d10194bd4506382cbcf83d05f6ec57ef9083600052600081526040600020837fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790556040519283523392a3005b506040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481885afa8015610443578291600091610789575b50163314156106c4565b6107a09150843d86116104e9576104db8183611dc7565b8661077f565b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff6107f2611d1b565b1660005260036020526040600020602435600052602052604080600020548151906fffffffffffffffffffffffffffffffff8116825260801c6020820152f35b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff61087e611d1b565b1660005260026020526020604060002054604051908152f35b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f0576108c9611d1b565b60243567ffffffffffffffff81116104f057366023820112156104f05767ffffffffffffffff8160040135116104f05736602460c0836004013502830101116104f05760a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126104f05773ffffffffffffffffffffffffffffffffffffffff821660005260016020526040600020543403611a6a5734611a34575b806004013515611a0a5760a061097c36611e41565b20916040517fcc718f7600000000000000000000000000000000000000000000000000000000815283600482015260608160248173ffffffffffffffffffffffffffffffffffffffff86165afa8015610443576020916000916119eb575b5001511561194d57600482013560018101919082106113c7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610a36610a2084611fb4565b93610a2e6040519586611dc7565b808552611fb4565b0160005b81811061199d5750506000926000805b82600401358210610d2c57505073ffffffffffffffffffffffffffffffffffffffff821660005260036020526040600020856000526020526fffffffffffffffffffffffffffffffff93846040600020541694808216809610610cfb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92610b74602093610b989373ffffffffffffffffffffffffffffffffffffffff88166000526003865260406000208b60005286526040600020907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825491610b328582851661204c565b16911617905573ffffffffffffffffffffffffffffffffffffffff87166000526003855260406000208a6000528552610304604060002091825460801c612028565b610b8281600401358761207b565b51610b8c36611e41565b9052600401358561207b565b51015273ffffffffffffffffffffffffffffffffffffffff81163b156104f05760405180927f7299aa310000000000000000000000000000000000000000000000000000000082526024820160206004840152815180915260206044840192019060005b818110610c8957505050908060009203818373ffffffffffffffffffffffffffffffffffffffff86165af19182156104435773ffffffffffffffffffffffffffffffffffffffff92610c7a575b5060405192835216907ff8ae80b0854dfc3c73d3eb4b6160df1996a5859e6c1d11d10f3980a7f469199160203392a4005b610c8390611d97565b84610c49565b91935091602060c0600192828751610ce68382516080809173ffffffffffffffffffffffffffffffffffffffff80825116855280602083015116602086015280604083015116604086015260608201511660608501520151910152565b015160a0820152019401910191859392610bfc565b602487604051907f2e581b4e0000000000000000000000000000000000000000000000000000000082526004820152fd5b949060a0610d4b36610d4684876004013560248901611fcc565b611ed9565b20916040517fcc718f7600000000000000000000000000000000000000000000000000000000815283600482015260608160248173ffffffffffffffffffffffffffffffffffffffff8a165afa80156104435760209160009161197e575b5001511561194d57610dc860a061027f84876004013560248901611fcc565b966fffffffffffffffffffffffffffffffff88161561191c578311156118f2578783146118c857610e0182856004013560248701611fcc565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b156104f0576080604051917f151c1ade00000000000000000000000000000000000000000000000000000000835273ffffffffffffffffffffffffffffffffffffffff610e8582611d3e565b16600484015273ffffffffffffffffffffffffffffffffffffffff610eac60208301611d3e565b16602484015273ffffffffffffffffffffffffffffffffffffffff610ed360408301611d3e565b16604484015273ffffffffffffffffffffffffffffffffffffffff610efa60608301611d3e565b1660648401520135608482015260008160a4818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af18015610443576118b9575b50610f6936610d4684876004013560248901611fcc565b60a08120604051602081019182526002604082015260408152610f8b81611dab565b519020604051602081019173ffffffffffffffffffffffffffffffffffffffff89168352604082015260408152610fc181611dab565b519020604051610fd081611d5f565b6001815260208101916020368437610fe78261206e565b526040519182917f7784c685000000000000000000000000000000000000000000000000000000008352602483019060206004850152518091526044830191906000905b80821061189d575050509080600092038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa8015610443576000906117f1575b61108d915061206e565b519060a0812090604051917f5c60e39a000000000000000000000000000000000000000000000000000000008352600483015260c08260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156104435760009261170b575b5060808201906111316fffffffffffffffffffffffffffffffff8351164261208f565b90811515806116ec575b806116c9575b6113f6575b5050506fffffffffffffffffffffffffffffffff602081835116920151166001820182116113c757620f4240810181116113c757611196926001620f424061119193019301906120e6565b6121e4565b73ffffffffffffffffffffffffffffffffffffffff861660005260036020526040600020846000526020526fffffffffffffffffffffffffffffffff881660406000205460801c10611396576fffffffffffffffffffffffffffffffff88168110611365576113078884936112f387948b6112d86112cf896112c98f9c8f73ffffffffffffffffffffffffffffffffffffffff60019f8e828216600052600360205260406000209060005260205260406000208d7fffffffffffffffffffffffffffffffff000000000000000000000000000000006fffffffffffffffffffffffffffffffff61128a845493828516612028565b16911617905516600052600360205260406000208d6000526020526112bb60406000206103048d825460801c61204c565b602481600401359101611fcc565b9361207b565b51913690611ed9565b90526fffffffffffffffffffffffffffffffff84169061208f565b60206112ff878d61207b565b510152612028565b976fffffffffffffffffffffffffffffffff6040519116815273ffffffffffffffffffffffffffffffffffffffff8716907f6218cdb9e8efb3d0e8136d32c91d9446eaf19e2e486bc67dfcb3d574ca60d50460203392a40190610a4a565b602484604051907f8c4bfb140000000000000000000000000000000000000000000000000000000082526004820152fd5b602484604051907fad5f61d30000000000000000000000000000000000000000000000000000000082526004820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6114a46101648273ffffffffffffffffffffffffffffffffffffffff60606020950151166fffffffffffffffffffffffffffffffff60405197889586947f8c00bf6b00000000000000000000000000000000000000000000000000000000865260048601906080809173ffffffffffffffffffffffffffffffffffffffff80825116855280602083015116602086015280604083015116604086015260608201511660608501520151910152565b818a511660a485015281878b01511660c48501528160408b01511660e48501528160608b01511661010485015251166101248301526fffffffffffffffffffffffffffffffff60a0890151166101448301525afa91821561044357600092611693575b509061157a61153061156f936fffffffffffffffffffffffffffffffff604087015116936120e6565b91611574671bc16d674ec8000061154785806120e6565b0493670de0b6b3a764000095856729a2241af62c0000611568848a996120e6565b0492611e34565b611e34565b906120e6565b046fffffffffffffffffffffffffffffffff6115a4611598836120f9565b82604087015116612028565b1660408401526fffffffffffffffffffffffffffffffff6115d06115c7836120f9565b82865116612028565b1683526fffffffffffffffffffffffffffffffff60a0840151168015611146576115f9916120e6565b04611617816fffffffffffffffffffffffffffffffff84511661208f565b6fffffffffffffffffffffffffffffffff60208401511690620f4240820182116113c7576001810181116113c757611674611685926111916fffffffffffffffffffffffffffffffff95620f4240600161167996019301906120e6565b6120f9565b82602085015116612028565b1660208201528a8080611146565b91506020823d6020116116c1575b816116ae60209383611dc7565b810103126104f05790519061157a611507565b3d91506116a1565b5073ffffffffffffffffffffffffffffffffffffffff6060820151161515611141565b506fffffffffffffffffffffffffffffffff604085015116151561113b565b90915060c0813d60c0116117e9575b8161172760c09383611dc7565b810103126104f057604051908160c081011067ffffffffffffffff60c0840111176117ba5760a06117ae9160c08401604052611762816120c9565b8452611770602082016120c9565b6020850152611781604082016120c9565b6040850152611792606082016120c9565b60608501526117a3608082016120c9565b6080850152016120c9565b60a0820152908b61110e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3d915061171a565b503d90816000823e6118038282611dc7565b60208183810103126104f05780519167ffffffffffffffff83116104f057808201601f8484010112156104f057828201519161183e83611fb4565b9361184c6040519586611dc7565b8385526020850192820160208560051b8385010101116104f057602081830101925b60208560051b8385010101841061188d57505050505061108d90611083565b835181526020938401930161186e565b919350916020806001928651815201940192018493929161102b565b6118c290611d97565b88610f52565b60046040517f898ca719000000000000000000000000000000000000000000000000000000008152fd5b60046040517fc9527748000000000000000000000000000000000000000000000000000000008152fd5b602484604051907f9565ed900000000000000000000000000000000000000000000000000000000082526004820152fd5b602483604051907f6113d8c70000000000000000000000000000000000000000000000000000000082526004820152fd5b611997915060603d60601161043c5761042e8183611dc7565b8a610da9565b6020906040516119ac81611d5f565b6040516119b881611d7b565b60008152600084820152600060408201526000606082015260006080820152815260008382015282828701015201610a3a565b611a04915060603d60601161043c5761042e8183611dc7565b856109da565b60046040517f76da5945000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff821660005260026020526040600020611a63348254611e34565b9055610967565b60046040517fcd3cb2bb000000000000000000000000000000000000000000000000000000008152fd5b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff611ae0611d1b565b1660005260016020526020604060002054604051908152f35b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057602073ffffffffffffffffffffffffffffffffffffffff80611b48611d1b565b166000526000825260406000205416604051908152f35b346104f05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057611c05611d1b565b906024359173ffffffffffffffffffffffffffffffffffffffff908184168094036104f057811691826000526020916000835280604060002054163314159182611cb5575b505061044f578160005260028152604060002090600082549255600082858115611cab575b600092839283928392f115610443577f6ab9f885fa0bfd2af57586f4cdde83bbfc79294d0cd2d61d4b31e9a3d1be6e2c906040519283523392a4005b6108fc9250611c6f565b9091507f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481875afa90811561044357600091611cfe575b50163314158480611c4a565b611d159150833d85116104e9576104db8183611dc7565b85611cf2565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104f057565b359073ffffffffffffffffffffffffffffffffffffffff821682036104f057565b6040810190811067ffffffffffffffff8211176117ba57604052565b60a0810190811067ffffffffffffffff8211176117ba57604052565b67ffffffffffffffff81116117ba57604052565b6060810190811067ffffffffffffffff8211176117ba57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176117ba57604052565b908160209103126104f0575173ffffffffffffffffffffffffffffffffffffffff811681036104f05790565b919082018092116113c757565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc60a09101126104f05760405190611e7882611d7b565b8173ffffffffffffffffffffffffffffffffffffffff60443581811681036104f057825260643581811681036104f057602083015260843581811681036104f057604083015260a43590811681036104f0576060820152608060c435910152565b91908260a09103126104f057604051611ef181611d7b565b6080808294611eff81611d3e565b8452611f0d60208201611d3e565b6020850152611f1e60408201611d3e565b6040850152611f2f60608201611d3e565b60608501520135910152565b908160609103126104f0576040519067ffffffffffffffff9060608301828111848210176117ba57604052805176ffffffffffffffffffffffffffffffffffffffffffffff811681036104f057835260208101519081151582036104f0576040916020850152015190811681036104f057604082015290565b67ffffffffffffffff81116117ba5760051b60200190565b9190811015611fdc5760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036104f05790565b9190916fffffffffffffffffffffffffffffffff808094169116019182116113c757565b6fffffffffffffffffffffffffffffffff91821690821603919082116113c757565b805115611fdc5760200190565b8051821015611fdc5760209160051b010190565b919082039182116113c757565b9190811015611fdc576060020190565b35906fffffffffffffffffffffffffffffffff821682036104f057565b51906fffffffffffffffffffffffffffffffff821682036104f057565b818102929181159184041417156113c757565b6fffffffffffffffffffffffffffffffff9060405161211781611d5f565b601481526020907f6d61782075696e7431323820657863656564656400000000000000000000000060208201528383116121515750501690565b60405180927f08c379a00000000000000000000000000000000000000000000000000000000082526020600483015282519283602484015260005b8481106121cd575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b81810183015186820160440152859350820161218c565b81156121ee570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea164736f6c6343000818000a00000000000000000000000068e37de8d93d3496ae143f2e900490f6280c57cd

Deployed Bytecode

0x608080604052600436101561001357600080fd5b60003560e01c9081630e4eecf814611bce575080633acb562414611b5f57806363a846f814611af95780636fcca69b14611a94578063833947fd1461089757806391b114b2146108325780639dbcd5b9146107a6578063c55b6bb71461064b578063e55156b5146104f55763f46180461461008d57600080fd5b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f0576100c4611d1b565b6024906024359067ffffffffffffffff8083116104f057366023840112156104f05782600401359081116104f0576024830192606090602436918385020101116104f05773ffffffffffffffffffffffffffffffffffffffff80931694856000526020936000855280604060002054163314159081610479575b5061044f576000939291935b8281106101e95750506040519281838501848652526040840194926000905b83821061019a5787337f709e1cb4b0ac458eb1c1a9c708e841ee963b229247afbf1437bd39e01ae4aa14888a0389a3005b90919293958380600192893581526fffffffffffffffffffffffffffffffff806101c5878d016120ac565b16868301526101d660408c016120ac565b1660408201520197019493920190610169565b6101f88184889795969761209c565b356040517fcc718f76000000000000000000000000000000000000000000000000000000008152816004820152848185818c5afa8015610443578791600091610416575b50015115806103c8575b610398576fffffffffffffffffffffffffffffffff91906f7fffffffffffffffffffffffffffffff80846102858a61027f878c8f61209c565b0161200b565b1611908484898c8515610378575b505050505061034e576001926103046040610344936102b3868b8e61209c565b908d60005260038c5282600020906000528b5281600020936102d68c830161200b565b167fffffffffffffffffffffffffffffffff000000000000000000000000000000008554161784550161200b565b6fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b019392919361014a565b60046040517fb9860d64000000000000000000000000000000000000000000000000000000008152fd5b61038c939495509161027f9160409361209c565b1611388484898c610293565b9050604051907f6113d8c70000000000000000000000000000000000000000000000000000000082526004820152fd5b506fffffffffffffffffffffffffffffffff806103ea8861027f868a8d61209c565b1615908115916103fb575b50610246565b905061040d604061027f85898c61209c565b161515386103f5565b6104369150863d881161043c575b61042e8183611dc7565b810190611f3b565b3861023c565b503d610424565b6040513d6000823e3d90fd5b60046040517f26d3a7bf000000000000000000000000000000000000000000000000000000008152fd5b90506040517f8da5cb5b00000000000000000000000000000000000000000000000000000000815285816004818b5afa908115610443576000916104c3575b50163314153861013e565b6104e39150863d88116104e9575b6104db8183611dc7565b810190611e08565b386104b8565b503d6104d1565b600080fd5b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05761052c611d1b565b60243573ffffffffffffffffffffffffffffffffffffffff809216918260005260209060008252806040600020541633141590816105e4575b5061044f57826000526001815281604060002054146105ba577f44a6d70a601a6f8a85c075467e9d7245897140cbf6dd505c9d9d764459f5fb64908360005260018152826040600020556040519283523392a3005b60046040517fa741a045000000000000000000000000000000000000000000000000000000008152fd5b90506040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481885afa9081156104435760009161062e575b501633141584610565565b6106459150833d85116104e9576104db8183611dc7565b85610623565b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057610682611d1b565b60243573ffffffffffffffffffffffffffffffffffffffff918282168092036104f0578216918260005260209060008252806040600020541633141580610740575b61044f578290846000526000835260406000205416146105ba577fc51248b3e510a1244e01043dffdc0132d10194bd4506382cbcf83d05f6ec57ef9083600052600081526040600020837fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790556040519283523392a3005b506040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481885afa8015610443578291600091610789575b50163314156106c4565b6107a09150843d86116104e9576104db8183611dc7565b8661077f565b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff6107f2611d1b565b1660005260036020526040600020602435600052602052604080600020548151906fffffffffffffffffffffffffffffffff8116825260801c6020820152f35b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff61087e611d1b565b1660005260026020526020604060002054604051908152f35b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f0576108c9611d1b565b60243567ffffffffffffffff81116104f057366023820112156104f05767ffffffffffffffff8160040135116104f05736602460c0836004013502830101116104f05760a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126104f05773ffffffffffffffffffffffffffffffffffffffff821660005260016020526040600020543403611a6a5734611a34575b806004013515611a0a5760a061097c36611e41565b20916040517fcc718f7600000000000000000000000000000000000000000000000000000000815283600482015260608160248173ffffffffffffffffffffffffffffffffffffffff86165afa8015610443576020916000916119eb575b5001511561194d57600482013560018101919082106113c7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610a36610a2084611fb4565b93610a2e6040519586611dc7565b808552611fb4565b0160005b81811061199d5750506000926000805b82600401358210610d2c57505073ffffffffffffffffffffffffffffffffffffffff821660005260036020526040600020856000526020526fffffffffffffffffffffffffffffffff93846040600020541694808216809610610cfb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92610b74602093610b989373ffffffffffffffffffffffffffffffffffffffff88166000526003865260406000208b60005286526040600020907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825491610b328582851661204c565b16911617905573ffffffffffffffffffffffffffffffffffffffff87166000526003855260406000208a6000528552610304604060002091825460801c612028565b610b8281600401358761207b565b51610b8c36611e41565b9052600401358561207b565b51015273ffffffffffffffffffffffffffffffffffffffff81163b156104f05760405180927f7299aa310000000000000000000000000000000000000000000000000000000082526024820160206004840152815180915260206044840192019060005b818110610c8957505050908060009203818373ffffffffffffffffffffffffffffffffffffffff86165af19182156104435773ffffffffffffffffffffffffffffffffffffffff92610c7a575b5060405192835216907ff8ae80b0854dfc3c73d3eb4b6160df1996a5859e6c1d11d10f3980a7f469199160203392a4005b610c8390611d97565b84610c49565b91935091602060c0600192828751610ce68382516080809173ffffffffffffffffffffffffffffffffffffffff80825116855280602083015116602086015280604083015116604086015260608201511660608501520151910152565b015160a0820152019401910191859392610bfc565b602487604051907f2e581b4e0000000000000000000000000000000000000000000000000000000082526004820152fd5b949060a0610d4b36610d4684876004013560248901611fcc565b611ed9565b20916040517fcc718f7600000000000000000000000000000000000000000000000000000000815283600482015260608160248173ffffffffffffffffffffffffffffffffffffffff8a165afa80156104435760209160009161197e575b5001511561194d57610dc860a061027f84876004013560248901611fcc565b966fffffffffffffffffffffffffffffffff88161561191c578311156118f2578783146118c857610e0182856004013560248701611fcc565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000068e37de8d93d3496ae143f2e900490f6280c57cd163b156104f0576080604051917f151c1ade00000000000000000000000000000000000000000000000000000000835273ffffffffffffffffffffffffffffffffffffffff610e8582611d3e565b16600484015273ffffffffffffffffffffffffffffffffffffffff610eac60208301611d3e565b16602484015273ffffffffffffffffffffffffffffffffffffffff610ed360408301611d3e565b16604484015273ffffffffffffffffffffffffffffffffffffffff610efa60608301611d3e565b1660648401520135608482015260008160a4818373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000068e37de8d93d3496ae143f2e900490f6280c57cd165af18015610443576118b9575b50610f6936610d4684876004013560248901611fcc565b60a08120604051602081019182526002604082015260408152610f8b81611dab565b519020604051602081019173ffffffffffffffffffffffffffffffffffffffff89168352604082015260408152610fc181611dab565b519020604051610fd081611d5f565b6001815260208101916020368437610fe78261206e565b526040519182917f7784c685000000000000000000000000000000000000000000000000000000008352602483019060206004850152518091526044830191906000905b80821061189d575050509080600092038173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000068e37de8d93d3496ae143f2e900490f6280c57cd165afa8015610443576000906117f1575b61108d915061206e565b519060a0812090604051917f5c60e39a000000000000000000000000000000000000000000000000000000008352600483015260c08260248173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000068e37de8d93d3496ae143f2e900490f6280c57cd165afa9182156104435760009261170b575b5060808201906111316fffffffffffffffffffffffffffffffff8351164261208f565b90811515806116ec575b806116c9575b6113f6575b5050506fffffffffffffffffffffffffffffffff602081835116920151166001820182116113c757620f4240810181116113c757611196926001620f424061119193019301906120e6565b6121e4565b73ffffffffffffffffffffffffffffffffffffffff861660005260036020526040600020846000526020526fffffffffffffffffffffffffffffffff881660406000205460801c10611396576fffffffffffffffffffffffffffffffff88168110611365576113078884936112f387948b6112d86112cf896112c98f9c8f73ffffffffffffffffffffffffffffffffffffffff60019f8e828216600052600360205260406000209060005260205260406000208d7fffffffffffffffffffffffffffffffff000000000000000000000000000000006fffffffffffffffffffffffffffffffff61128a845493828516612028565b16911617905516600052600360205260406000208d6000526020526112bb60406000206103048d825460801c61204c565b602481600401359101611fcc565b9361207b565b51913690611ed9565b90526fffffffffffffffffffffffffffffffff84169061208f565b60206112ff878d61207b565b510152612028565b976fffffffffffffffffffffffffffffffff6040519116815273ffffffffffffffffffffffffffffffffffffffff8716907f6218cdb9e8efb3d0e8136d32c91d9446eaf19e2e486bc67dfcb3d574ca60d50460203392a40190610a4a565b602484604051907f8c4bfb140000000000000000000000000000000000000000000000000000000082526004820152fd5b602484604051907fad5f61d30000000000000000000000000000000000000000000000000000000082526004820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6114a46101648273ffffffffffffffffffffffffffffffffffffffff60606020950151166fffffffffffffffffffffffffffffffff60405197889586947f8c00bf6b00000000000000000000000000000000000000000000000000000000865260048601906080809173ffffffffffffffffffffffffffffffffffffffff80825116855280602083015116602086015280604083015116604086015260608201511660608501520151910152565b818a511660a485015281878b01511660c48501528160408b01511660e48501528160608b01511661010485015251166101248301526fffffffffffffffffffffffffffffffff60a0890151166101448301525afa91821561044357600092611693575b509061157a61153061156f936fffffffffffffffffffffffffffffffff604087015116936120e6565b91611574671bc16d674ec8000061154785806120e6565b0493670de0b6b3a764000095856729a2241af62c0000611568848a996120e6565b0492611e34565b611e34565b906120e6565b046fffffffffffffffffffffffffffffffff6115a4611598836120f9565b82604087015116612028565b1660408401526fffffffffffffffffffffffffffffffff6115d06115c7836120f9565b82865116612028565b1683526fffffffffffffffffffffffffffffffff60a0840151168015611146576115f9916120e6565b04611617816fffffffffffffffffffffffffffffffff84511661208f565b6fffffffffffffffffffffffffffffffff60208401511690620f4240820182116113c7576001810181116113c757611674611685926111916fffffffffffffffffffffffffffffffff95620f4240600161167996019301906120e6565b6120f9565b82602085015116612028565b1660208201528a8080611146565b91506020823d6020116116c1575b816116ae60209383611dc7565b810103126104f05790519061157a611507565b3d91506116a1565b5073ffffffffffffffffffffffffffffffffffffffff6060820151161515611141565b506fffffffffffffffffffffffffffffffff604085015116151561113b565b90915060c0813d60c0116117e9575b8161172760c09383611dc7565b810103126104f057604051908160c081011067ffffffffffffffff60c0840111176117ba5760a06117ae9160c08401604052611762816120c9565b8452611770602082016120c9565b6020850152611781604082016120c9565b6040850152611792606082016120c9565b60608501526117a3608082016120c9565b6080850152016120c9565b60a0820152908b61110e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3d915061171a565b503d90816000823e6118038282611dc7565b60208183810103126104f05780519167ffffffffffffffff83116104f057808201601f8484010112156104f057828201519161183e83611fb4565b9361184c6040519586611dc7565b8385526020850192820160208560051b8385010101116104f057602081830101925b60208560051b8385010101841061188d57505050505061108d90611083565b835181526020938401930161186e565b919350916020806001928651815201940192018493929161102b565b6118c290611d97565b88610f52565b60046040517f898ca719000000000000000000000000000000000000000000000000000000008152fd5b60046040517fc9527748000000000000000000000000000000000000000000000000000000008152fd5b602484604051907f9565ed900000000000000000000000000000000000000000000000000000000082526004820152fd5b602483604051907f6113d8c70000000000000000000000000000000000000000000000000000000082526004820152fd5b611997915060603d60601161043c5761042e8183611dc7565b8a610da9565b6020906040516119ac81611d5f565b6040516119b881611d7b565b60008152600084820152600060408201526000606082015260006080820152815260008382015282828701015201610a3a565b611a04915060603d60601161043c5761042e8183611dc7565b856109da565b60046040517f76da5945000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff821660005260026020526040600020611a63348254611e34565b9055610967565b60046040517fcd3cb2bb000000000000000000000000000000000000000000000000000000008152fd5b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff611ae0611d1b565b1660005260016020526020604060002054604051908152f35b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057602073ffffffffffffffffffffffffffffffffffffffff80611b48611d1b565b166000526000825260406000205416604051908152f35b346104f05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000068e37de8d93d3496ae143f2e900490f6280c57cd168152f35b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057611c05611d1b565b906024359173ffffffffffffffffffffffffffffffffffffffff908184168094036104f057811691826000526020916000835280604060002054163314159182611cb5575b505061044f578160005260028152604060002090600082549255600082858115611cab575b600092839283928392f115610443577f6ab9f885fa0bfd2af57586f4cdde83bbfc79294d0cd2d61d4b31e9a3d1be6e2c906040519283523392a4005b6108fc9250611c6f565b9091507f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481875afa90811561044357600091611cfe575b50163314158480611c4a565b611d159150833d85116104e9576104db8183611dc7565b85611cf2565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104f057565b359073ffffffffffffffffffffffffffffffffffffffff821682036104f057565b6040810190811067ffffffffffffffff8211176117ba57604052565b60a0810190811067ffffffffffffffff8211176117ba57604052565b67ffffffffffffffff81116117ba57604052565b6060810190811067ffffffffffffffff8211176117ba57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176117ba57604052565b908160209103126104f0575173ffffffffffffffffffffffffffffffffffffffff811681036104f05790565b919082018092116113c757565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc60a09101126104f05760405190611e7882611d7b565b8173ffffffffffffffffffffffffffffffffffffffff60443581811681036104f057825260643581811681036104f057602083015260843581811681036104f057604083015260a43590811681036104f0576060820152608060c435910152565b91908260a09103126104f057604051611ef181611d7b565b6080808294611eff81611d3e565b8452611f0d60208201611d3e565b6020850152611f1e60408201611d3e565b6040850152611f2f60608201611d3e565b60608501520135910152565b908160609103126104f0576040519067ffffffffffffffff9060608301828111848210176117ba57604052805176ffffffffffffffffffffffffffffffffffffffffffffff811681036104f057835260208101519081151582036104f0576040916020850152015190811681036104f057604082015290565b67ffffffffffffffff81116117ba5760051b60200190565b9190811015611fdc5760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036104f05790565b9190916fffffffffffffffffffffffffffffffff808094169116019182116113c757565b6fffffffffffffffffffffffffffffffff91821690821603919082116113c757565b805115611fdc5760200190565b8051821015611fdc5760209160051b010190565b919082039182116113c757565b9190811015611fdc576060020190565b35906fffffffffffffffffffffffffffffffff821682036104f057565b51906fffffffffffffffffffffffffffffffff821682036104f057565b818102929181159184041417156113c757565b6fffffffffffffffffffffffffffffffff9060405161211781611d5f565b601481526020907f6d61782075696e7431323820657863656564656400000000000000000000000060208201528383116121515750501690565b60405180927f08c379a00000000000000000000000000000000000000000000000000000000082526020600483015282519283602484015260005b8481106121cd575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b81810183015186820160440152859350820161218c565b81156121ee570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea164736f6c6343000818000a

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

00000000000000000000000068e37de8d93d3496ae143f2e900490f6280c57cd

-----Decoded View---------------
Arg [0] : morpho (address): 0x68e37dE8d93d3496ae143F2E900490f6280C57cD

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000068e37de8d93d3496ae143f2e900490f6280c57cd


Block Transaction 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
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.