Contract 0x367A6E3fAC26F17A62bab8a8a24b5EE1d21EffFe 1

  Note: Our ether balance display is temporarily unavailable. Please check back later.

Contract Overview

Balance:
Txn Hash
Method
Block
From
To
Value
0x5bf899ae8e5dc683ef55368fdaf735b62ab1dfbb25d66a86bd94a14afb5f63cb0x60806040124131992022-06-17 13:49:48174 days 17 hrs ago0x42a7ba5dbd5a0f032e3c50f67a7934b19e776ede IN  Create: PositionsManager0 Ether0.014914 3
[ Download CSV Export 
Latest 17 internal transactions
Parent Txn Hash Block From To Value
0xa946b77bfa7df423c2d01bda0269e684396eefdec52db27d43ebbde33e1e34a9127735472022-08-12 15:09:48118 days 15 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0xb9c4d4c0c59cf8bc751f94b2f63969a26e65ad2ed6be5fa66c45f992f61aa9cb127735452022-08-12 15:09:24118 days 15 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x1e6980c6e4351cf4ac20221f9c93ab160406763981e30eef5b6b7c1f199d8f42127735402022-08-12 15:08:24118 days 15 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x50d26c75e844d4253e22791f1d1dd22d2d5f16f83b62b5466be0e6f7b84128f9127735362022-08-12 15:07:36118 days 15 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x5160f97e718a23aa50d6b8d6fa36c87e5a96dc8975f31fa78f22a5941fe32916127734822022-08-12 14:56:00118 days 16 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x814d1d57bec952019875cedf983f0a5ecf686b6987abc9566e657dd0ba420522127734722022-08-12 14:54:00118 days 16 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0xba6068973d7867529c7f636f31edf8df007af2452d113c0ac25303528c132078127734652022-08-12 14:52:36118 days 16 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x897f7563a96b2bf60be6c04b0c1a2b7e42cfd8d57306b91a16702fc8af08653e127734462022-08-12 14:48:48118 days 16 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x8edd5728c1cef5718db94cbba3b7be19fb36b3f90966a24120b8194a34df5646127734372022-08-12 14:46:24118 days 16 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x7d444f3ce25e1eacafdbeddfe34a9439cc074dfd5a849c9dbaf5d9e4abcddbb6125983582022-07-15 14:45:00146 days 16 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x805f01b8aa437df46b0a08f90eeb8a3475be13e4d4428f4979e10d9668004c56125969802022-07-15 10:01:12146 days 21 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0xfed62a9156aef60850f5cfb4e5c75b9be6821f195387da8ae19c0e47d6e41754125969752022-07-15 10:00:12146 days 21 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x94b77cabb3ee7ec72b0271967b6d31fe6ec142d6aaf33ecacbfa20e2d0a483b1125965952022-07-15 8:42:24146 days 22 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x9f295415b5eae41d86706e68b4a465875c5cd7b80459841efa15263072a9ca81125450542022-07-07 15:03:00154 days 16 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x3122df762b73971d1f9d80aea571936650cba929cdd3daeb1d95e9b946038139125450492022-07-07 15:02:00154 days 16 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x255b483bfb052666747570f7df65f9a95e9cb7d9ba5bb52111ad12b4f2cdadc2125447192022-07-07 13:50:12154 days 17 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
0x7463ecdcbe20d12ed8ceb63e4220b411e88f34f3ee985fe0ce6dece0d1852943125447152022-07-07 13:49:24154 days 17 hrs ago 0x68416e5225668e1af28f859eb1f2982c5b0756d40x367a6e3fac26f17a62bab8a8a24b5ee1d21efffe0 Ether
[ Download CSV Export 
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.

Contract Source Code Verified (Exact Match)

Contract Name:
PositionsManager

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 23 : PositionsManager.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "./interfaces/IPositionsManager.sol";
import "./interfaces/IWETH.sol";

import "./MatchingEngine.sol";

/// @title PositionsManager.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @notice Main Logic of Morpho Protocol, implementation of the 5 main functionalities: supply, borrow, withdraw, repay and liquidate.
contract PositionsManager is IPositionsManager, MatchingEngine {
    using DoubleLinkedList for DoubleLinkedList.List;
    using SafeTransferLib for ERC20;
    using CompoundMath for uint256;

    /// EVENTS ///

    /// @notice Emitted when a supply happens.
    /// @param _supplier The address of the account sending funds.
    /// @param _onBehalf The address of the account whose positions will be updated.
    /// @param _poolTokenAddress The address of the market where assets are supplied into.
    /// @param _amount The amount of assets supplied (in underlying).
    /// @param _balanceOnPool The supply balance on pool after update.
    /// @param _balanceInP2P The supply balance in peer-to-peer after update.
    event Supplied(
        address indexed _supplier,
        address indexed _onBehalf,
        address indexed _poolTokenAddress,
        uint256 _amount,
        uint256 _balanceOnPool,
        uint256 _balanceInP2P
    );

    /// @notice Emitted when a borrow happens.
    /// @param _borrower The address of the borrower.
    /// @param _poolTokenAddress The address of the market where assets are borrowed.
    /// @param _amount The amount of assets borrowed (in underlying).
    /// @param _balanceOnPool The borrow balance on pool after update.
    /// @param _balanceInP2P The borrow balance in peer-to-peer after update
    event Borrowed(
        address indexed _borrower,
        address indexed _poolTokenAddress,
        uint256 _amount,
        uint256 _balanceOnPool,
        uint256 _balanceInP2P
    );

    /// @notice Emitted when a withdrawal happens.
    /// @param _supplier The address of the supplier whose supply is withdrawn.
    /// @param _receiver The address receiving the tokens.
    /// @param _poolTokenAddress The address of the market from where assets are withdrawn.
    /// @param _amount The amount of assets withdrawn (in underlying).
    /// @param _balanceOnPool The supply balance on pool after update.
    /// @param _balanceInP2P The supply balance in peer-to-peer after update.
    event Withdrawn(
        address indexed _supplier,
        address indexed _receiver,
        address indexed _poolTokenAddress,
        uint256 _amount,
        uint256 _balanceOnPool,
        uint256 _balanceInP2P
    );

    /// @notice Emitted when a repayment happens.
    /// @param _repayer The address of the account repaying the debt.
    /// @param _onBehalf The address of the account whose debt is repaid.
    /// @param _poolTokenAddress The address of the market where assets are repaid.
    /// @param _amount The amount of assets repaid (in underlying).
    /// @param _balanceOnPool The borrow balance on pool after update.
    /// @param _balanceInP2P The borrow balance in peer-to-peer after update.
    event Repaid(
        address indexed _repayer,
        address indexed _onBehalf,
        address indexed _poolTokenAddress,
        uint256 _amount,
        uint256 _balanceOnPool,
        uint256 _balanceInP2P
    );

    /// @notice Emitted when a liquidation happens.
    /// @param _liquidator The address of the liquidator.
    /// @param _liquidated The address of the liquidated.
    /// @param _poolTokenBorrowedAddress The address of the borrowed asset.
    /// @param _amountRepaid The amount of borrowed asset repaid (in underlying).
    /// @param _poolTokenCollateralAddress The address of the collateral asset seized.
    /// @param _amountSeized The amount of collateral asset seized (in underlying).
    event Liquidated(
        address _liquidator,
        address indexed _liquidated,
        address indexed _poolTokenBorrowedAddress,
        uint256 _amountRepaid,
        address indexed _poolTokenCollateralAddress,
        uint256 _amountSeized
    );

    /// @notice Emitted when the borrow peer-to-peer delta is updated.
    /// @param _poolTokenAddress The address of the market.
    /// @param _p2pBorrowDelta The borrow peer-to-peer delta after update.
    event P2PBorrowDeltaUpdated(address indexed _poolTokenAddress, uint256 _p2pBorrowDelta);

    /// @notice Emitted when the supply peer-to-peer delta is updated.
    /// @param _poolTokenAddress The address of the market.
    /// @param _p2pSupplyDelta The supply peer-to-peer delta after update.
    event P2PSupplyDeltaUpdated(address indexed _poolTokenAddress, uint256 _p2pSupplyDelta);

    /// @notice Emitted when the supply and borrow peer-to-peer amounts are updated.
    /// @param _poolTokenAddress The address of the market.
    /// @param _p2pSupplyAmount The supply peer-to-peer amount after update.
    /// @param _p2pBorrowAmount The borrow peer-to-peer amount after update.
    event P2PAmountsUpdated(
        address indexed _poolTokenAddress,
        uint256 _p2pSupplyAmount,
        uint256 _p2pBorrowAmount
    );

    /// ERRORS ///

    /// @notice Thrown when the amount repaid during the liquidation is above what is allowed to be repaid.
    error AmountAboveWhatAllowedToRepay();

    /// @notice Thrown when the borrow on Compound failed.
    error BorrowOnCompoundFailed();

    /// @notice Thrown when the redeem on Compound failed .
    error RedeemOnCompoundFailed();

    /// @notice Thrown when the repay on Compound failed.
    error RepayOnCompoundFailed();

    /// @notice Thrown when the mint on Compound failed.
    error MintOnCompoundFailed();

    /// @notice Thrown when user is not a member of the market.
    error UserNotMemberOfMarket();

    /// @notice Thrown when the user does not have enough remaining collateral to withdraw.
    error UnauthorisedWithdraw();

    /// @notice Thrown when the positions of the user is not liquidatable.
    error UnauthorisedLiquidate();

    /// @notice Thrown when the user does not have enough collateral for the borrow.
    error UnauthorisedBorrow();

    /// @notice Thrown when the amount desired for a withdrawal is too small.
    error WithdrawTooSmall();

    /// @notice Thrown when the address is zero.
    error AddressIsZero();

    /// @notice Thrown when the amount is equal to 0.
    error AmountIsZero();

    /// @notice Thrown when a user tries to repay its debt after borrowing in the same block.
    error SameBlockBorrowRepay();

    /// STRUCTS ///

    // Struct to avoid stack too deep.
    struct SupplyVars {
        uint256 remainingToSupply;
        uint256 poolBorrowIndex;
        uint256 toRepay;
    }

    // Struct to avoid stack too deep.
    struct WithdrawVars {
        uint256 remainingToWithdraw;
        uint256 maxGasForMatching;
        uint256 poolSupplyIndex;
        uint256 p2pSupplyIndex;
        uint256 withdrawable;
        uint256 toWithdraw;
        ERC20 underlyingToken;
        ICToken poolToken;
    }

    // Struct to avoid stack too deep.
    struct RepayVars {
        uint256 maxGasForMatching;
        uint256 remainingToRepay;
        uint256 maxToRepayOnPool;
        uint256 poolBorrowIndex;
        uint256 p2pSupplyIndex;
        uint256 p2pBorrowIndex;
        uint256 borrowedOnPool;
        uint256 feeToRepay;
        uint256 toRepay;
    }

    // Struct to avoid stack too deep.
    struct LiquidateVars {
        uint256 collateralPrice;
        uint256 borrowBalance;
        uint256 supplyBalance;
        uint256 borrowedPrice;
        uint256 amountToSeize;
    }

    /// LOGIC ///

    /// @dev Implements supply logic.
    /// @param _poolTokenAddress The address of the pool token the user wants to interact with.
    /// @param _supplier The address of the account sending funds.
    /// @param _onBehalf The address of the account whose positions will be updated.
    /// @param _amount The amount of token (in underlying).
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    function supplyLogic(
        address _poolTokenAddress,
        address _supplier,
        address _onBehalf,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) external {
        if (_onBehalf == address(0)) revert AddressIsZero();
        if (_amount == 0) revert AmountIsZero();
        _updateP2PIndexes(_poolTokenAddress);

        _enterMarketIfNeeded(_poolTokenAddress, _onBehalf);
        ERC20 underlyingToken = _getUnderlying(_poolTokenAddress);
        underlyingToken.safeTransferFrom(_supplier, address(this), _amount);

        Types.Delta storage delta = deltas[_poolTokenAddress];
        SupplyVars memory vars;
        vars.poolBorrowIndex = ICToken(_poolTokenAddress).borrowIndex();
        vars.remainingToSupply = _amount;

        /// Supply in peer-to-peer ///

        // Match borrow peer-to-peer delta first if any.
        if (delta.p2pBorrowDelta > 0) {
            uint256 deltaInUnderlying = delta.p2pBorrowDelta.mul(vars.poolBorrowIndex);
            if (deltaInUnderlying > vars.remainingToSupply) {
                vars.toRepay += vars.remainingToSupply;
                delta.p2pBorrowDelta -= vars.remainingToSupply.div(vars.poolBorrowIndex);
                vars.remainingToSupply = 0;
            } else {
                vars.toRepay += deltaInUnderlying;
                delta.p2pBorrowDelta = 0;
                vars.remainingToSupply -= deltaInUnderlying;
            }
            emit P2PBorrowDeltaUpdated(_poolTokenAddress, delta.p2pBorrowDelta);
        }

        // Match pool borrowers if any.
        if (
            vars.remainingToSupply > 0 &&
            !p2pDisabled[_poolTokenAddress] &&
            borrowersOnPool[_poolTokenAddress].getHead() != address(0)
        ) {
            (uint256 matched, ) = _matchBorrowers(
                _poolTokenAddress,
                vars.remainingToSupply,
                _maxGasForMatching
            ); // In underlying.

            if (matched > 0) {
                vars.toRepay += matched;
                vars.remainingToSupply -= matched;
                delta.p2pBorrowAmount += matched.div(p2pBorrowIndex[_poolTokenAddress]);
            }
        }

        if (vars.toRepay > 0) {
            uint256 toAddInP2P = vars.toRepay.div(p2pSupplyIndex[_poolTokenAddress]);

            delta.p2pSupplyAmount += toAddInP2P;
            supplyBalanceInOf[_poolTokenAddress][_onBehalf].inP2P += toAddInP2P;
            _repayToPool(_poolTokenAddress, underlyingToken, vars.toRepay); // Reverts on error.

            emit P2PAmountsUpdated(_poolTokenAddress, delta.p2pSupplyAmount, delta.p2pBorrowAmount);
        }

        /// Supply on pool ///

        if (vars.remainingToSupply > 0) {
            supplyBalanceInOf[_poolTokenAddress][_onBehalf].onPool += vars.remainingToSupply.div(
                ICToken(_poolTokenAddress).exchangeRateStored() // Exchange rate has already been updated.
            ); // In scaled balance.
            _supplyToPool(_poolTokenAddress, underlyingToken, vars.remainingToSupply); // Reverts on error.
        }

        _updateSupplierInDS(_poolTokenAddress, _onBehalf);

        emit Supplied(
            _supplier,
            _onBehalf,
            _poolTokenAddress,
            _amount,
            supplyBalanceInOf[_poolTokenAddress][_onBehalf].onPool,
            supplyBalanceInOf[_poolTokenAddress][_onBehalf].inP2P
        );
    }

    /// @dev Implements borrow logic.
    /// @param _poolTokenAddress The address of the market the user wants to interact with.
    /// @param _amount The amount of token (in underlying).
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    function borrowLogic(
        address _poolTokenAddress,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) external {
        if (_amount == 0) revert AmountIsZero();
        _updateP2PIndexes(_poolTokenAddress);

        _enterMarketIfNeeded(_poolTokenAddress, msg.sender);
        lastBorrowBlock[msg.sender] = block.number;

        if (_isLiquidatable(msg.sender, _poolTokenAddress, 0, _amount)) revert UnauthorisedBorrow();
        ERC20 underlyingToken = _getUnderlying(_poolTokenAddress);
        uint256 remainingToBorrow = _amount;
        uint256 toWithdraw;
        Types.Delta storage delta = deltas[_poolTokenAddress];
        uint256 poolSupplyIndex = ICToken(_poolTokenAddress).exchangeRateStored(); // Exchange rate has already been updated.
        uint256 withdrawable = ICToken(_poolTokenAddress).balanceOfUnderlying(address(this)); // The balance on pool.

        /// Borrow in peer-to-peer ///

        // Match supply peer-to-peer delta first if any.
        if (delta.p2pSupplyDelta > 0) {
            uint256 deltaInUnderlying = delta.p2pSupplyDelta.mul(poolSupplyIndex);
            if (deltaInUnderlying > remainingToBorrow || deltaInUnderlying > withdrawable) {
                uint256 matchedDelta = CompoundMath.min(remainingToBorrow, withdrawable);
                toWithdraw += matchedDelta;
                delta.p2pSupplyDelta -= matchedDelta.div(poolSupplyIndex);
                remainingToBorrow -= matchedDelta;
            } else {
                toWithdraw += deltaInUnderlying;
                delta.p2pSupplyDelta = 0;
                remainingToBorrow -= deltaInUnderlying;
            }

            emit P2PSupplyDeltaUpdated(_poolTokenAddress, delta.p2pSupplyDelta);
        }

        // Match pool suppliers if any.
        if (
            remainingToBorrow > 0 &&
            !p2pDisabled[_poolTokenAddress] &&
            suppliersOnPool[_poolTokenAddress].getHead() != address(0)
        ) {
            (uint256 matched, ) = _matchSuppliers(
                _poolTokenAddress,
                CompoundMath.min(remainingToBorrow, withdrawable - toWithdraw),
                _maxGasForMatching
            ); // In underlying.

            if (matched > 0) {
                toWithdraw += matched;
                remainingToBorrow -= matched;
                deltas[_poolTokenAddress].p2pSupplyAmount += matched.div(
                    p2pSupplyIndex[_poolTokenAddress]
                );
            }
        }

        if (toWithdraw > 0) {
            uint256 toAddInP2P = toWithdraw.div(p2pBorrowIndex[_poolTokenAddress]); // In peer-to-peer unit.

            deltas[_poolTokenAddress].p2pBorrowAmount += toAddInP2P;
            borrowBalanceInOf[_poolTokenAddress][msg.sender].inP2P += toAddInP2P;
            emit P2PAmountsUpdated(_poolTokenAddress, delta.p2pSupplyAmount, delta.p2pBorrowAmount);

            // If this value is equal to 0 the withdraw will revert on Compound.
            if (toWithdraw.div(poolSupplyIndex) > 0)
                _withdrawFromPool(_poolTokenAddress, toWithdraw); // Reverts on error.
        }

        /// Borrow on pool ///

        if (remainingToBorrow > 0) {
            borrowBalanceInOf[_poolTokenAddress][msg.sender].onPool += remainingToBorrow.div(
                ICToken(_poolTokenAddress).borrowIndex()
            ); // In cdUnit.
            _borrowFromPool(_poolTokenAddress, remainingToBorrow);
        }

        _updateBorrowerInDS(_poolTokenAddress, msg.sender);
        underlyingToken.safeTransfer(msg.sender, _amount);

        emit Borrowed(
            msg.sender,
            _poolTokenAddress,
            _amount,
            borrowBalanceInOf[_poolTokenAddress][msg.sender].onPool,
            borrowBalanceInOf[_poolTokenAddress][msg.sender].inP2P
        );
    }

    /// @dev Implements withdraw logic with security checks.
    /// @param _poolTokenAddress The address of the market the user wants to interact with.
    /// @param _amount The amount of token (in underlying).
    /// @param _supplier The address of the supplier.
    /// @param _receiver The address of the user who will receive the tokens.
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    function withdrawLogic(
        address _poolTokenAddress,
        uint256 _amount,
        address _supplier,
        address _receiver,
        uint256 _maxGasForMatching
    ) external {
        if (_amount == 0) revert AmountIsZero();
        if (!userMembership[_poolTokenAddress][_supplier]) revert UserNotMemberOfMarket();

        _updateP2PIndexes(_poolTokenAddress);
        uint256 toWithdraw = Math.min(
            _getUserSupplyBalanceInOf(_poolTokenAddress, _supplier),
            _amount
        );

        if (_isLiquidatable(_supplier, _poolTokenAddress, toWithdraw, 0))
            revert UnauthorisedWithdraw();

        _safeWithdrawLogic(_poolTokenAddress, toWithdraw, _supplier, _receiver, _maxGasForMatching);
    }

    /// @dev Implements repay logic with security checks.
    /// @param _poolTokenAddress The address of the market the user wants to interact with.
    /// @param _repayer The address of the account repaying the debt.
    /// @param _onBehalf The address of the account whose debt is repaid.
    /// @param _amount The amount of token (in underlying).
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    function repayLogic(
        address _poolTokenAddress,
        address _repayer,
        address _onBehalf,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) external {
        if (_amount == 0) revert AmountIsZero();
        if (!userMembership[_poolTokenAddress][_onBehalf]) revert UserNotMemberOfMarket();

        _updateP2PIndexes(_poolTokenAddress);
        uint256 toRepay = Math.min(
            _getUserBorrowBalanceInOf(_poolTokenAddress, _onBehalf),
            _amount
        );

        _safeRepayLogic(_poolTokenAddress, _repayer, _onBehalf, toRepay, _maxGasForMatching);
    }

    /// @notice Liquidates a position.
    /// @param _poolTokenBorrowedAddress The address of the pool token the liquidator wants to repay.
    /// @param _poolTokenCollateralAddress The address of the collateral pool token the liquidator wants to seize.
    /// @param _borrower The address of the borrower to liquidate.
    /// @param _amount The amount of token (in underlying) to repay.
    function liquidateLogic(
        address _poolTokenBorrowedAddress,
        address _poolTokenCollateralAddress,
        address _borrower,
        uint256 _amount
    ) external {
        if (
            !userMembership[_poolTokenBorrowedAddress][_borrower] ||
            !userMembership[_poolTokenCollateralAddress][_borrower]
        ) revert UserNotMemberOfMarket();

        _updateP2PIndexes(_poolTokenBorrowedAddress);
        _updateP2PIndexes(_poolTokenCollateralAddress);

        if (!_isLiquidatable(_borrower, address(0), 0, 0)) revert UnauthorisedLiquidate();

        LiquidateVars memory vars;
        vars.borrowBalance = _getUserBorrowBalanceInOf(_poolTokenBorrowedAddress, _borrower);

        if (_amount > vars.borrowBalance.mul(comptroller.closeFactorMantissa()))
            revert AmountAboveWhatAllowedToRepay(); // Same mechanism as Compound. Liquidator cannot repay more than part of the debt (cf close factor on Compound).

        _safeRepayLogic(_poolTokenBorrowedAddress, msg.sender, _borrower, _amount, 0);

        ICompoundOracle compoundOracle = ICompoundOracle(comptroller.oracle());
        vars.collateralPrice = compoundOracle.getUnderlyingPrice(_poolTokenCollateralAddress);
        vars.borrowedPrice = compoundOracle.getUnderlyingPrice(_poolTokenBorrowedAddress);
        if (vars.collateralPrice == 0 || vars.borrowedPrice == 0) revert CompoundOracleFailed();

        // Compute the amount of collateral tokens to seize. This is the minimum between the repaid value plus the liquidation incentive and the available supply.
        vars.amountToSeize = Math.min(
            _amount.mul(comptroller.liquidationIncentiveMantissa()).mul(vars.borrowedPrice).div(
                vars.collateralPrice
            ),
            _getUserSupplyBalanceInOf(_poolTokenCollateralAddress, _borrower)
        );

        _safeWithdrawLogic(
            _poolTokenCollateralAddress,
            vars.amountToSeize,
            _borrower,
            msg.sender,
            0
        );

        emit Liquidated(
            msg.sender,
            _borrower,
            _poolTokenBorrowedAddress,
            _amount,
            _poolTokenCollateralAddress,
            vars.amountToSeize
        );
    }

    /// INTERNAL ///

    /// @dev Implements withdraw logic without security checks.
    /// @param _poolTokenAddress The address of the market the user wants to interact with.
    /// @param _amount The amount of token (in underlying).
    /// @param _supplier The address of the supplier.
    /// @param _receiver The address of the user who will receive the tokens.
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    function _safeWithdrawLogic(
        address _poolTokenAddress,
        uint256 _amount,
        address _supplier,
        address _receiver,
        uint256 _maxGasForMatching
    ) internal {
        if (_amount == 0) revert AmountIsZero();

        WithdrawVars memory vars;
        vars.poolToken = ICToken(_poolTokenAddress);
        vars.underlyingToken = _getUnderlying(_poolTokenAddress);
        vars.remainingToWithdraw = _amount;
        vars.maxGasForMatching = _maxGasForMatching;
        vars.withdrawable = vars.poolToken.balanceOfUnderlying(address(this));
        vars.poolSupplyIndex = vars.poolToken.exchangeRateStored(); // Exchange rate has already been updated.

        if (_amount.div(vars.poolSupplyIndex) == 0) revert WithdrawTooSmall();

        /// Soft withdraw ///

        uint256 onPoolSupply = supplyBalanceInOf[_poolTokenAddress][_supplier].onPool;
        if (onPoolSupply > 0) {
            uint256 maxToWithdrawOnPool = onPoolSupply.mul(vars.poolSupplyIndex);

            if (
                maxToWithdrawOnPool > vars.remainingToWithdraw ||
                maxToWithdrawOnPool > vars.withdrawable
            ) {
                vars.toWithdraw = CompoundMath.min(vars.remainingToWithdraw, vars.withdrawable);

                vars.remainingToWithdraw -= vars.toWithdraw;
                supplyBalanceInOf[_poolTokenAddress][_supplier].onPool -= vars.toWithdraw.div(
                    vars.poolSupplyIndex
                );
            } else {
                vars.toWithdraw = maxToWithdrawOnPool;
                vars.remainingToWithdraw -= maxToWithdrawOnPool;
                supplyBalanceInOf[_poolTokenAddress][_supplier].onPool = 0;
            }

            _updateSupplierInDS(_poolTokenAddress, _supplier);

            if (vars.remainingToWithdraw == 0) {
                _leaveMarketIfNeeded(_poolTokenAddress, _supplier);

                // If this value is equal to 0 the withdraw will revert on Compound.
                if (vars.toWithdraw.div(vars.poolSupplyIndex) > 0)
                    _withdrawFromPool(_poolTokenAddress, vars.toWithdraw); // Reverts on error.
                vars.underlyingToken.safeTransfer(_receiver, _amount);

                emit Withdrawn(
                    _supplier,
                    _receiver,
                    _poolTokenAddress,
                    _amount,
                    supplyBalanceInOf[_poolTokenAddress][_supplier].onPool,
                    supplyBalanceInOf[_poolTokenAddress][_supplier].inP2P
                );

                return;
            }
        }

        Types.Delta storage delta = deltas[_poolTokenAddress];
        vars.p2pSupplyIndex = p2pSupplyIndex[_poolTokenAddress];

        supplyBalanceInOf[_poolTokenAddress][_supplier].inP2P -= CompoundMath.min(
            supplyBalanceInOf[_poolTokenAddress][_supplier].inP2P,
            vars.remainingToWithdraw.div(vars.p2pSupplyIndex)
        ); // In peer-to-peer unit
        _updateSupplierInDS(_poolTokenAddress, _supplier);

        /// Transfer withdraw ///

        // Reduce peer-to-peer supply delta first if any.
        if (vars.remainingToWithdraw > 0 && delta.p2pSupplyDelta > 0) {
            uint256 deltaInUnderlying = delta.p2pSupplyDelta.mul(vars.poolSupplyIndex);

            if (
                deltaInUnderlying > vars.remainingToWithdraw ||
                deltaInUnderlying > vars.withdrawable - vars.toWithdraw
            ) {
                uint256 matchedDelta = CompoundMath.min(
                    vars.remainingToWithdraw,
                    vars.withdrawable - vars.toWithdraw
                );

                delta.p2pSupplyDelta -= matchedDelta.div(vars.poolSupplyIndex);
                delta.p2pSupplyAmount -= matchedDelta.div(vars.p2pSupplyIndex);
                vars.toWithdraw += matchedDelta;
                vars.remainingToWithdraw -= matchedDelta;
            } else {
                vars.toWithdraw += deltaInUnderlying;
                vars.remainingToWithdraw -= deltaInUnderlying;
                delta.p2pSupplyDelta = 0;
                delta.p2pSupplyAmount -= deltaInUnderlying.div(vars.p2pSupplyIndex);
            }

            emit P2PSupplyDeltaUpdated(_poolTokenAddress, delta.p2pSupplyDelta);
            emit P2PAmountsUpdated(_poolTokenAddress, delta.p2pSupplyAmount, delta.p2pBorrowAmount);
        }

        // Match pool suppliers if any.
        if (
            vars.remainingToWithdraw > 0 &&
            !p2pDisabled[_poolTokenAddress] &&
            suppliersOnPool[_poolTokenAddress].getHead() != address(0)
        ) {
            (uint256 matched, uint256 gasConsumedInMatching) = _matchSuppliers(
                _poolTokenAddress,
                CompoundMath.min(vars.remainingToWithdraw, vars.withdrawable - vars.toWithdraw),
                vars.maxGasForMatching
            );
            if (vars.maxGasForMatching <= gasConsumedInMatching) vars.maxGasForMatching = 0;
            else vars.maxGasForMatching -= gasConsumedInMatching;

            if (matched > 0) {
                vars.remainingToWithdraw -= matched;
                vars.toWithdraw += matched;
            }
        }

        // If this value is equal to 0 the withdraw will revert on Compound.
        if (vars.toWithdraw.div(vars.poolSupplyIndex) > 0)
            _withdrawFromPool(_poolTokenAddress, vars.toWithdraw); // Reverts on error.

        /// Hard withdraw ///

        // Unmatch peer-to-peer borrowers.
        if (vars.remainingToWithdraw > 0) {
            uint256 unmatched = _unmatchBorrowers(
                _poolTokenAddress,
                vars.remainingToWithdraw,
                _maxGasForMatching
            );

            // If unmatched does not cover remainingToWithdraw, the difference is added to the borrow peer-to-peer delta.
            if (unmatched < vars.remainingToWithdraw) {
                delta.p2pBorrowDelta += (vars.remainingToWithdraw - unmatched).div(
                    vars.poolToken.borrowIndex()
                );
                emit P2PBorrowDeltaUpdated(_poolTokenAddress, delta.p2pBorrowAmount);
            }

            delta.p2pSupplyAmount -= vars.remainingToWithdraw.div(vars.p2pSupplyIndex);
            delta.p2pBorrowAmount -= unmatched.div(p2pBorrowIndex[_poolTokenAddress]);
            emit P2PAmountsUpdated(_poolTokenAddress, delta.p2pSupplyAmount, delta.p2pBorrowAmount);

            _borrowFromPool(_poolTokenAddress, vars.remainingToWithdraw); // Reverts on error.
        }

        _leaveMarketIfNeeded(_poolTokenAddress, _supplier);
        vars.underlyingToken.safeTransfer(_receiver, _amount);

        emit Withdrawn(
            _supplier,
            _receiver,
            _poolTokenAddress,
            _amount,
            supplyBalanceInOf[_poolTokenAddress][_supplier].onPool,
            supplyBalanceInOf[_poolTokenAddress][_supplier].inP2P
        );
    }

    /// @dev Implements repay logic without security checks.
    /// @param _poolTokenAddress The address of the market the user wants to interact with.
    /// @param _repayer The address of the account repaying the debt.
    /// @param _onBehalf The address of the account whose debt is repaid.
    /// @param _amount The amount of token (in underlying).
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    function _safeRepayLogic(
        address _poolTokenAddress,
        address _repayer,
        address _onBehalf,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) internal {
        if (lastBorrowBlock[_onBehalf] == block.number) revert SameBlockBorrowRepay();

        ERC20 underlyingToken = _getUnderlying(_poolTokenAddress);
        underlyingToken.safeTransferFrom(_repayer, address(this), _amount);

        RepayVars memory vars;
        vars.remainingToRepay = _amount;
        vars.maxGasForMatching = _maxGasForMatching;
        vars.poolBorrowIndex = ICToken(_poolTokenAddress).borrowIndex();

        /// Soft repay ///

        vars.borrowedOnPool = borrowBalanceInOf[_poolTokenAddress][_onBehalf].onPool;
        if (vars.borrowedOnPool > 0) {
            vars.maxToRepayOnPool = vars.borrowedOnPool.mul(vars.poolBorrowIndex);

            if (vars.maxToRepayOnPool > vars.remainingToRepay) {
                vars.toRepay = vars.remainingToRepay;

                borrowBalanceInOf[_poolTokenAddress][_onBehalf].onPool -= CompoundMath.min(
                    vars.borrowedOnPool,
                    vars.toRepay.div(vars.poolBorrowIndex)
                ); // In cdUnit.
                _updateBorrowerInDS(_poolTokenAddress, _onBehalf);

                _repayToPool(_poolTokenAddress, underlyingToken, vars.toRepay); // Reverts on error.
                _leaveMarketIfNeeded(_poolTokenAddress, _onBehalf);

                emit Repaid(
                    _repayer,
                    _onBehalf,
                    _poolTokenAddress,
                    _amount,
                    borrowBalanceInOf[_poolTokenAddress][_onBehalf].onPool,
                    borrowBalanceInOf[_poolTokenAddress][_onBehalf].inP2P
                );

                return;
            } else {
                vars.toRepay = vars.maxToRepayOnPool;
                vars.remainingToRepay -= vars.toRepay;

                borrowBalanceInOf[_poolTokenAddress][_onBehalf].onPool = 0;
                _updateBorrowerInDS(_poolTokenAddress, _onBehalf);
            }
        }

        Types.Delta storage delta = deltas[_poolTokenAddress];
        vars.p2pSupplyIndex = p2pSupplyIndex[_poolTokenAddress];
        vars.p2pBorrowIndex = p2pBorrowIndex[_poolTokenAddress];

        borrowBalanceInOf[_poolTokenAddress][_onBehalf].inP2P -= CompoundMath.min(
            borrowBalanceInOf[_poolTokenAddress][_onBehalf].inP2P,
            vars.remainingToRepay.div(vars.p2pBorrowIndex)
        ); // In peer-to-peer unit.
        _updateBorrowerInDS(_poolTokenAddress, _onBehalf);

        /// Transfer repay ///

        // Reduce peer-to-peer borrow delta first if any.
        if (vars.remainingToRepay > 0 && delta.p2pBorrowDelta > 0) {
            uint256 deltaInUnderlying = delta.p2pBorrowDelta.mul(vars.poolBorrowIndex);
            if (deltaInUnderlying > vars.remainingToRepay) {
                delta.p2pBorrowDelta -= vars.remainingToRepay.div(vars.poolBorrowIndex);
                delta.p2pBorrowAmount -= vars.remainingToRepay.div(vars.p2pBorrowIndex);
                vars.toRepay += vars.remainingToRepay;
                vars.remainingToRepay = 0;
            } else {
                delta.p2pBorrowDelta = 0;
                delta.p2pBorrowAmount -= deltaInUnderlying.div(vars.p2pBorrowIndex);
                vars.toRepay += deltaInUnderlying;
                vars.remainingToRepay -= deltaInUnderlying;
            }

            emit P2PBorrowDeltaUpdated(_poolTokenAddress, delta.p2pBorrowDelta);
            emit P2PAmountsUpdated(_poolTokenAddress, delta.p2pSupplyAmount, delta.p2pBorrowAmount);
        }

        // Repay the fee.
        if (vars.remainingToRepay > 0) {
            // Fee = (p2pBorrowAmount - p2pBorrowDelta) - (p2pSupplyAmount - p2pSupplyDelta).
            vars.feeToRepay = CompoundMath.safeSub(
                (delta.p2pBorrowAmount.mul(vars.p2pBorrowIndex) -
                    delta.p2pBorrowDelta.mul(vars.poolBorrowIndex)),
                (delta.p2pSupplyAmount.mul(vars.p2pSupplyIndex) -
                    delta.p2pSupplyDelta.mul(ICToken(_poolTokenAddress).exchangeRateStored()))
            );

            if (vars.feeToRepay > 0) {
                uint256 feeRepaid = CompoundMath.min(vars.feeToRepay, vars.remainingToRepay);
                vars.remainingToRepay -= feeRepaid;
                delta.p2pBorrowAmount -= feeRepaid.div(vars.p2pBorrowIndex);
                emit P2PAmountsUpdated(
                    _poolTokenAddress,
                    delta.p2pSupplyAmount,
                    delta.p2pBorrowAmount
                );
            }
        }

        // Match pool borrowers if any.
        if (
            vars.remainingToRepay > 0 &&
            !p2pDisabled[_poolTokenAddress] &&
            borrowersOnPool[_poolTokenAddress].getHead() != address(0)
        ) {
            (uint256 matched, uint256 gasConsumedInMatching) = _matchBorrowers(
                _poolTokenAddress,
                vars.remainingToRepay,
                vars.maxGasForMatching
            );
            if (vars.maxGasForMatching <= gasConsumedInMatching) vars.maxGasForMatching = 0;
            else vars.maxGasForMatching -= gasConsumedInMatching;

            if (matched > 0) {
                vars.remainingToRepay -= matched;
                vars.toRepay += matched;
            }
        }

        _repayToPool(_poolTokenAddress, underlyingToken, vars.toRepay); // Reverts on error.

        /// Hard repay ///

        // Unmatch peer-to-peer suppliers.
        if (vars.remainingToRepay > 0) {
            uint256 unmatched = _unmatchSuppliers(
                _poolTokenAddress,
                vars.remainingToRepay,
                vars.maxGasForMatching
            );

            // If unmatched does not cover remainingToRepay, the difference is added to the supply peer-to-peer delta.
            if (unmatched < vars.remainingToRepay) {
                delta.p2pSupplyDelta += (vars.remainingToRepay - unmatched).div(
                    ICToken(_poolTokenAddress).exchangeRateStored() // Exchange rate has already been updated.
                );
                emit P2PSupplyDeltaUpdated(_poolTokenAddress, delta.p2pBorrowDelta);
            }

            delta.p2pSupplyAmount -= unmatched.div(vars.p2pSupplyIndex);
            delta.p2pBorrowAmount -= vars.remainingToRepay.div(vars.p2pBorrowIndex);
            emit P2PAmountsUpdated(_poolTokenAddress, delta.p2pSupplyAmount, delta.p2pBorrowAmount);

            _supplyToPool(_poolTokenAddress, underlyingToken, vars.remainingToRepay); // Reverts on error.
        }

        _leaveMarketIfNeeded(_poolTokenAddress, _onBehalf);

        emit Repaid(
            _repayer,
            _onBehalf,
            _poolTokenAddress,
            _amount,
            borrowBalanceInOf[_poolTokenAddress][_onBehalf].onPool,
            borrowBalanceInOf[_poolTokenAddress][_onBehalf].inP2P
        );
    }

    /// @dev Supplies underlying tokens to Compound.
    /// @param _poolTokenAddress The address of the pool token.
    /// @param _underlyingToken The underlying token of the market to supply to.
    /// @param _amount The amount of token (in underlying).
    function _supplyToPool(
        address _poolTokenAddress,
        ERC20 _underlyingToken,
        uint256 _amount
    ) internal {
        if (_poolTokenAddress == cEth) {
            IWETH(wEth).withdraw(_amount); // Turn wETH into ETH.
            ICEther(_poolTokenAddress).mint{value: _amount}();
        } else {
            _underlyingToken.safeApprove(_poolTokenAddress, _amount);
            if (ICToken(_poolTokenAddress).mint(_amount) != 0) revert MintOnCompoundFailed();
        }
    }

    /// @dev Withdraws underlying tokens from Compound.
    /// @param _poolTokenAddress The address of the pool token.
    /// @param _amount The amount of token (in underlying).
    function _withdrawFromPool(address _poolTokenAddress, uint256 _amount) internal {
        if (ICToken(_poolTokenAddress).redeemUnderlying(_amount) != 0)
            revert RedeemOnCompoundFailed();
        if (_poolTokenAddress == cEth) IWETH(address(wEth)).deposit{value: _amount}(); // Turn the ETH received in wETH.
    }

    /// @dev Borrows underlying tokens from Compound.
    /// @param _poolTokenAddress The address of the pool token.
    /// @param _amount The amount of token (in underlying).
    function _borrowFromPool(address _poolTokenAddress, uint256 _amount) internal {
        if ((ICToken(_poolTokenAddress).borrow(_amount) != 0)) revert BorrowOnCompoundFailed();
        if (_poolTokenAddress == cEth) IWETH(address(wEth)).deposit{value: _amount}(); // Turn the ETH received in wETH.
    }

    /// @dev Repays underlying tokens to Compound.
    /// @param _poolTokenAddress The address of the pool token.
    /// @param _underlyingToken The underlying token of the market to repay to.
    /// @param _amount The amount of token (in underlying).
    function _repayToPool(
        address _poolTokenAddress,
        ERC20 _underlyingToken,
        uint256 _amount
    ) internal {
        // Repay only what is necessary. The remaining tokens stays on the contracts and are claimable by the DAO.
        _amount = Math.min(
            _amount,
            ICToken(_poolTokenAddress).borrowBalanceCurrent(address(this)) // The debt of the contract.
        );

        if (_amount > 0) {
            if (_poolTokenAddress == cEth) {
                IWETH(wEth).withdraw(_amount); // Turn wETH into ETH.
                ICEther(_poolTokenAddress).repayBorrow{value: _amount}();
            } else {
                _underlyingToken.safeApprove(_poolTokenAddress, _amount);
                if (ICToken(_poolTokenAddress).repayBorrow(_amount) != 0)
                    revert RepayOnCompoundFailed();
            }
        }
    }

    /// @dev Enters the user into the market if not already there.
    /// @param _user The address of the user to update.
    /// @param _poolTokenAddress The address of the market to check.
    function _enterMarketIfNeeded(address _poolTokenAddress, address _user) internal {
        if (!userMembership[_poolTokenAddress][_user]) {
            userMembership[_poolTokenAddress][_user] = true;
            enteredMarkets[_user].push(_poolTokenAddress);
        }
    }

    /// @dev Removes the user from the market if its balances are null.
    /// @param _user The address of the user to update.
    /// @param _poolTokenAddress The address of the market to check.
    function _leaveMarketIfNeeded(address _poolTokenAddress, address _user) internal {
        if (
            userMembership[_poolTokenAddress][_user] &&
            supplyBalanceInOf[_poolTokenAddress][_user].inP2P == 0 &&
            supplyBalanceInOf[_poolTokenAddress][_user].onPool == 0 &&
            borrowBalanceInOf[_poolTokenAddress][_user].inP2P == 0 &&
            borrowBalanceInOf[_poolTokenAddress][_user].onPool == 0
        ) {
            uint256 index;
            while (enteredMarkets[_user][index] != _poolTokenAddress) {
                unchecked {
                    ++index;
                }
            }
            userMembership[_poolTokenAddress][_user] = false;

            uint256 length = enteredMarkets[_user].length;
            if (index != length - 1)
                enteredMarkets[_user][index] = enteredMarkets[_user][length - 1];
            enteredMarkets[_user].pop();
        }
    }
}

File 2 of 23 : IPositionsManager.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

interface IPositionsManager {
    function supplyLogic(
        address _poolTokenAddress,
        address _supplier,
        address _onBehalf,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) external;

    function borrowLogic(
        address _poolTokenAddress,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) external;

    function withdrawLogic(
        address _poolTokenAddress,
        uint256 _amount,
        address _supplier,
        address _receiver,
        uint256 _maxGasForMatching
    ) external;

    function repayLogic(
        address _poolTokenAddress,
        address _repayer,
        address _onBehalf,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) external;

    function liquidateLogic(
        address _poolTokenBorrowedAddress,
        address _poolTokenCollateralAddress,
        address _borrower,
        uint256 _amount
    ) external;
}

File 3 of 23 : IWETH.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

interface IWETH {
    function deposit() external payable;

    function withdraw(uint256) external;
}

File 4 of 23 : MatchingEngine.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "./MorphoUtils.sol";

/// @title MatchingEngine.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @notice Smart contract managing the matching engine.
abstract contract MatchingEngine is MorphoUtils {
    using DoubleLinkedList for DoubleLinkedList.List;
    using CompoundMath for uint256;

    /// STRUCTS ///

    // Struct to avoid stack too deep.
    struct UnmatchVars {
        uint256 p2pIndex;
        uint256 toUnmatch;
        uint256 poolIndex;
        uint256 inUnderlying;
        uint256 gasLeftAtTheBeginning;
    }

    // Struct to avoid stack too deep.
    struct MatchVars {
        uint256 p2pIndex;
        uint256 toMatch;
        uint256 poolIndex;
        uint256 inUnderlying;
        uint256 gasLeftAtTheBeginning;
    }

    /// @notice Emitted when the position of a supplier is updated.
    /// @param _user The address of the supplier.
    /// @param _poolTokenAddress The address of the market.
    /// @param _balanceOnPool The supply balance on pool after update.
    /// @param _balanceInP2P The supply balance in peer-to-peer after update.
    event SupplierPositionUpdated(
        address indexed _user,
        address indexed _poolTokenAddress,
        uint256 _balanceOnPool,
        uint256 _balanceInP2P
    );

    /// @notice Emitted when the position of a borrower is updated.
    /// @param _user The address of the borrower.
    /// @param _poolTokenAddress The address of the market.
    /// @param _balanceOnPool The borrow balance on pool after update.
    /// @param _balanceInP2P The borrow balance in peer-to-peer after update.
    event BorrowerPositionUpdated(
        address indexed _user,
        address indexed _poolTokenAddress,
        uint256 _balanceOnPool,
        uint256 _balanceInP2P
    );

    /// INTERNAL ///

    /// @notice Matches suppliers' liquidity waiting on Compound up to the given `_amount` and moves it to peer-to-peer.
    /// @dev Note: This function expects Compound's exchange rate and peer-to-peer indexes to have been updated.
    /// @param _poolTokenAddress The address of the market from which to match suppliers.
    /// @param _amount The token amount to search for (in underlying).
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    /// @return matched The amount of liquidity matched (in underlying).
    /// @return gasConsumedInMatching The amount of gas consumed within the matching loop.
    function _matchSuppliers(
        address _poolTokenAddress,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) internal returns (uint256 matched, uint256 gasConsumedInMatching) {
        if (_maxGasForMatching == 0) return (0, 0);

        MatchVars memory vars;
        vars.poolIndex = ICToken(_poolTokenAddress).exchangeRateStored(); // Exchange rate has already been updated.
        vars.p2pIndex = p2pSupplyIndex[_poolTokenAddress];
        address firstPoolSupplier;
        Types.SupplyBalance storage firstPoolSupplierBalance;

        vars.gasLeftAtTheBeginning = gasleft();
        while (
            matched < _amount &&
            (firstPoolSupplier = suppliersOnPool[_poolTokenAddress].getHead()) != address(0) &&
            vars.gasLeftAtTheBeginning - gasleft() < _maxGasForMatching
        ) {
            firstPoolSupplierBalance = supplyBalanceInOf[_poolTokenAddress][firstPoolSupplier];
            vars.inUnderlying = firstPoolSupplierBalance.onPool.mul(vars.poolIndex);

            uint256 newPoolSupplyBalance;
            uint256 newP2PSupplyBalance;
            uint256 maxToMatch = _amount - matched;

            if (vars.inUnderlying <= maxToMatch) {
                // newPoolSupplyBalance is 0.
                newP2PSupplyBalance =
                    firstPoolSupplierBalance.inP2P +
                    vars.inUnderlying.div(vars.p2pIndex);
                matched += vars.inUnderlying;
            } else {
                newPoolSupplyBalance =
                    firstPoolSupplierBalance.onPool -
                    maxToMatch.div(vars.poolIndex);
                newP2PSupplyBalance =
                    firstPoolSupplierBalance.inP2P +
                    maxToMatch.div(vars.p2pIndex);
                matched = _amount;
            }

            firstPoolSupplierBalance.onPool = newPoolSupplyBalance;
            firstPoolSupplierBalance.inP2P = newP2PSupplyBalance;
            _updateSupplierInDS(_poolTokenAddress, firstPoolSupplier);

            emit SupplierPositionUpdated(
                firstPoolSupplier,
                _poolTokenAddress,
                newPoolSupplyBalance,
                newP2PSupplyBalance
            );
        }

        gasConsumedInMatching = vars.gasLeftAtTheBeginning - gasleft();
    }

    /// @notice Unmatches suppliers' liquidity in peer-to-peer up to the given `_amount` and moves it to Compound.
    /// @dev Note: This function expects Compound's exchange rate and peer-to-peer indexes to have been updated.
    /// @param _poolTokenAddress The address of the market from which to unmatch suppliers.
    /// @param _amount The amount to search for (in underlying).
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    /// @return The amount unmatched (in underlying).
    function _unmatchSuppliers(
        address _poolTokenAddress,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) internal returns (uint256) {
        if (_maxGasForMatching == 0) return 0;

        UnmatchVars memory vars;
        vars.poolIndex = ICToken(_poolTokenAddress).exchangeRateStored(); // Exchange rate has already been updated.
        vars.p2pIndex = p2pSupplyIndex[_poolTokenAddress];
        address firstP2PSupplier;
        Types.SupplyBalance storage firstP2PSupplierBalance;
        uint256 remainingToUnmatch = _amount;

        vars.gasLeftAtTheBeginning = gasleft();
        while (
            remainingToUnmatch > 0 &&
            (firstP2PSupplier = suppliersInP2P[_poolTokenAddress].getHead()) != address(0) &&
            vars.gasLeftAtTheBeginning - gasleft() < _maxGasForMatching
        ) {
            firstP2PSupplierBalance = supplyBalanceInOf[_poolTokenAddress][firstP2PSupplier];
            vars.inUnderlying = firstP2PSupplierBalance.inP2P.mul(vars.p2pIndex);

            uint256 newPoolSupplyBalance;
            uint256 newP2PSupplyBalance;

            if (vars.inUnderlying <= remainingToUnmatch) {
                // newP2PSupplyBalance is 0.
                newPoolSupplyBalance =
                    firstP2PSupplierBalance.onPool +
                    vars.inUnderlying.div(vars.poolIndex);
                remainingToUnmatch -= vars.inUnderlying;
            } else {
                newPoolSupplyBalance =
                    firstP2PSupplierBalance.onPool +
                    remainingToUnmatch.div(vars.poolIndex);
                newP2PSupplyBalance =
                    firstP2PSupplierBalance.inP2P -
                    remainingToUnmatch.div(vars.p2pIndex);
                remainingToUnmatch = 0;
            }

            firstP2PSupplierBalance.onPool = newPoolSupplyBalance;
            firstP2PSupplierBalance.inP2P = newP2PSupplyBalance;
            _updateSupplierInDS(_poolTokenAddress, firstP2PSupplier);

            emit SupplierPositionUpdated(
                firstP2PSupplier,
                _poolTokenAddress,
                newPoolSupplyBalance,
                newP2PSupplyBalance
            );
        }

        return _amount - remainingToUnmatch;
    }

    /// @notice Matches borrowers' liquidity waiting on Compound up to the given `_amount` and moves it to peer-to-peer.
    /// @dev Note: This function expects peer-to-peer indexes to have been updated..
    /// @param _poolTokenAddress The address of the market from which to match borrowers.
    /// @param _amount The amount to search for (in underlying).
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    /// @return matched The amount of liquidity matched (in underlying).
    /// @return gasConsumedInMatching The amount of gas consumed within the matching loop.
    function _matchBorrowers(
        address _poolTokenAddress,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) internal returns (uint256 matched, uint256 gasConsumedInMatching) {
        if (_maxGasForMatching == 0) return (0, 0);

        MatchVars memory vars;
        vars.poolIndex = ICToken(_poolTokenAddress).borrowIndex();
        vars.p2pIndex = p2pBorrowIndex[_poolTokenAddress];
        address firstPoolBorrower;
        Types.BorrowBalance storage firstPoolBorrowerBalance;

        vars.gasLeftAtTheBeginning = gasleft();
        while (
            matched < _amount &&
            (firstPoolBorrower = borrowersOnPool[_poolTokenAddress].getHead()) != address(0) &&
            vars.gasLeftAtTheBeginning - gasleft() < _maxGasForMatching
        ) {
            firstPoolBorrowerBalance = borrowBalanceInOf[_poolTokenAddress][firstPoolBorrower];
            vars.inUnderlying = firstPoolBorrowerBalance.onPool.mul(vars.poolIndex);

            uint256 newPoolBorrowBalance;
            uint256 newP2PBorrowBalance;
            uint256 maxToMatch = _amount - matched;

            if (vars.inUnderlying <= maxToMatch) {
                // newPoolBorrowBalance is 0.
                newP2PBorrowBalance =
                    firstPoolBorrowerBalance.inP2P +
                    vars.inUnderlying.div(vars.p2pIndex);
                matched += vars.inUnderlying;
            } else {
                newPoolBorrowBalance =
                    firstPoolBorrowerBalance.onPool -
                    maxToMatch.div(vars.poolIndex);
                newP2PBorrowBalance =
                    firstPoolBorrowerBalance.inP2P +
                    maxToMatch.div(vars.p2pIndex);
                matched = _amount;
            }

            firstPoolBorrowerBalance.onPool = newPoolBorrowBalance;
            firstPoolBorrowerBalance.inP2P = newP2PBorrowBalance;
            _updateBorrowerInDS(_poolTokenAddress, firstPoolBorrower);

            emit BorrowerPositionUpdated(
                firstPoolBorrower,
                _poolTokenAddress,
                newPoolBorrowBalance,
                newP2PBorrowBalance
            );
        }

        gasConsumedInMatching = vars.gasLeftAtTheBeginning - gasleft();
    }

    /// @notice Unmatches borrowers' liquidity in peer-to-peer for the given `_amount` and moves it to Compound.
    /// @dev Note: This function expects and peer-to-peer indexes to have been updated.
    /// @param _poolTokenAddress The address of the market from which to unmatch borrowers.
    /// @param _amount The amount to unmatch (in underlying).
    /// @param _maxGasForMatching The maximum amount of gas to consume within a matching engine loop.
    /// @return The amount unmatched (in underlying).
    function _unmatchBorrowers(
        address _poolTokenAddress,
        uint256 _amount,
        uint256 _maxGasForMatching
    ) internal returns (uint256) {
        if (_maxGasForMatching == 0) return 0;

        UnmatchVars memory vars;
        vars.poolIndex = ICToken(_poolTokenAddress).borrowIndex();
        vars.p2pIndex = p2pBorrowIndex[_poolTokenAddress];
        address firstP2PBorrower;
        Types.BorrowBalance storage firstP2PBorrowerBalance;
        uint256 remainingToUnmatch = _amount;

        vars.gasLeftAtTheBeginning = gasleft();
        while (
            remainingToUnmatch > 0 &&
            (firstP2PBorrower = borrowersInP2P[_poolTokenAddress].getHead()) != address(0) &&
            vars.gasLeftAtTheBeginning - gasleft() < _maxGasForMatching
        ) {
            firstP2PBorrowerBalance = borrowBalanceInOf[_poolTokenAddress][firstP2PBorrower];
            vars.inUnderlying = firstP2PBorrowerBalance.inP2P.mul(vars.p2pIndex);

            uint256 newPoolBorrowBalance;
            uint256 newP2PBorrowBalance;

            if (vars.inUnderlying <= remainingToUnmatch) {
                // newP2PBorrowBalance is 0.
                newPoolBorrowBalance =
                    firstP2PBorrowerBalance.onPool +
                    vars.inUnderlying.div(vars.poolIndex);
                remainingToUnmatch -= vars.inUnderlying;
            } else {
                newPoolBorrowBalance =
                    firstP2PBorrowerBalance.onPool +
                    remainingToUnmatch.div(vars.poolIndex);
                newP2PBorrowBalance =
                    firstP2PBorrowerBalance.inP2P -
                    remainingToUnmatch.div(vars.p2pIndex);
                remainingToUnmatch = 0;
            }

            firstP2PBorrowerBalance.onPool = newPoolBorrowBalance;
            firstP2PBorrowerBalance.inP2P = newP2PBorrowBalance;
            _updateBorrowerInDS(_poolTokenAddress, firstP2PBorrower);

            emit BorrowerPositionUpdated(
                firstP2PBorrower,
                _poolTokenAddress,
                newPoolBorrowBalance,
                newP2PBorrowBalance
            );
        }

        return _amount - remainingToUnmatch;
    }

    /// @notice Updates `_user` in the supplier data structures.
    /// @param _poolTokenAddress The address of the market on which to update the suppliers data structure.
    /// @param _user The address of the user.
    function _updateSupplierInDS(address _poolTokenAddress, address _user) internal {
        uint256 onPool = supplyBalanceInOf[_poolTokenAddress][_user].onPool;
        uint256 inP2P = supplyBalanceInOf[_poolTokenAddress][_user].inP2P;
        uint256 formerValueOnPool = suppliersOnPool[_poolTokenAddress].getValueOf(_user);
        uint256 formerValueInP2P = suppliersInP2P[_poolTokenAddress].getValueOf(_user);

        // Round pool balance to 0 if below threshold.
        if (onPool <= dustThreshold) {
            supplyBalanceInOf[_poolTokenAddress][_user].onPool = 0;
            onPool = 0;
        }
        if (formerValueOnPool != onPool) {
            if (formerValueOnPool > 0) suppliersOnPool[_poolTokenAddress].remove(_user);
            if (onPool > 0)
                suppliersOnPool[_poolTokenAddress].insertSorted(_user, onPool, maxSortedUsers);
        }

        // Round peer-to-peer balance to 0 if below threshold.
        if (inP2P <= dustThreshold) {
            supplyBalanceInOf[_poolTokenAddress][_user].inP2P = 0;
            inP2P = 0;
        }
        if (formerValueInP2P != inP2P) {
            if (formerValueInP2P > 0) suppliersInP2P[_poolTokenAddress].remove(_user);
            if (inP2P > 0)
                suppliersInP2P[_poolTokenAddress].insertSorted(_user, inP2P, maxSortedUsers);
        }

        if (address(rewardsManager) != address(0))
            rewardsManager.accrueUserSupplyUnclaimedRewards(
                _user,
                _poolTokenAddress,
                formerValueOnPool
            );
    }

    /// @notice Updates `_user` in the borrower data structures.
    /// @param _poolTokenAddress The address of the market on which to update the borrowers data structure.
    /// @param _user The address of the user.
    function _updateBorrowerInDS(address _poolTokenAddress, address _user) internal {
        uint256 onPool = borrowBalanceInOf[_poolTokenAddress][_user].onPool;
        uint256 inP2P = borrowBalanceInOf[_poolTokenAddress][_user].inP2P;
        uint256 formerValueOnPool = borrowersOnPool[_poolTokenAddress].getValueOf(_user);
        uint256 formerValueInP2P = borrowersInP2P[_poolTokenAddress].getValueOf(_user);

        // Round pool balance to 0 if below threshold.
        if (onPool <= dustThreshold) {
            borrowBalanceInOf[_poolTokenAddress][_user].onPool = 0;
            onPool = 0;
        }
        if (formerValueOnPool != onPool) {
            if (formerValueOnPool > 0) borrowersOnPool[_poolTokenAddress].remove(_user);
            if (onPool > 0)
                borrowersOnPool[_poolTokenAddress].insertSorted(_user, onPool, maxSortedUsers);
        }

        // Round peer-to-peer balance to 0 if below threshold.
        if (inP2P <= dustThreshold) {
            borrowBalanceInOf[_poolTokenAddress][_user].inP2P = 0;
            inP2P = 0;
        }
        if (formerValueInP2P != inP2P) {
            if (formerValueInP2P > 0) borrowersInP2P[_poolTokenAddress].remove(_user);
            if (inP2P > 0)
                borrowersInP2P[_poolTokenAddress].insertSorted(_user, inP2P, maxSortedUsers);
        }

        if (address(rewardsManager) != address(0))
            rewardsManager.accrueUserBorrowUnclaimedRewards(
                _user,
                _poolTokenAddress,
                formerValueOnPool
            );
    }
}

File 5 of 23 : MorphoUtils.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "@rari-capital/solmate/src/utils/SafeTransferLib.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";
import "./libraries/CompoundMath.sol";
import "../common/libraries/DelegateCall.sol";

import "./MorphoStorage.sol";

/// @title MorphoUtils.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @notice Modifiers, getters and other util functions for Morpho.
abstract contract MorphoUtils is MorphoStorage {
    using DoubleLinkedList for DoubleLinkedList.List;
    using CompoundMath for uint256;
    using DelegateCall for address;

    /// ERRORS ///

    /// @notice Thrown when the Compound's oracle failed.
    error CompoundOracleFailed();

    /// @notice Thrown when the market is not created yet.
    error MarketNotCreated();

    /// @notice Thrown when the market is paused.
    error MarketPaused();

    /// MODIFIERS ///

    /// @notice Prevents to update a market not created yet.
    /// @param _poolTokenAddress The address of the market to check.
    modifier isMarketCreated(address _poolTokenAddress) {
        if (!marketStatus[_poolTokenAddress].isCreated) revert MarketNotCreated();
        _;
    }

    /// @notice Prevents a user to trigger a function when market is not created or paused.
    /// @param _poolTokenAddress The address of the market to check.
    modifier isMarketCreatedAndNotPaused(address _poolTokenAddress) {
        Types.MarketStatus memory marketStatus_ = marketStatus[_poolTokenAddress];
        if (!marketStatus_.isCreated) revert MarketNotCreated();
        if (marketStatus_.isPaused) revert MarketPaused();
        _;
    }

    /// @notice Prevents a user to trigger a function when market is not created or paused or partial paused.
    /// @param _poolTokenAddress The address of the market to check.
    modifier isMarketCreatedAndNotPausedNorPartiallyPaused(address _poolTokenAddress) {
        Types.MarketStatus memory marketStatus_ = marketStatus[_poolTokenAddress];
        if (!marketStatus_.isCreated) revert MarketNotCreated();
        if (marketStatus_.isPaused || marketStatus_.isPartiallyPaused) revert MarketPaused();
        _;
    }

    /// EXTERNAL ///

    /// @notice Returns all markets entered by a given user.
    /// @param _user The address of the user.
    /// @return enteredMarkets_ The list of markets entered by this user.
    function getEnteredMarkets(address _user)
        external
        view
        returns (address[] memory enteredMarkets_)
    {
        return enteredMarkets[_user];
    }

    /// @notice Returns all created markets.
    /// @return marketsCreated_ The list of market addresses.
    function getAllMarkets() external view returns (address[] memory marketsCreated_) {
        return marketsCreated;
    }

    /// @notice Gets the head of the data structure on a specific market (for UI).
    /// @param _poolTokenAddress The address of the market from which to get the head.
    /// @param _positionType The type of user from which to get the head.
    /// @return head The head in the data structure.
    function getHead(address _poolTokenAddress, Types.PositionType _positionType)
        external
        view
        returns (address head)
    {
        if (_positionType == Types.PositionType.SUPPLIERS_IN_P2P)
            head = suppliersInP2P[_poolTokenAddress].getHead();
        else if (_positionType == Types.PositionType.SUPPLIERS_ON_POOL)
            head = suppliersOnPool[_poolTokenAddress].getHead();
        else if (_positionType == Types.PositionType.BORROWERS_IN_P2P)
            head = borrowersInP2P[_poolTokenAddress].getHead();
        else if (_positionType == Types.PositionType.BORROWERS_ON_POOL)
            head = borrowersOnPool[_poolTokenAddress].getHead();
    }

    /// @notice Gets the next user after `_user` in the data structure on a specific market (for UI).
    /// @param _poolTokenAddress The address of the market from which to get the user.
    /// @param _positionType The type of user from which to get the next user.
    /// @param _user The address of the user from which to get the next user.
    /// @return next The next user in the data structure.
    function getNext(
        address _poolTokenAddress,
        Types.PositionType _positionType,
        address _user
    ) external view returns (address next) {
        if (_positionType == Types.PositionType.SUPPLIERS_IN_P2P)
            next = suppliersInP2P[_poolTokenAddress].getNext(_user);
        else if (_positionType == Types.PositionType.SUPPLIERS_ON_POOL)
            next = suppliersOnPool[_poolTokenAddress].getNext(_user);
        else if (_positionType == Types.PositionType.BORROWERS_IN_P2P)
            next = borrowersInP2P[_poolTokenAddress].getNext(_user);
        else if (_positionType == Types.PositionType.BORROWERS_ON_POOL)
            next = borrowersOnPool[_poolTokenAddress].getNext(_user);
    }

    /// @notice Updates the peer-to-peer indexes.
    /// @dev Note: This function updates the exchange rate on Compound. As a consequence only a call to exchangeRatesStored() is necessary to get the most up to date exchange rate.
    /// @param _poolTokenAddress The address of the market to update.
    function updateP2PIndexes(address _poolTokenAddress)
        external
        isMarketCreated(_poolTokenAddress)
    {
        _updateP2PIndexes(_poolTokenAddress);
    }

    /// INTERNAL ///

    /// @dev Updates the peer-to-peer indexes.
    /// @dev Note: This function updates the exchange rate on Compound. As a consequence only a call to exchangeRatesStored() is necessary to get the most up to date exchange rate.
    /// @param _poolTokenAddress The address of the market to update.
    function _updateP2PIndexes(address _poolTokenAddress) internal {
        address(interestRatesManager).functionDelegateCall(
            abi.encodeWithSelector(
                interestRatesManager.updateP2PIndexes.selector,
                _poolTokenAddress
            )
        );
    }

    /// @dev Checks whether the user has enough collateral to maintain such a borrow position.
    /// @param _user The user to check.
    /// @param _poolTokenAddress The market to hypothetically withdraw/borrow in.
    /// @param _withdrawnAmount The amount of tokens to hypothetically withdraw (in underlying).
    /// @param _borrowedAmount The amount of tokens to hypothetically borrow (in underlying).
    function _isLiquidatable(
        address _user,
        address _poolTokenAddress,
        uint256 _withdrawnAmount,
        uint256 _borrowedAmount
    ) internal view returns (bool) {
        ICompoundOracle oracle = ICompoundOracle(comptroller.oracle());
        uint256 numberOfEnteredMarkets = enteredMarkets[_user].length;

        Types.AssetLiquidityData memory assetData;
        uint256 maxDebtValue;
        uint256 debtValue;
        uint256 i;

        while (i < numberOfEnteredMarkets) {
            address poolTokenEntered = enteredMarkets[_user][i];

            assetData = _getUserLiquidityDataForAsset(_user, poolTokenEntered, oracle);
            maxDebtValue += assetData.maxDebtValue;
            debtValue += assetData.debtValue;

            if (_poolTokenAddress == poolTokenEntered) {
                if (_borrowedAmount > 0)
                    debtValue += _borrowedAmount.mul(assetData.underlyingPrice);

                if (_withdrawnAmount > 0)
                    maxDebtValue -= _withdrawnAmount.mul(assetData.underlyingPrice).mul(
                        assetData.collateralFactor
                    );
            }

            unchecked {
                ++i;
            }
        }

        return debtValue > maxDebtValue;
    }

    /// @notice Returns the data related to `_poolTokenAddress` for the `_user`.
    /// @dev Note: Must be called after calling `_updateP2PIndexes()` to have the most up-to-date indexes.
    /// @param _user The user to determine data for.
    /// @param _poolTokenAddress The address of the market.
    /// @param _oracle The oracle used.
    /// @return assetData The data related to this asset.
    function _getUserLiquidityDataForAsset(
        address _user,
        address _poolTokenAddress,
        ICompoundOracle _oracle
    ) internal view returns (Types.AssetLiquidityData memory assetData) {
        assetData.underlyingPrice = _oracle.getUnderlyingPrice(_poolTokenAddress);
        if (assetData.underlyingPrice == 0) revert CompoundOracleFailed();
        (, assetData.collateralFactor, ) = comptroller.markets(_poolTokenAddress);

        assetData.collateralValue = _getUserSupplyBalanceInOf(_poolTokenAddress, _user).mul(
            assetData.underlyingPrice
        );
        assetData.debtValue = _getUserBorrowBalanceInOf(_poolTokenAddress, _user).mul(
            assetData.underlyingPrice
        );
        assetData.maxDebtValue = assetData.collateralValue.mul(assetData.collateralFactor);
    }

    /// @dev Returns the supply balance of `_user` in the `_poolTokenAddress` market.
    /// @dev Note: Compute the result with the index stored and not the most up to date one.
    /// @param _user The address of the user.
    /// @param _poolTokenAddress The market where to get the supply amount.
    /// @return The supply balance of the user (in underlying).
    function _getUserSupplyBalanceInOf(address _poolTokenAddress, address _user)
        internal
        view
        returns (uint256)
    {
        return
            supplyBalanceInOf[_poolTokenAddress][_user].inP2P.mul(
                p2pSupplyIndex[_poolTokenAddress]
            ) +
            supplyBalanceInOf[_poolTokenAddress][_user].onPool.mul(
                ICToken(_poolTokenAddress).exchangeRateStored()
            );
    }

    /// @dev Returns the borrow balance of `_user` in the `_poolTokenAddress` market.
    /// @param _user The address of the user.
    /// @param _poolTokenAddress The market where to get the borrow amount.
    /// @return The borrow balance of the user (in underlying).
    function _getUserBorrowBalanceInOf(address _poolTokenAddress, address _user)
        internal
        view
        returns (uint256)
    {
        return
            borrowBalanceInOf[_poolTokenAddress][_user].inP2P.mul(
                p2pBorrowIndex[_poolTokenAddress]
            ) +
            borrowBalanceInOf[_poolTokenAddress][_user].onPool.mul(
                ICToken(_poolTokenAddress).borrowIndex()
            );
    }

    /// @dev Returns the underlying ERC20 token related to the pool token.
    /// @param _poolTokenAddress The address of the pool token.
    /// @return The underlying ERC20 token.
    function _getUnderlying(address _poolTokenAddress) internal view returns (ERC20) {
        if (_poolTokenAddress == cEth)
            // cETH has no underlying() function.
            return ERC20(wEth);
        else return ERC20(ICToken(_poolTokenAddress).underlying());
    }
}

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

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

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

    /*///////////////////////////////////////////////////////////////
                            ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

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

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

        require(success, "ETH_TRANSFER_FAILED");
    }

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

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

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

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

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

        require(success, "TRANSFER_FROM_FAILED");
    }

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

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

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

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

        require(success, "TRANSFER_FAILED");
    }

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

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

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

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

        require(success, "APPROVE_FAILED");
    }
}

File 7 of 23 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }
}

File 8 of 23 : CompoundMath.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

/// @title CompoundMath.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @dev Library emulating in solidity 8+ the behavior of Compound's mulScalarTruncate and divScalarByExpTruncate functions.
library CompoundMath {
    /// ERRORS ///

    /// @notice Reverts when the number exceeds 224 bits.
    error NumberExceeds224Bits();

    /// @notice Reverts when the number exceeds 32 bits.
    error NumberExceeds32Bits();

    /// INTERNAL ///

    function mul(uint256 x, uint256 y) internal pure returns (uint256) {
        return (x * y) / 1e18;
    }

    function div(uint256 x, uint256 y) internal pure returns (uint256) {
        return ((1e18 * x * 1e18) / y) / 1e18;
    }

    function safe224(uint256 n) internal pure returns (uint224) {
        if (n >= 2**224) revert NumberExceeds224Bits();
        return uint224(n);
    }

    function safe32(uint256 n) internal pure returns (uint32) {
        if (n >= 2**32) revert NumberExceeds32Bits();
        return uint32(n);
    }

    function min(
        uint256 a,
        uint256 b,
        uint256 c
    ) internal pure returns (uint256) {
        return a < b ? a < c ? a : c : b < c ? b : c;
    }

    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    function safeSub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a - b : 0;
    }
}

File 9 of 23 : DelegateCall.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

/// @title Delegate Call Library.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @dev Library to perform delegate calls inspired by the OZ Address library: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol.
library DelegateCall {
    /// ERRORS ///

    /// @notice Thrown when a low delegate call has failed without error message.
    error LowLevelDelegateCallFailed();

    /// INTERNAL ///

    /// @dev Performs a low-level delegate call to the `_target` contract.
    /// @dev Note: Unlike the OZ's library this function does not check if the `_target` is a contract. It is the responsibility of the caller to ensure that the `_target` is a contract.
    /// @param _target The address of the target contract.
    /// @param _data The data to pass to the function called on the target contract.
    /// @return The return data from the function called on the target contract.
    function functionDelegateCall(address _target, bytes memory _data)
        internal
        returns (bytes memory)
    {
        (bool success, bytes memory returndata) = _target.delegatecall(_data);
        if (success) return returndata;
        else {
            // Look for revert reason and bubble it up if present.
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly.

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else revert LowLevelDelegateCallFailed();
        }
    }
}

File 10 of 23 : MorphoStorage.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "./interfaces/compound/ICompound.sol";
import "./interfaces/IPositionsManager.sol";
import "./interfaces/IIncentivesVault.sol";
import "./interfaces/IRewardsManager.sol";
import "./interfaces/IInterestRatesManager.sol";

import "../common/libraries/DoubleLinkedList.sol";
import "./libraries/Types.sol";

import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";

/// @title MorphoStorage.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @notice All storage variables used in Morpho contracts.
abstract contract MorphoStorage is OwnableUpgradeable, ReentrancyGuardUpgradeable {
    /// GLOBAL STORAGE ///

    uint8 public constant CTOKEN_DECIMALS = 8; // The number of decimals for cToken.
    uint16 public constant MAX_BASIS_POINTS = 10_000; // 100% in basis points.
    uint16 public constant MAX_CLAIMABLE_RESERVE = 9_000; // The max proportion of reserve fee claimable by the DAO at once (90% in basis points).
    uint256 public constant WAD = 1e18;

    uint256 public maxSortedUsers; // The max number of users to sort in the data structure.
    uint256 public dustThreshold; // The minimum amount to keep in the data structure.
    Types.MaxGasForMatching public defaultMaxGasForMatching; // The default max gas to consume within loops in matching engine functions.

    /// POSITIONS STORAGE ///

    mapping(address => DoubleLinkedList.List) internal suppliersInP2P; // For a given market, the suppliers in peer-to-peer.
    mapping(address => DoubleLinkedList.List) internal suppliersOnPool; // For a given market, the suppliers on Compound.
    mapping(address => DoubleLinkedList.List) internal borrowersInP2P; // For a given market, the borrowers in peer-to-peer.
    mapping(address => DoubleLinkedList.List) internal borrowersOnPool; // For a given market, the borrowers on Compound.
    mapping(address => mapping(address => Types.SupplyBalance)) public supplyBalanceInOf; // For a given market, the supply balance of a user. cToken -> user -> balances.
    mapping(address => mapping(address => Types.BorrowBalance)) public borrowBalanceInOf; // For a given market, the borrow balance of a user. cToken -> user -> balances.
    mapping(address => mapping(address => bool)) public userMembership; // Whether the user is in the market or not. cToken -> user -> bool.
    mapping(address => address[]) public enteredMarkets; // The markets entered by a user. user -> cTokens.

    /// MARKETS STORAGE ///

    address[] public marketsCreated; // Keeps track of the created markets.
    mapping(address => bool) public p2pDisabled; // Whether the peer-to-peer market is open or not.
    mapping(address => uint256) public p2pSupplyIndex; // Current index from supply peer-to-peer unit to underlying (in wad).
    mapping(address => uint256) public p2pBorrowIndex; // Current index from borrow peer-to-peer unit to underlying (in wad).
    mapping(address => Types.LastPoolIndexes) public lastPoolIndexes; // Last pool index stored.
    mapping(address => Types.MarketParameters) public marketParameters; // Market parameters.
    mapping(address => Types.MarketStatus) public marketStatus; // Market status.
    mapping(address => Types.Delta) public deltas; // Delta parameters for each market.

    /// CONTRACTS AND ADDRESSES ///

    IPositionsManager public positionsManager;
    IIncentivesVault public incentivesVault;
    IRewardsManager public rewardsManager;
    IInterestRatesManager public interestRatesManager;
    IComptroller public comptroller;
    address public treasuryVault;
    address public cEth;
    address public wEth;

    /// APPENDIX STORAGE ///

    mapping(address => uint256) public lastBorrowBlock; // Block number of the last borrow of the user.

    /// CONSTRUCTOR ///

    /// @notice Constructs the contract.
    /// @dev The contract is automatically marked as initialized when deployed so that nobody can highjack the implementation contract.
    constructor() initializer {}
}

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

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

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

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

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

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

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

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

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

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

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

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

        return true;
    }

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

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

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

        return true;
    }

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

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

        balanceOf[from] -= amount;

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

        emit Transfer(from, to, amount);

        return true;
    }

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

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

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

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

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

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

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

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

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

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

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

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

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

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

File 12 of 23 : ICompound.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

interface ICEth {
    function accrueInterest() external returns (uint256);

    function borrowRate() external returns (uint256);

    function borrowIndex() external returns (uint256);

    function borrowBalanceStored(address) external returns (uint256);

    function mint() external payable;

    function exchangeRateCurrent() external returns (uint256);

    function exchangeRateStored() external view returns (uint256);

    function supplyRatePerBlock() external returns (uint256);

    function redeem(uint256) external returns (uint256);

    function redeemUnderlying(uint256) external returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);

    function transferFrom(
        address,
        address,
        uint256
    ) external returns (bool);

    function transfer(address dst, uint256 amount) external returns (bool);

    function balanceOf(address) external returns (uint256);

    function balanceOfUnderlying(address account) external returns (uint256);

    function borrow(uint256) external returns (uint256);

    function repayBorrow() external payable;

    function borrowBalanceCurrent(address) external returns (uint256);

    function borrowRatePerBlock() external view returns (uint256);
}

interface IComptroller {
    struct CompMarketState {
        /// @notice The market's last updated compBorrowIndex or compSupplyIndex
        uint224 index;
        /// @notice The block number the index was last updated at
        uint32 block;
    }

    function liquidationIncentiveMantissa() external view returns (uint256);

    function closeFactorMantissa() external view returns (uint256);

    function admin() external view returns (address);

    function oracle() external view returns (address);

    function borrowCaps(address) external view returns (uint256);

    function markets(address)
        external
        view
        returns (
            bool isListed,
            uint256 collateralFactorMantissa,
            bool isComped
        );

    function enterMarkets(address[] calldata cTokens) external returns (uint256[] memory);

    function exitMarket(address cToken) external returns (uint256);

    function mintAllowed(
        address cToken,
        address minter,
        uint256 mintAmount
    ) external returns (uint256);

    function mintVerify(
        address cToken,
        address minter,
        uint256 mintAmount,
        uint256 mintTokens
    ) external;

    function redeemAllowed(
        address cToken,
        address redeemer,
        uint256 redeemTokens
    ) external returns (uint256);

    function redeemVerify(
        address cToken,
        address redeemer,
        uint256 redeemAmount,
        uint256 redeemTokens
    ) external;

    function borrowAllowed(
        address cToken,
        address borrower,
        uint256 borrowAmount
    ) external returns (uint256);

    function borrowVerify(
        address cToken,
        address borrower,
        uint256 borrowAmount
    ) external;

    function repayBorrowAllowed(
        address cToken,
        address payer,
        address borrower,
        uint256 repayAmount
    ) external returns (uint256);

    function repayBorrowVerify(
        address cToken,
        address payer,
        address borrower,
        uint256 repayAmount,
        uint256 borrowerIndex
    ) external;

    function liquidateBorrowAllowed(
        address cTokenBorrowed,
        address cTokenCollateral,
        address liquidator,
        address borrower,
        uint256 repayAmount
    ) external returns (uint256);

    function liquidateBorrowVerify(
        address cTokenBorrowed,
        address cTokenCollateral,
        address liquidator,
        address borrower,
        uint256 repayAmount,
        uint256 seizeTokens
    ) external;

    function seizeAllowed(
        address cTokenCollateral,
        address cTokenBorrowed,
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external returns (uint256);

    function seizeVerify(
        address cTokenCollateral,
        address cTokenBorrowed,
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external;

    function transferAllowed(
        address cToken,
        address src,
        address dst,
        uint256 transferTokens
    ) external returns (uint256);

    function transferVerify(
        address cToken,
        address src,
        address dst,
        uint256 transferTokens
    ) external;

    /*** Liquidity/Liquidation Calculations ***/

    function liquidateCalculateSeizeTokens(
        address cTokenBorrowed,
        address cTokenCollateral,
        uint256 repayAmount
    ) external view returns (uint256, uint256);

    function getAccountLiquidity(address)
        external
        view
        returns (
            uint256,
            uint256,
            uint256
        );

    function getHypotheticalAccountLiquidity(
        address,
        address,
        uint256,
        uint256
    )
        external
        returns (
            uint256,
            uint256,
            uint256
        );

    function checkMembership(address, address) external view returns (bool);

    function claimComp(address holder) external;

    function claimComp(address holder, address[] memory cTokens) external;

    function compSpeeds(address) external view returns (uint256);

    function compSupplySpeeds(address) external view returns (uint256);

    function compBorrowSpeeds(address) external view returns (uint256);

    function compSupplyState(address) external view returns (CompMarketState memory);

    function compBorrowState(address) external view returns (CompMarketState memory);

    function getCompAddress() external view returns (address);

    function _setPriceOracle(address newOracle) external returns (uint256);

    function _setMintPaused(ICToken cToken, bool state) external returns (bool);

    function _setBorrowPaused(ICToken cToken, bool state) external returns (bool);

    function _setCollateralFactor(ICToken cToken, uint256 newCollateralFactorMantissa)
        external
        returns (uint256);

    function _setCompSpeeds(
        ICToken[] memory cTokens,
        uint256[] memory supplySpeeds,
        uint256[] memory borrowSpeeds
    ) external;
}

interface IInterestRateModel {
    function getBorrowRate(
        uint256 cash,
        uint256 borrows,
        uint256 reserves
    ) external view returns (uint256);

    function getSupplyRate(
        uint256 cash,
        uint256 borrows,
        uint256 reserves,
        uint256 reserveFactorMantissa
    ) external view returns (uint256);
}

interface ICToken {
    function isCToken() external returns (bool);

    function transfer(address dst, uint256 amount) external returns (bool);

    function transferFrom(
        address src,
        address dst,
        uint256 amount
    ) external returns (bool);

    function approve(address spender, uint256 amount) external returns (bool);

    function allowance(address owner, address spender) external view returns (uint256);

    function balanceOf(address owner) external view returns (uint256);

    function balanceOfUnderlying(address owner) external returns (uint256);

    function getAccountSnapshot(address account)
        external
        view
        returns (
            uint256,
            uint256,
            uint256,
            uint256
        );

    function borrowRatePerBlock() external view returns (uint256);

    function supplyRatePerBlock() external view returns (uint256);

    function totalBorrowsCurrent() external returns (uint256);

    function borrowBalanceCurrent(address account) external returns (uint256);

    function borrowBalanceStored(address account) external view returns (uint256);

    function exchangeRateCurrent() external returns (uint256);

    function exchangeRateStored() external view returns (uint256);

    function getCash() external view returns (uint256);

    function seize(
        address liquidator,
        address borrower,
        uint256 seizeTokens
    ) external returns (uint256);

    function borrowRate() external returns (uint256);

    function borrowIndex() external view returns (uint256);

    function borrow(uint256) external returns (uint256);

    function repayBorrow(uint256) external returns (uint256);

    function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256);

    function liquidateBorrow(
        address borrower,
        uint256 repayAmount,
        address cTokenCollateral
    ) external returns (uint256);

    function underlying() external view returns (address);

    function mint(uint256) external returns (uint256);

    function redeemUnderlying(uint256) external returns (uint256);

    function accrueInterest() external returns (uint256);

    function totalSupply() external view returns (uint256);

    function totalBorrows() external view returns (uint256);

    function accrualBlockNumber() external view returns (uint256);

    function totalReserves() external view returns (uint256);

    function interestRateModel() external view returns (IInterestRateModel);

    function reserveFactorMantissa() external view returns (uint256);

    function initialExchangeRateMantissa() external view returns (uint256);

    /*** Admin Functions ***/

    function _setPendingAdmin(address payable newPendingAdmin) external returns (uint256);

    function _acceptAdmin() external returns (uint256);

    function _setComptroller(IComptroller newComptroller) external returns (uint256);

    function _setReserveFactor(uint256 newReserveFactorMantissa) external returns (uint256);

    function _reduceReserves(uint256 reduceAmount) external returns (uint256);

    function _setInterestRateModel(IInterestRateModel newInterestRateModel)
        external
        returns (uint256);
}

interface ICEther is ICToken {
    function mint() external payable;

    function repayBorrow() external payable;
}

interface ICompoundOracle {
    function getUnderlyingPrice(address) external view returns (uint256);
}

File 13 of 23 : IIncentivesVault.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "./IOracle.sol";

interface IIncentivesVault {
    function setOracle(IOracle _newOracle) external;

    function setMorphoDao(address _newMorphoDao) external;

    function setBonus(uint256 _newBonus) external;

    function setPauseStatus(bool _newStatus) external;

    function transferMorphoTokensToDao(uint256 _amount) external;

    function tradeCompForMorphoTokens(address _to, uint256 _amount) external;
}

File 14 of 23 : IRewardsManager.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

import "./compound/ICompound.sol";

interface IRewardsManager {
    function initialize(address _morpho) external;

    function claimRewards(address[] calldata, address) external returns (uint256);

    function userUnclaimedCompRewards(address) external view returns (uint256);

    function compSupplierIndex(address, address) external view returns (uint256);

    function compBorrowerIndex(address, address) external view returns (uint256);

    function getLocalCompSupplyState(address _cTokenAddress)
        external
        view
        returns (IComptroller.CompMarketState memory);

    function getLocalCompBorrowState(address _cTokenAddress)
        external
        view
        returns (IComptroller.CompMarketState memory);

    function accrueUserSupplyUnclaimedRewards(
        address,
        address,
        uint256
    ) external;

    function accrueUserBorrowUnclaimedRewards(
        address,
        address,
        uint256
    ) external;
}

File 15 of 23 : IInterestRatesManager.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

interface IInterestRatesManager {
    function updateP2PIndexes(address _marketAddress) external;
}

File 16 of 23 : DoubleLinkedList.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

/// @title Double Linked List.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @notice Modified double linked list with capped sorting insertion.
library DoubleLinkedList {
    /// STRUCTS ///

    struct Account {
        address prev;
        address next;
        uint256 value;
    }

    struct List {
        mapping(address => Account) accounts;
        address head;
        address tail;
    }

    /// ERRORS ///

    /// @notice Thrown when the account is already inserted in the double linked list.
    error AccountAlreadyInserted();

    /// @notice Thrown when the account to remove does not exist.
    error AccountDoesNotExist();

    /// @notice Thrown when the address is zero at insertion.
    error AddressIsZero();

    /// @notice Thrown when the value is zero at insertion.
    error ValueIsZero();

    /// INTERNAL ///

    /// @notice Returns the `account` linked to `_id`.
    /// @param _list The list to search in.
    /// @param _id The address of the account.
    /// @return The value of the account.
    function getValueOf(List storage _list, address _id) internal view returns (uint256) {
        return _list.accounts[_id].value;
    }

    /// @notice Returns the address at the head of the `_list`.
    /// @param _list The list to get the head.
    /// @return The address of the head.
    function getHead(List storage _list) internal view returns (address) {
        return _list.head;
    }

    /// @notice Returns the address at the tail of the `_list`.
    /// @param _list The list to get the tail.
    /// @return The address of the tail.
    function getTail(List storage _list) internal view returns (address) {
        return _list.tail;
    }

    /// @notice Returns the next id address from the current `_id`.
    /// @param _list The list to search in.
    /// @param _id The address of the account.
    /// @return The address of the next account.
    function getNext(List storage _list, address _id) internal view returns (address) {
        return _list.accounts[_id].next;
    }

    /// @notice Returns the previous id address from the current `_id`.
    /// @param _list The list to search in.
    /// @param _id The address of the account.
    /// @return The address of the previous account.
    function getPrev(List storage _list, address _id) internal view returns (address) {
        return _list.accounts[_id].prev;
    }

    /// @notice Removes an account of the `_list`.
    /// @param _list The list to search in.
    /// @param _id The address of the account.
    function remove(List storage _list, address _id) internal {
        if (_list.accounts[_id].value == 0) revert AccountDoesNotExist();
        Account memory account = _list.accounts[_id];

        if (account.prev != address(0)) _list.accounts[account.prev].next = account.next;
        else _list.head = account.next;
        if (account.next != address(0)) _list.accounts[account.next].prev = account.prev;
        else _list.tail = account.prev;

        delete _list.accounts[_id];
    }

    /// @notice Inserts an account in the `_list` at the right slot based on its `_value`.
    /// @param _list The list to search in.
    /// @param _id The address of the account.
    /// @param _value The value of the account.
    /// @param _maxIterations The max number of iterations.
    function insertSorted(
        List storage _list,
        address _id,
        uint256 _value,
        uint256 _maxIterations
    ) internal {
        if (_value == 0) revert ValueIsZero();
        if (_id == address(0)) revert AddressIsZero();
        if (_list.accounts[_id].value != 0) revert AccountAlreadyInserted();

        uint256 numberOfIterations;
        address next = _list.head; // If not added at the end of the list `_id` will be inserted before `next`.

        while (
            numberOfIterations < _maxIterations &&
            next != _list.tail &&
            _list.accounts[next].value >= _value
        ) {
            next = _list.accounts[next].next;
            unchecked {
                ++numberOfIterations;
            }
        }

        // Account is not the new tail.
        if (next != address(0) && _list.accounts[next].value < _value) {
            // Account is the new head.
            if (next == _list.head) {
                _list.accounts[_id] = Account(address(0), next, _value);
                _list.head = _id;
                _list.accounts[next].prev = _id;
            }
            // Account is not the new head.
            else {
                _list.accounts[_id] = Account(_list.accounts[next].prev, next, _value);
                _list.accounts[_list.accounts[next].prev].next = _id;
                _list.accounts[next].prev = _id;
            }
        }
        // Account is the new tail.
        else {
            // Account is the new head.
            if (_list.head == address(0)) {
                _list.accounts[_id] = Account(address(0), address(0), _value);
                _list.head = _id;
                _list.tail = _id;
            }
            // Account is not the new head.
            else {
                _list.accounts[_id] = Account(_list.tail, address(0), _value);
                _list.accounts[_list.tail].next = _id;
                _list.tail = _id;
            }
        }
    }
}

File 17 of 23 : Types.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

/// @title Types.
/// @author Morpho Labs.
/// @custom:contact [email protected]
/// @dev Common types and structs used in Moprho contracts.
library Types {
    /// ENUMS ///

    enum PositionType {
        SUPPLIERS_IN_P2P,
        SUPPLIERS_ON_POOL,
        BORROWERS_IN_P2P,
        BORROWERS_ON_POOL
    }

    /// STRUCTS ///

    struct SupplyBalance {
        uint256 inP2P; // In supplier's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests earned by suppliers in peer-to-peer. Multiply by the peer-to-peer supply index to get the underlying amount.
        uint256 onPool; // In cToken. Multiply by the pool supply index to get the underlying amount.
    }

    struct BorrowBalance {
        uint256 inP2P; // In borrower's peer-to-peer unit, a unit that grows in underlying value, to keep track of the interests paid by borrowers in peer-to-peer. Multiply by the peer-to-peer borrow index to get the underlying amount.
        uint256 onPool; // In cdUnit, a unit that grows in value, to keep track of the debt increase when borrowers are on Compound. Multiply by the pool borrow index to get the underlying amount.
    }

    // Max gas to consume during the matching process for supply, borrow, withdraw and repay functions.
    struct MaxGasForMatching {
        uint64 supply;
        uint64 borrow;
        uint64 withdraw;
        uint64 repay;
    }

    struct Delta {
        uint256 p2pSupplyDelta; // Difference between the stored peer-to-peer supply amount and the real peer-to-peer supply amount (in cToken).
        uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in cdUnit).
        uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer unit).
        uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer unit).
    }

    struct AssetLiquidityData {
        uint256 collateralValue; // The collateral value of the asset.
        uint256 maxDebtValue; // The maximum possible debt value of the asset.
        uint256 debtValue; // The debt value of the asset.
        uint256 underlyingPrice; // The price of the token.
        uint256 collateralFactor; // The liquidation threshold applied on this token.
    }

    struct LiquidityData {
        uint256 collateralValue; // The collateral value.
        uint256 maxDebtValue; // The maximum debt value possible.
        uint256 debtValue; // The debt value.
    }

    // Variables are packed together to save gas (will not exceed their limit during Morpho's lifetime).
    struct LastPoolIndexes {
        uint32 lastUpdateBlockNumber; // The last time the peer-to-peer indexes were updated.
        uint112 lastSupplyPoolIndex; // Last pool supply index.
        uint112 lastBorrowPoolIndex; // Last pool borrow index.
    }

    struct MarketParameters {
        uint16 reserveFactor; // Proportion of the interest earned by users sent to the DAO for each market, in basis point (100% = 10 000). The value is set at market creation.
        uint16 p2pIndexCursor; // Position of the peer-to-peer rate in the pool's spread. Determine the weights of the weighted arithmetic average in the indexes computations ((1 - p2pIndexCursor) * r^S + p2pIndexCursor * r^B) (in basis point).
    }

    struct MarketStatus {
        bool isCreated; // Whether or not this market is created.
        bool isPaused; // Whether the market is paused or not (all entry points on Morpho are frozen; supply, borrow, withdraw, repay and liquidate).
        bool isPartiallyPaused; // Whether the market is partially paused or not (only supply and borrow are frozen).
    }
}

File 18 of 23 : ReentrancyGuardUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuardUpgradeable is Initializable {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 19 of 23 : OwnableUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

File 20 of 23 : IOracle.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity ^0.8.0;

interface IOracle {
    function consult(uint256 _amountIn) external returns (uint256);
}

File 21 of 23 : Initializable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.0;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To initialize the implementation contract, you can either invoke the
 * initializer manually, or you can include a constructor to automatically mark it as initialized when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() initializer {}
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     */
    bool private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Modifier to protect an initializer function from being invoked twice.
     */
    modifier initializer() {
        // If the contract is initializing we ignore whether _initialized is set in order to support multiple
        // inheritance patterns, but we only do this in the context of a constructor, because in other contexts the
        // contract may have been reentered.
        require(_initializing ? _isConstructor() : !_initialized, "Initializable: contract is already initialized");

        bool isTopLevelCall = !_initializing;
        if (isTopLevelCall) {
            _initializing = true;
            _initialized = true;
        }

        _;

        if (isTopLevelCall) {
            _initializing = false;
        }
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} modifier, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    function _isConstructor() private view returns (bool) {
        return !AddressUpgradeable.isContract(address(this));
    }
}

File 22 of 23 : ContextUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

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

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

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

File 23 of 23 : AddressUpgradeable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

Settings
{
  "remappings": [
    "@aave/core-v3/=lib/aave-v3-core/",
    "@aave/periphery-v3/=lib/aave-v3-periphery/",
    "@config/=config/ropsten/compound/",
    "@contracts/=contracts/",
    "@ensdomains/=node_modules/@ensdomains/",
    "@morpho/data-structures/=lib/data-structures/",
    "@openzeppelin/=node_modules/@openzeppelin/",
    "@rari-capital/solmate/=lib/solmate/",
    "@uniswap/=node_modules/@uniswap/",
    "aave-v3-core/=lib/aave-v3-core/",
    "aave-v3-periphery/=lib/aave-v3-periphery/contracts/",
    "base64-sol/=node_modules/base64-sol/",
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "hardhat-deploy/=node_modules/hardhat-deploy/",
    "hardhat/=node_modules/hardhat/",
    "solmate/=lib/solmate/src/",
    "compound/=contracts/compound/",
    "test/=test/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract ABI

[{"inputs":[],"name":"AccountAlreadyInserted","type":"error"},{"inputs":[],"name":"AccountDoesNotExist","type":"error"},{"inputs":[],"name":"AddressIsZero","type":"error"},{"inputs":[],"name":"AddressIsZero","type":"error"},{"inputs":[],"name":"AmountAboveWhatAllowedToRepay","type":"error"},{"inputs":[],"name":"AmountIsZero","type":"error"},{"inputs":[],"name":"BorrowOnCompoundFailed","type":"error"},{"inputs":[],"name":"CompoundOracleFailed","type":"error"},{"inputs":[],"name":"LowLevelDelegateCallFailed","type":"error"},{"inputs":[],"name":"MarketNotCreated","type":"error"},{"inputs":[],"name":"MarketPaused","type":"error"},{"inputs":[],"name":"MintOnCompoundFailed","type":"error"},{"inputs":[],"name":"RedeemOnCompoundFailed","type":"error"},{"inputs":[],"name":"RepayOnCompoundFailed","type":"error"},{"inputs":[],"name":"SameBlockBorrowRepay","type":"error"},{"inputs":[],"name":"UnauthorisedBorrow","type":"error"},{"inputs":[],"name":"UnauthorisedLiquidate","type":"error"},{"inputs":[],"name":"UnauthorisedWithdraw","type":"error"},{"inputs":[],"name":"UserNotMemberOfMarket","type":"error"},{"inputs":[],"name":"ValueIsZero","type":"error"},{"inputs":[],"name":"WithdrawTooSmall","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_borrower","type":"address"},{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceOnPool","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceInP2P","type":"uint256"}],"name":"Borrowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_balanceOnPool","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceInP2P","type":"uint256"}],"name":"BorrowerPositionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_liquidator","type":"address"},{"indexed":true,"internalType":"address","name":"_liquidated","type":"address"},{"indexed":true,"internalType":"address","name":"_poolTokenBorrowedAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountRepaid","type":"uint256"},{"indexed":true,"internalType":"address","name":"_poolTokenCollateralAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amountSeized","type":"uint256"}],"name":"Liquidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_p2pSupplyAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_p2pBorrowAmount","type":"uint256"}],"name":"P2PAmountsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_p2pBorrowDelta","type":"uint256"}],"name":"P2PBorrowDeltaUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_p2pSupplyDelta","type":"uint256"}],"name":"P2PSupplyDeltaUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_repayer","type":"address"},{"indexed":true,"internalType":"address","name":"_onBehalf","type":"address"},{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceOnPool","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceInP2P","type":"uint256"}],"name":"Repaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_supplier","type":"address"},{"indexed":true,"internalType":"address","name":"_onBehalf","type":"address"},{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceOnPool","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceInP2P","type":"uint256"}],"name":"Supplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_user","type":"address"},{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_balanceOnPool","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceInP2P","type":"uint256"}],"name":"SupplierPositionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_supplier","type":"address"},{"indexed":true,"internalType":"address","name":"_receiver","type":"address"},{"indexed":true,"internalType":"address","name":"_poolTokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceOnPool","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_balanceInP2P","type":"uint256"}],"name":"Withdrawn","type":"event"},{"inputs":[],"name":"CTOKEN_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_BASIS_POINTS","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_CLAIMABLE_RESERVE","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WAD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"borrowBalanceInOf","outputs":[{"internalType":"uint256","name":"inP2P","type":"uint256"},{"internalType":"uint256","name":"onPool","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolTokenAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_maxGasForMatching","type":"uint256"}],"name":"borrowLogic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cEth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"comptroller","outputs":[{"internalType":"contract IComptroller","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultMaxGasForMatching","outputs":[{"internalType":"uint64","name":"supply","type":"uint64"},{"internalType":"uint64","name":"borrow","type":"uint64"},{"internalType":"uint64","name":"withdraw","type":"uint64"},{"internalType":"uint64","name":"repay","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"deltas","outputs":[{"internalType":"uint256","name":"p2pSupplyDelta","type":"uint256"},{"internalType":"uint256","name":"p2pBorrowDelta","type":"uint256"},{"internalType":"uint256","name":"p2pSupplyAmount","type":"uint256"},{"internalType":"uint256","name":"p2pBorrowAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dustThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"enteredMarkets","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllMarkets","outputs":[{"internalType":"address[]","name":"marketsCreated_","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getEnteredMarkets","outputs":[{"internalType":"address[]","name":"enteredMarkets_","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolTokenAddress","type":"address"},{"internalType":"enum Types.PositionType","name":"_positionType","type":"uint8"}],"name":"getHead","outputs":[{"internalType":"address","name":"head","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolTokenAddress","type":"address"},{"internalType":"enum Types.PositionType","name":"_positionType","type":"uint8"},{"internalType":"address","name":"_user","type":"address"}],"name":"getNext","outputs":[{"internalType":"address","name":"next","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incentivesVault","outputs":[{"internalType":"contract IIncentivesVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRatesManager","outputs":[{"internalType":"contract IInterestRatesManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastBorrowBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"lastPoolIndexes","outputs":[{"internalType":"uint32","name":"lastUpdateBlockNumber","type":"uint32"},{"internalType":"uint112","name":"lastSupplyPoolIndex","type":"uint112"},{"internalType":"uint112","name":"lastBorrowPoolIndex","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolTokenBorrowedAddress","type":"address"},{"internalType":"address","name":"_poolTokenCollateralAddress","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"liquidateLogic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"marketParameters","outputs":[{"internalType":"uint16","name":"reserveFactor","type":"uint16"},{"internalType":"uint16","name":"p2pIndexCursor","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"marketStatus","outputs":[{"internalType":"bool","name":"isCreated","type":"bool"},{"internalType":"bool","name":"isPaused","type":"bool"},{"internalType":"bool","name":"isPartiallyPaused","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"marketsCreated","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSortedUsers","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"p2pBorrowIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"p2pDisabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"p2pSupplyIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionsManager","outputs":[{"internalType":"contract IPositionsManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_poolTokenAddress","type":"address"},{"internalType":"address","name":"_repayer","type":"address"},{"internalType":"address","name":"_onBehalf","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_maxGasForMatching","type":"uint256"}],"name":"repayLogic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rewardsManager","outputs":[{"internalType":"contract IRewardsManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"supplyBalanceInOf","outputs":[{"internalType":"uint256","name":"inP2P","type":"uint256"},{"internalType":"uint256","name":"onPool","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolTokenAddress","type":"address"},{"internalType":"address","name":"_supplier","type":"address"},{"internalType":"address","name":"_onBehalf","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_maxGasForMatching","type":"uint256"}],"name":"supplyLogic","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasuryVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolTokenAddress","type":"address"}],"name":"updateP2PIndexes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userMembership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"wEth","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_poolTokenAddress","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_supplier","type":"address"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_maxGasForMatching","type":"uint256"}],"name":"withdrawLogic","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b50600054610100900460ff166200002f5760005460ff161562000039565b62000039620000de565b620000a15760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b600054610100900460ff16158015620000c4576000805461ffff19166101011790555b8015620000d7576000805461ff00191690555b506200010b565b6000620000f630620000fc60201b62001c921760201c565b15905090565b6001600160a01b03163b151590565b61587b806200011b6000396000f3fe608060405234801561001057600080fd5b50600436106102535760003560e01c80639fab103611610146578063d59c9eb6116100c3578063e501ed0411610087578063e501ed04146106cb578063e61c6d6f146106fd578063e8462e8f14610706578063f2f4ca161461070f578063f2fde38b1461077b578063f4ea93d81461078e57600080fd5b8063d59c9eb6146105ce578063db0577fd14610623578063defe205314610692578063df6d9212146106a5578063e34b5145146106b857600080fd5b8063b0772d0b1161010a578063b0772d0b1461056d578063b24be68714610575578063b59ec47814610588578063b6f2bf1c146105a8578063c2af9787146105bb57600080fd5b80639fab1036146104cc578063a086fc22146104df578063a44026a314610534578063ac0b4b1214610547578063af8b1c6f1461055a57600080fd5b8063789caa3e116101d45780638ccb720b116101985780638ccb720b146103f15780638da5cb5b14610411578063947574ac1461042257806396bd512c146104695780639df5a1f2146104b257600080fd5b8063789caa3e146103855780637907016a146103985780637f3ad056146103ab578063854f7ebb146103be57806385d7334d146103de57600080fd5b80635fe3b5671161021b5780635fe3b567146103115780636a14602414610324578063715018a61461033357806371f3cf5c1461033b578063720ceb021461035757600080fd5b806320c342d9146102585780632ebf4be0146102905780633528e4ce146102be57806352f0f814146102d35780635acff027146102fe575b600080fd5b61027b6102663660046153ef565b60a36020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6102b061029e3660046153ef565b60a56020526000908152604090205481565b604051908152602001610287565b6102d16102cc3660046153ef565b610797565b005b60aa546102e6906001600160a01b031681565b6040516001600160a01b039091168152602001610287565b6102e661030c36600461541b565b6107df565b60ae546102e6906001600160a01b031681565b6102b0670de0b6b3a764000081565b6102d16108d8565b61034461232881565b60405161ffff9091168152602001610287565b61027b610365366004615450565b60a060209081526000928352604080842090915290825290205460ff1681565b6102e6610393366004615489565b610943565b6102d16103a63660046154d2565b610a41565b60ad546102e6906001600160a01b031681565b6102b06103cc3660046153ef565b60a46020526000908152604090205481565b6102d16103ec366004615523565b610e84565b6104046103ff3660046153ef565b6113aa565b604051610287919061557e565b6033546001600160a01b03166102e6565b610454610430366004615450565b609f6020908152600092835260408084209091529082529020805460019091015482565b60408051928352602083019190915201610287565b6104976104773660046153ef565b60a76020526000908152604090205461ffff808216916201000090041682565b6040805161ffff938416815292909116602083015201610287565b6104ba600881565b60405160ff9091168152602001610287565b6102d16104da3660046155cb565b611420565b6105146104ed3660046153ef565b60a96020526000908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610287565b60b0546102e6906001600160a01b031681565b6102e6610555366004615600565b6119a8565b60af546102e6906001600160a01b031681565b6104046119d2565b60ab546102e6906001600160a01b031681565b6102b06105963660046153ef565b60b26020526000908152604090205481565b6102d16105b6366004615619565b611a34565b6102d16105c9366004615523565b611afb565b6106046105dc3660046153ef565b60a86020526000908152604090205460ff808216916101008104821691620100009091041683565b6040805193151584529115156020840152151590820152606001610287565b6106666106313660046153ef565b60a66020526000908152604090205463ffffffff8116906001600160701b036401000000008204811691600160901b90041683565b6040805163ffffffff90941684526001600160701b039283166020850152911690820152606001610287565b60ac546102e6906001600160a01b031681565b60b1546102e6906001600160a01b031681565b6102e66106c6366004615674565b611b8f565b6104546106d9366004615450565b609e6020908152600092835260408084209091529082529020805460019091015482565b6102b060975481565b6102b060985481565b6099546107479067ffffffffffffffff80821691680100000000000000008104821691600160801b8204811691600160c01b90041684565b6040805167ffffffffffffffff95861681529385166020850152918416918301919091529091166060820152608001610287565b6102d16107893660046153ef565b611bc7565b61034461271081565b6001600160a01b038116600090815260a86020526040902054819060ff166107d2576040516396e1352960e01b815260040160405180910390fd5b6107db82611ca1565b5050565b6000808260038111156107f4576107f46156a0565b0361081f576001600160a01b038381166000908152609a6020526040902060010154165b90506108d2565b6001826003811115610833576108336156a0565b0361085b576001600160a01b038381166000908152609b602052604090206001015416610818565b600282600381111561086f5761086f6156a0565b03610897576001600160a01b038381166000908152609c602052604090206001015416610818565b60038260038111156108ab576108ab6156a0565b036108d2576001600160a01b038381166000908152609d6020526040902060010154165b90505b92915050565b6033546001600160a01b031633146109375760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6109416000611cf3565b565b600080836003811115610958576109586156a0565b03610986576001600160a01b0384166000908152609a6020526040902061097f9083611d45565b9050610a3a565b600183600381111561099a5761099a6156a0565b036109c1576001600160a01b0384166000908152609b6020526040902061097f9083611d45565b60028360038111156109d5576109d56156a0565b036109fc576001600160a01b0384166000908152609c6020526040902061097f9083611d45565b6003836003811115610a1057610a106156a0565b03610a3a576001600160a01b0384166000908152609d60205260409020610a379083611d45565b90505b9392505050565b6001600160a01b03808516600090815260a0602090815260408083209386168352929052205460ff161580610a9c57506001600160a01b03808416600090815260a0602090815260408083209386168352929052205460ff16155b15610aba576040516301187a4360e61b815260040160405180910390fd5b610ac384611ca1565b610acc83611ca1565b610ada826000806000611d68565b610af75760405163b3165ffd60e01b815260040160405180910390fd5b610b296040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b610b338584611f29565b60208083019190915260ae546040805163743aaa2360e11b81529051610bb4936001600160a01b039093169263e875544692600480820193918290030181865afa158015610b85573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba991906156b6565b602083015190612002565b821115610bd457604051633b1b989f60e01b815260040160405180910390fd5b610be2853385856000612021565b60ae54604080516307dc0d1d60e41b815290516000926001600160a01b031691637dc0d1d09160048083019260209291908290030181865afa158015610c2c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5091906156cf565b60405163fc57d4df60e01b81526001600160a01b0387811660048301529192509082169063fc57d4df90602401602060405180830381865afa158015610c9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cbe91906156b6565b825260405163fc57d4df60e01b81526001600160a01b03878116600483015282169063fc57d4df90602401602060405180830381865afa158015610d06573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2a91906156b6565b606083015281511580610d3f57506060820151155b15610d5d57604051634b6b62e560e01b815260040160405180910390fd5b610e0c610dfd8360000151610df78560600151610df160ae60009054906101000a90046001600160a01b03166001600160a01b0316634ada90af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dc6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dea91906156b6565b8990612002565b90612002565b906129e7565b610e078787612a19565b612ae8565b60808301819052610e2290869086336000612afe565b60808201516040805133815260208101869052908101919091526001600160a01b0380871691888216918716907fc2c75a73164c2efcbb9f74bfa511cd0866489d90687831a7217b3dbeeb6970889060600160405180910390a4505050505050565b6001600160a01b038316610eab5760405163867915ab60e01b815260040160405180910390fd5b81600003610ecc576040516310eb483f60e21b815260040160405180910390fd5b610ed585611ca1565b610edf85846134a0565b6000610eea8661352c565b9050610f016001600160a01b0382168630866135bd565b6001600160a01b038616600090815260a960209081526040808320815160608101835284815292830184905290820192909252876001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f72573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9691906156b6565b6020820152848152600182015415611092576000610fc58260200151846001015461200290919063ffffffff16565b8251909150811115611018578151604083018051610fe4908390615702565b90525060208201518251610ff7916129e7565b83600101600082825461100a919061571a565b909155505060008252611049565b808260400181815161102a9190615702565b9052506000600184015581518190839061104590839061571a565b9052505b886001600160a01b03167f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df6846001015460405161108891815260200190565b60405180910390a2505b8051158015906110bb57506001600160a01b038816600090815260a3602052604090205460ff16155b80156110fa57506001600160a01b0388166000908152609d602052604081206110ee90600101546001600160a01b031690565b6001600160a01b031614155b1561118157600061111089836000015187613640565b509050801561117f57808260400181815161112b9190615702565b90525081518190839061113f90839061571a565b9052506001600160a01b038916600090815260a560205260409020546111669082906129e7565b8360030160008282546111799190615702565b90915550505b505b60408101511561125e576001600160a01b038816600090815260a4602052604080822054908301516111b2916129e7565b9050808360020160008282546111c89190615702565b90915550506001600160a01b03808a166000908152609e60209081526040808320938b1683529290529081208054839290611204908490615702565b9250508190555061121a898584604001516138d9565b886001600160a01b031660008051602061582683398151915284600201548560030154604051611254929190918252602082015260400190565b60405180910390a2505b805115611320576112d2886001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ca91906156b6565b8251906129e7565b6001600160a01b03808a166000908152609e60209081526040808320938b168352929052908120600101805490919061130c908490615702565b909155505080516113209089908590613ac0565b61132a8887613c0c565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518c8152938401919091529082015291929091908a16907f11adb3570ba55fd255b1f04252ca0071ae6639c86d4fd69e7c1bf1688afb493f906060015b60405180910390a45050505050505050565b6001600160a01b038116600090815260a1602090815260409182902080548351818402810184019094528084526060939283018282801561141457602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113f6575b50505050509050919050565b81600003611441576040516310eb483f60e21b815260040160405180910390fd5b61144a83611ca1565b61145483336134a0565b33600081815260b2602052604081204390556114739190859085611d68565b156114915760405163df9db46360e01b815260040160405180910390fd5b600061149c8461352c565b6001600160a01b038516600081815260a960209081526040808320815163182df0f560e01b815291519596508895939490938593919263182df0f59260048083019391928290030181865afa1580156114f9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061151d91906156b6565b604051633af9e66960e01b81523060048201529091506000906001600160a01b038a1690633af9e669906024016020604051808303816000875af1158015611569573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061158d91906156b6565b8354909150156116685782546000906115a69084612002565b9050858111806115b557508181115b156116095760006115c68784612ae8565b90506115d28187615702565b95506115de81856129e7565b8560000160008282546115f1919061571a565b909155506116019050818861571a565b965050611626565b6116138186615702565b600085559450611623818761571a565b95505b83546040519081526001600160a01b038b16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a2505b60008511801561169157506001600160a01b038916600090815260a3602052604090205460ff16155b80156116d057506001600160a01b0389166000908152609b602052604081206116c490600101546001600160a01b031690565b6001600160a01b031614155b156117685760006116ef8a6116e988610e07898761571a565b8a613e1f565b5090508015611766576117028186615702565b945061170e818761571a565b6001600160a01b038b16600090815260a460205260409020549096506117359082906129e7565b6001600160a01b038b16600090815260a9602052604081206002018054909190611760908490615702565b90915550505b505b831561185f576001600160a01b038916600090815260a560205260408120546117929086906129e7565b6001600160a01b038b16600090815260a960205260408120600301805492935083929091906117c2908490615702565b90915550506001600160a01b038a166000908152609f60209081526040808320338452909152812080548392906117fa908490615702565b9091555050600284015460038501546040516001600160a01b038d16926000805160206158268339815191529261183992918252602082015260400190565b60405180910390a2600061184d86856129e7565b111561185d5761185d8a8661409a565b505b8415611917576118d1896001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118a6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ca91906156b6565b86906129e7565b6001600160a01b038a166000908152609f6020908152604080832033845290915281206001018054909190611907908490615702565b90915550611917905089866141a6565b611921893361422f565b6119356001600160a01b038716338a61440c565b6001600160a01b0389166000818152609f6020908152604080832033808552908352928190206001810154905482518e81529384019190915282820152517fc1cba78646fef030830d099fc25cb498953709c9d47d883848f81fd207174c9f9181900360600190a3505050505050505050565b60a281815481106119b857600080fd5b6000918252602090912001546001600160a01b0316905081565b606060a2805480602002602001604051908101604052809291908181526020018280548015611a2a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611a0c575b5050505050905090565b83600003611a55576040516310eb483f60e21b815260040160405180910390fd5b6001600160a01b03808616600090815260a0602090815260408083209387168352929052205460ff16611a9b576040516301187a4360e61b815260040160405180910390fd5b611aa485611ca1565b6000611ab9611ab38786612a19565b86612ae8565b9050611ac88487836000611d68565b15611ae657604051630cba3c5f60e21b815260040160405180910390fd5b611af38682868686612afe565b505050505050565b81600003611b1c576040516310eb483f60e21b815260040160405180910390fd5b6001600160a01b03808616600090815260a0602090815260408083209387168352929052205460ff16611b62576040516301187a4360e61b815260040160405180910390fd5b611b6b85611ca1565b6000611b80611b7a8786611f29565b84612ae8565b9050611af38686868486612021565b60a16020528160005260406000208181548110611bab57600080fd5b6000918252602090912001546001600160a01b03169150829050565b6033546001600160a01b03163314611c215760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161092e565b6001600160a01b038116611c865760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161092e565b611c8f81611cf3565b50565b6001600160a01b03163b151590565b604080516001600160a01b038381166024808401919091528351808403909101815260449092019092526020810180516001600160e01b0316631a94726760e11b17905260ad546107db92169061448a565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0390811660009081526020929092526040909120600101541690565b60008060ae60009054906101000a90046001600160a01b03166001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dbe573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de291906156cf565b6001600160a01b038716600090815260a16020908152604080832054815160a08101835284815292830184905290820183905260608201839052608082018390529293509080805b84811015611f1b576001600160a01b038b16600090815260a160205260408120805483908110611e5c57611e5c615731565b6000918252602090912001546001600160a01b03169050611e7e8c8289614524565b9450846020015184611e909190615702565b9350846040015183611ea29190615702565b9250806001600160a01b03168b6001600160a01b031603611f12578815611ee0576060850151611ed3908a90612002565b611edd9084615702565b92505b8915611f1257611f058560800151610df187606001518d61200290919063ffffffff16565b611f0f908561571a565b93505b50600101611e2a565b501198975050505050505050565b6000611fbf836001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f6c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f9091906156b6565b6001600160a01b038086166000908152609f602090815260408083209388168352929052206001015490612002565b6001600160a01b03808516600090815260a56020908152604080832054609f8352818420948816845293909152902054611ff891612002565b6108cf9190615702565b6000670de0b6b3a76400006120178385615747565b6108cf9190615766565b6001600160a01b038316600090815260b2602052604090205443900361205a5760405163dff88f5160e01b815260040160405180910390fd5b60006120658661352c565b905061207c6001600160a01b0382168630866135bd565b6120cb6040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60208082018590528382526040805163aa5af0fd60e01b815290516001600160a01b038a169263aa5af0fd92600480820193918290030181865afa158015612117573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213b91906156b6565b60608201526001600160a01b038088166000908152609f602090815260408083209389168352929052206001015460c08201819052156122f257606081015160c082015161218891612002565b604082018190526020820151101561229c576020810151610100820181905260c082015160608301516121be92610e07916129e7565b6001600160a01b038089166000908152609f60209081526040808320938a16835292905290812060010180549091906121f890849061571a565b909155506122089050878661422f565b61221887838361010001516138d9565b61222287866146a5565b6001600160a01b038781166000818152609f602090815260408083208a8616808552908352928190206001810154905482518b815293840191909152828201525192939192918a16917f7b417e520d2b905fc5a1689d29d329358dd55efc60ed115aa165b0a2b64232c69181900360600190a450506129e0565b604081015161010082018190526020820180516122ba90839061571a565b9052506001600160a01b038088166000908152609f602090815260408083209389168352929052908120600101556122f2878661422f565b6001600160a01b03808816600081815260a96020908152604080832060a4835281842054608088015284845260a583528184205460a08801908152948452609f8352818420958b168452948252909120549151908401516123589291610e0791906129e7565b6001600160a01b03808a166000908152609f60209081526040808320938b168352929052908120805490919061238f90849061571a565b9091555061239f9050888761422f565b600082602001511180156123b7575060008160010154115b1561254d5760006123d98360600151836001015461200290919063ffffffff16565b9050826020015181111561246357606083015160208401516123fa916129e7565b82600101600082825461240d919061571a565b909155505060a08301516020840151612425916129e7565b826003016000828254612438919061571a565b9091555050602083015161010084018051612454908390615702565b905250600060208401526124c2565b6000600183015560a083015161247a9082906129e7565b82600301600082825461248d919061571a565b925050819055508083610100018181516124a79190615702565b9052506020830180518291906124be90839061571a565b9052505b886001600160a01b03167f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df6836001015460405161250191815260200190565b60405180910390a2886001600160a01b031660008051602061582683398151915283600201548460030154604051612543929190918252602082015260400190565b60405180910390a2505b6020820151156126c4576126216125758360600151836001015461200290919063ffffffff16565b60a0840151600384015461258891612002565b612592919061571a565b6125ff8a6001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125f791906156b6565b845490612002565b6080850151600285015461261291612002565b61261c919061571a565b61491d565b60e08301819052156126c45760006126418360e001518460200151612ae8565b90508083602001818151612655919061571a565b90525060a08301516126689082906129e7565b82600301600082825461267b919061571a565b9091555050600282015460038301546040516001600160a01b038c1692600080516020615826833981519152926126ba92918252602082015260400190565b60405180910390a2505b600082602001511180156126f157506001600160a01b038816600090815260a3602052604090205460ff16155b801561273057506001600160a01b0388166000908152609d6020526040812061272490600101546001600160a01b031690565b6001600160a01b031614155b156127b05760008061274b8a85602001518660000151613640565b91509150808460000151116127635760008452612779565b8084600001818151612775919061571a565b9052505b81156127ad578184602001818151612791919061571a565b905250610100840180518391906127a9908390615702565b9052505b50505b6127c088848461010001516138d9565b6020820151156129645760006127df8984602001518560000151614938565b905082602001518110156128be57612862896001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa15801561282e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061285291906156b6565b828560200151610df7919061571a565b8260000160008282546128759190615702565b909155505060018201546040519081526001600160a01b038a16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a25b60808301516128ce9082906129e7565b8260020160008282546128e1919061571a565b909155505060a083015160208401516128f9916129e7565b82600301600082825461290c919061571a565b9091555050600282015460038301546040516001600160a01b038c16926000805160206158268339815191529261294b92918252602082015260400190565b60405180910390a261296289858560200151613ac0565b505b61296e88876146a5565b6001600160a01b038881166000818152609f602090815260408083208b8616808552908352928190206001810154905482518c8152938401919091529082015291929091908a16907f7b417e520d2b905fc5a1689d29d329358dd55efc60ed115aa165b0a2b64232c690606001611398565b5050505050565b6000670de0b6b3a7640000826129fd8583615747565b612a0f90670de0b6b3a7640000615747565b6120179190615766565b6000612aaf836001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a8091906156b6565b6001600160a01b038086166000908152609e602090815260408083209388168352929052206001015490612002565b6001600160a01b03808516600090815260a46020908152604080832054609e8352818420948816845293909152902054611ff891612002565b6000818310612af757816108cf565b5090919050565b83600003612b1f576040516310eb483f60e21b815260040160405180910390fd5b612b7960405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b6001600160a01b03861660e0820152612b918661352c565b6001600160a01b0390811660c08301528582526020820183905260e0820151604051633af9e66960e01b8152306004820152911690633af9e669906024016020604051808303816000875af1158015612bee573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c1291906156b6565b8160800181815250508060e001516001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612c5d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c8191906156b6565b60408201819052612c939086906129e7565b600003612cb3576040516393c76c6f60e01b815260040160405180910390fd5b6001600160a01b038087166000908152609e60209081526040808320938816835292905220600101548015612ec0576000612cfb83604001518361200290919063ffffffff16565b8351909150811180612d105750826080015181115b15612d9857612d2783600001518460800151612ae8565b60a0840181905283518490612d3d90839061571a565b905250604083015160a0840151612d53916129e7565b6001600160a01b03808a166000908152609e60209081526040808320938b1683529290529081206001018054909190612d8d90849061571a565b90915550612ddf9050565b60a08301819052825181908490612db090839061571a565b9052506001600160a01b038089166000908152609e60209081526040808320938a168352929052908120600101555b612de98887613c0c565b8251600003612ebe57612dfc88876146a5565b6000612e1984604001518560a001516129e790919063ffffffff16565b1115612e2d57612e2d888460a0015161409a565b60c0830151612e46906001600160a01b0316868961440c565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518e815293840191909152828201525192938916927f378f9d375cd79e36c19c26a9e57791fe7cd5953b61986c01ebf980c0efb928019181900360600190a45050506129e0565b505b6001600160a01b03808816600081815260a96020908152604080832060a483528184205460608901908152948452609e8352818420958b1684529490915290205490518451612f149291610e0791906129e7565b6001600160a01b03808a166000908152609e60209081526040808320938b1683529290529081208054909190612f4b90849061571a565b90915550612f5b90508887613c0c565b825115801590612f6b5750805415155b156131305760408301518154600091612f849190612002565b8451909150811180612fa857508360a001518460800151612fa5919061571a565b81115b15613056576000612fcb85600001518660a001518760800151610e07919061571a565b9050612fe48560400151826129e790919063ffffffff16565b836000016000828254612ff7919061571a565b9091555050606085015161300c9082906129e7565b83600201600082825461301f919061571a565b92505081905550808560a0018181516130389190615702565b90525084518190869061304c90839061571a565b9052506130ac9050565b808460a0018181516130689190615702565b90525083518190859061307c90839061571a565b9052506000825560608401516130939082906129e7565b8260020160008282546130a6919061571a565b90915550505b81546040519081526001600160a01b038a16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a2886001600160a01b031660008051602061582683398151915283600201548460030154604051613126929190918252602082015260400190565b60405180910390a2505b82511580159061315957506001600160a01b038816600090815260a3602052604090205460ff16155b801561319857506001600160a01b0388166000908152609b6020526040812061318c90600101546001600160a01b031690565b6001600160a01b031614155b15613231576000806131ca8a6131c087600001518860a001518960800151610e07919061571a565b8760200151613e1f565b91509150808560200151116131e557600060208601526131fb565b80856020018181516131f7919061571a565b9052505b811561322e578185600001818151613213919061571a565b90525060a08501805183919061322a908390615702565b9052505b50505b600061324e84604001518560a001516129e790919063ffffffff16565b111561326257613262888460a0015161409a565b82511561340e57600061327a89856000015187614bbb565b8451909150811015613358576132fc8460e001516001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156132cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132ef91906156b6565b8551610df790849061571a565b82600101600082825461330f9190615702565b909155505060038201546040519081526001600160a01b038a16907f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df69060200160405180910390a25b60608401518451613368916129e7565b82600201600082825461337b919061571a565b90915550506001600160a01b038916600090815260a560205260409020546133a49082906129e7565b8260030160008282546133b7919061571a565b9091555050600282015460038301546040516001600160a01b038c1692600080516020615826833981519152926133f692918252602082015260400190565b60405180910390a261340c8985600001516141a6565b505b61341888876146a5565b60c0830151613431906001600160a01b0316868961440c565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518e8152938401919091529082015291928816917f378f9d375cd79e36c19c26a9e57791fe7cd5953b61986c01ebf980c0efb9280190606001611398565b6001600160a01b03808316600090815260a0602090815260408083209385168352929052205460ff166107db576001600160a01b03808316600081815260a0602090815260408083209486168352938152838220805460ff1916600190811790915560a18252938220805494850181558252902090910180546001600160a01b03191690911790555050565b60b0546000906001600160a01b039081169083160361355657505060b1546001600160a01b031690565b816001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015613594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d291906156cf565b919050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806129e05760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b604482015260640161092e565b60008082600003613656575060009050806138d1565b6136886040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b856001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136ea91906156b6565b6040808301919091526001600160a01b038716600090815260a5602052908120548252805a60808401525b868510801561375a57506001600160a01b0388166000908152609d6020526040812061374b90600101546001600160a01b031690565b9250826001600160a01b031614155b80156137745750855a8460800151613772919061571a565b105b156138bb57506001600160a01b038088166000908152609f6020908152604080832093851683529290528190209083015160018201546137b391612002565b6060840152600080806137c6888b61571a565b90508086606001511161380857855160608701516137e3916129e7565b84546137ef9190615702565b91508560600151886138019190615702565b9750613849565b60408601516138189082906129e7565b8460010154613827919061571a565b86519093506138379082906129e7565b84546138439190615702565b91508997505b6001840183905581845561385d8b8661422f565b8a6001600160a01b0316856001600160a01b03167f0aec3812ec00f2d2f0eacc89fd13923091a68f30c3b3d0336e364544322b975885856040516138ab929190918252602082015260400190565b60405180910390a3505050613715565b5a83608001516138cb919061571a565b93505050505b935093915050565b6040516305eff7ef60e21b81523060048201526139499082906001600160a01b038616906317bfdfbc906024016020604051808303816000875af1158015613925573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0791906156b6565b90508015613abb5760b0546001600160a01b0390811690841603613a1e5760b154604051632e1a7d4d60e01b8152600481018390526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b1580156139ad57600080fd5b505af11580156139c1573d6000803e3d6000fd5b50505050826001600160a01b0316634e4d9fea826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613a0057600080fd5b505af1158015613a14573d6000803e3d6000fd5b5050505050505050565b613a326001600160a01b0383168483614e28565b60405163073a938160e11b8152600481018290526001600160a01b03841690630e752702906024016020604051808303816000875af1158015613a79573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a9d91906156b6565b15613abb57604051637112354b60e01b815260040160405180910390fd5b505050565b60b0546001600160a01b0390811690841603613b6f5760b154604051632e1a7d4d60e01b8152600481018390526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015613b1c57600080fd5b505af1158015613b30573d6000803e3d6000fd5b50505050826001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b158015613a0057600080fd5b613b836001600160a01b0383168483614e28565b60405163140e25ad60e31b8152600481018290526001600160a01b0384169063a0712d68906024016020604051808303816000875af1158015613bca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613bee91906156b6565b15613abb5760405163757d648760e11b815260040160405180910390fd5b6001600160a01b038281166000818152609e6020908152604080832094861680845294825280832060018101549054858552609b84528285208786528452828520600290810154968652609a855283862097865296909352922090930154609854919392918411613ca7576001600160a01b038087166000908152609e60209081526040808320938916835292905290812060010181905593505b838214613d04578115613cd6576001600160a01b0386166000908152609b60205260409020613cd69086614e9f565b8315613d04576097546001600160a01b0387166000908152609b60205260409020613d049187908790615034565b6098548311613d3a576001600160a01b038087166000908152609e60209081526040808320938916835292905290812081905592505b828114613d97578015613d69576001600160a01b0386166000908152609a60205260409020613d699086614e9f565b8215613d97576097546001600160a01b0387166000908152609a60205260409020613d979187908690615034565b60ac546001600160a01b031615611af35760ac5460405163636c55d360e01b81526001600160a01b0387811660048301528881166024830152604482018590529091169063636c55d3906064015b600060405180830381600087803b158015613dff57600080fd5b505af1158015613e13573d6000803e3d6000fd5b50505050505050505050565b60008082600003613e35575060009050806138d1565b613e676040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b856001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015613ea5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613ec991906156b6565b6040808301919091526001600160a01b038716600090815260a4602052908120548252805a60808401525b8685108015613f3957506001600160a01b0388166000908152609b60205260408120613f2a90600101546001600160a01b031690565b9250826001600160a01b031614155b8015613f535750855a8460800151613f51919061571a565b105b156138bb57506001600160a01b038088166000908152609e602090815260408083209385168352929052819020908301516001820154613f9291612002565b606084015260008080613fa5888b61571a565b905080866060015111613fe75785516060870151613fc2916129e7565b8454613fce9190615702565b9150856060015188613fe09190615702565b9750614028565b6040860151613ff79082906129e7565b8460010154614006919061571a565b86519093506140169082906129e7565b84546140229190615702565b91508997505b6001840183905581845561403c8b86613c0c565b8a6001600160a01b0316856001600160a01b03167f76908587112671ab2dcd9323f0d9b27d193156f95fe5e1251411a151c20e82dd858560405161408a929190918252602082015260400190565b60405180910390a3505050613ef4565b60405163852a12e360e01b8152600481018290526001600160a01b0383169063852a12e3906024016020604051808303816000875af11580156140e1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061410591906156b6565b1561412357604051637d47a0c360e01b815260040160405180910390fd5b60b0546001600160a01b03908116908316036107db5760b160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561418957600080fd5b505af115801561419d573d6000803e3d6000fd5b50505050505050565b60405163317afabb60e21b8152600481018290526001600160a01b0383169063c5ebeaec906024016020604051808303816000875af11580156141ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061421191906156b6565b15614123576040516302738d6b60e11b815260040160405180910390fd5b6001600160a01b038281166000818152609f6020908152604080832094861680845294825280832060018101549054858552609d84528285208786528452828520600290810154968652609c8552838620978652969093529220909301546098549193929184116142ca576001600160a01b038087166000908152609f60209081526040808320938916835292905290812060010181905593505b8382146143275781156142f9576001600160a01b0386166000908152609d602052604090206142f99086614e9f565b8315614327576097546001600160a01b0387166000908152609d602052604090206143279187908790615034565b609854831161435d576001600160a01b038087166000908152609f60209081526040808320938916835292905290812081905592505b8281146143ba57801561438c576001600160a01b0386166000908152609c6020526040902061438c9086614e9f565b82156143ba576097546001600160a01b0387166000908152609c602052604090206143ba9187908690615034565b60ac546001600160a01b031615611af35760ac5460405163a9de645d60e01b81526001600160a01b0387811660048301528881166024830152604482018590529091169063a9de645d90606401613de5565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806144845760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015260640161092e565b50505050565b6060600080846001600160a01b0316846040516144a79190615788565b600060405180830381855af49150503d80600081146144e2576040519150601f19603f3d011682016040523d82523d6000602084013e6144e7565b606091505b509150915081156144fb5791506108d29050565b80511561450b5780518082602001fd5b60405163037b81af60e11b815260040160405180910390fd5b6145566040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60405163fc57d4df60e01b81526001600160a01b03848116600483015283169063fc57d4df90602401602060405180830381865afa15801561459c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145c091906156b6565b606082018190526000036145e757604051634b6b62e560e01b815260040160405180910390fd5b60ae54604051638e8f294b60e01b81526001600160a01b03858116600483015290911690638e8f294b90602401606060405180830381865afa158015614631573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061465591906157d3565b50608083015250606081015161466f90610df18587612a19565b8152606081015161468490610df18587611f29565b60408201526080810151815161469991612002565b60208201529392505050565b6001600160a01b03808316600090815260a0602090815260408083209385168352929052205460ff1680156146fd57506001600160a01b038083166000908152609e6020908152604080832093851683529290522054155b801561472f57506001600160a01b038083166000908152609e6020908152604080832093851683529290522060010154155b801561475e57506001600160a01b038083166000908152609f6020908152604080832093851683529290522054155b801561479057506001600160a01b038083166000908152609f6020908152604080832093851683529290522060010154155b156107db5760005b6001600160a01b03828116600090815260a160205260409020805491851691839081106147c7576147c7615731565b6000918252602090912001546001600160a01b0316146147e957600101614798565b6001600160a01b03808416600090815260a0602090815260408083209386168352928152828220805460ff1916905560a19052205461482960018261571a565b82146148ce576001600160a01b038316600090815260a16020526040902061485260018361571a565b8154811061486257614862615731565b60009182526020808320909101546001600160a01b03868116845260a1909252604090922080549190921691908490811061489f5761489f615731565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b6001600160a01b038316600090815260a1602052604090208054806148f5576148f561580f565b600082815260209020810160001990810180546001600160a01b031916905501905550505050565b60008183101561492e5760006108cf565b6108cf828461571a565b60008160000361494a57506000610a3a565b61497c6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b846001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156149ba573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149de91906156b6565b6040808301919091526001600160a01b038616600090815260a460205290812054825280855a60808501525b600081118015614a5057506001600160a01b0388166000908152609a60205260408120614a4190600101546001600160a01b031690565b9350836001600160a01b031614155b8015614a6a5750855a8560800151614a68919061571a565b105b15614ba5576001600160a01b038089166000908152609e6020908152604080832093871683529290522084518154919350614aa59190612002565b6060850181905260009081908310614af25760408601516060870151614aca916129e7565b8460010154614ad99190615702565b9150856060015183614aeb919061571a565b9250614b34565b6040860151614b029084906129e7565b8460010154614b119190615702565b8651909250614b219084906129e7565b8454614b2d919061571a565b9050600092505b60018401829055808455614b488a86613c0c565b896001600160a01b0316856001600160a01b03167f76908587112671ab2dcd9323f0d9b27d193156f95fe5e1251411a151c20e82dd8484604051614b96929190918252602082015260400190565b60405180910390a35050614a0a565b614baf818861571a565b98975050505050505050565b600081600003614bcd57506000610a3a565b614bff6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b846001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614c3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614c6191906156b6565b6040808301919091526001600160a01b038616600090815260a560205290812054825280855a60808501525b600081118015614cd357506001600160a01b0388166000908152609c60205260408120614cc490600101546001600160a01b031690565b9350836001600160a01b031614155b8015614ced5750855a8560800151614ceb919061571a565b105b15614ba5576001600160a01b038089166000908152609f6020908152604080832093871683529290522084518154919350614d289190612002565b6060850181905260009081908310614d755760408601516060870151614d4d916129e7565b8460010154614d5c9190615702565b9150856060015183614d6e919061571a565b9250614db7565b6040860151614d859084906129e7565b8460010154614d949190615702565b8651909250614da49084906129e7565b8454614db0919061571a565b9050600092505b60018401829055808455614dcb8a8661422f565b896001600160a01b0316856001600160a01b03167f0aec3812ec00f2d2f0eacc89fd13923091a68f30c3b3d0336e364544322b97588484604051614e19929190918252602082015260400190565b60405180910390a35050614c8d565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806144845760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b604482015260640161092e565b6001600160a01b0381166000908152602083905260408120600201549003614eda5760405163e76ea87f60e01b815260040160405180910390fd5b6001600160a01b038082166000908152602084815260409182902082516060810184528154851680825260018301549095169281019290925260020154918101919091529015614f635760208181015182516001600160a01b03908116600090815292869052604090922060010180546001600160a01b03191692909116919091179055614f8a565b60208101516001840180546001600160a01b0319166001600160a01b039092169190911790555b60208101516001600160a01b031615614fd65780516020808301516001600160a01b03908116600090815291869052604090912080546001600160a01b03191691909216179055614ffa565b80516002840180546001600160a01b0319166001600160a01b039092169190911790555b506001600160a01b031660009081526020919091526040812080546001600160a01b03199081168255600182018054909116905560020155565b816000036150555760405163ba3b5b5960e01b815260040160405180910390fd5b6001600160a01b03831661507c5760405163867915ab60e01b815260040160405180910390fd5b6001600160a01b038316600090815260208590526040902060020154156150b65760405163f5ab373160e01b815260040160405180910390fd5b60018401546000906001600160a01b03165b82821080156150e7575060028601546001600160a01b03828116911614155b801561510e57506001600160a01b0381166000908152602087905260409020600201548411155b1561513b576001600160a01b039081166000908152602087905260409020600190810154920191166150c8565b6001600160a01b0381161580159061516d57506001600160a01b03811660009081526020879052604090206002015484115b156152ae5760018601546001600160a01b0390811690821603615215576040805160608101825260008082526001600160a01b0384811660208085018281528587018b81528c8516808752928e9052878620965187549086166001600160a01b0319918216178855915160018089018054929097169184169190911790955551600290960195909555918b018054851683179055825292902080549091169091179055611af3565b604080516060810182526001600160a01b03808416600081815260208b81528582208054851686528186018481528688018c81528d8716808652938f9052888520975188549088166001600160a01b0319918216178955915160018981018054928a1692851692909217909155905160029098019790975581549095168352958220909401805484168517905552825416179055611af3565b60018601546001600160a01b03166153455760408051606081018252600080825260208083018281528385018981526001600160a01b038b8116808652938d905295909320935184549086166001600160a01b031991821617855590516001808601805492909716918316919091179095559151600293840155928901805482168417905590880180549091169091179055611af3565b505060408051606081018252600286810180546001600160a01b03908116845260006020808601828152868801998a529983168083529a9052858120945185549083166001600160a01b0319918216178655985160018087018054928516928c169290921790915597519490930193909355805490921681529190912090920180548416851790555080549091169091179055565b6001600160a01b0381168114611c8f57600080fd5b60006020828403121561540157600080fd5b8135610a3a816153da565b8035600481106135b857600080fd5b6000806040838503121561542e57600080fd5b8235615439816153da565b91506154476020840161540c565b90509250929050565b6000806040838503121561546357600080fd5b823561546e816153da565b9150602083013561547e816153da565b809150509250929050565b60008060006060848603121561549e57600080fd5b83356154a9816153da565b92506154b76020850161540c565b915060408401356154c7816153da565b809150509250925092565b600080600080608085870312156154e857600080fd5b84356154f3816153da565b93506020850135615503816153da565b92506040850135615513816153da565b9396929550929360600135925050565b600080600080600060a0868803121561553b57600080fd5b8535615546816153da565b94506020860135615556816153da565b93506040860135615566816153da565b94979396509394606081013594506080013592915050565b6020808252825182820181905260009190848201906040850190845b818110156155bf5783516001600160a01b03168352928401929184019160010161559a565b50909695505050505050565b6000806000606084860312156155e057600080fd5b83356155eb816153da565b95602085013595506040909401359392505050565b60006020828403121561561257600080fd5b5035919050565b600080600080600060a0868803121561563157600080fd5b853561563c816153da565b9450602086013593506040860135615653816153da565b92506060860135615663816153da565b949793965091946080013592915050565b6000806040838503121561568757600080fd5b8235615692816153da565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156156c857600080fd5b5051919050565b6000602082840312156156e157600080fd5b8151610a3a816153da565b634e487b7160e01b600052601160045260246000fd5b60008219821115615715576157156156ec565b500190565b60008282101561572c5761572c6156ec565b500390565b634e487b7160e01b600052603260045260246000fd5b6000816000190483118215151615615761576157616156ec565b500290565b60008261578357634e487b7160e01b600052601260045260246000fd5b500490565b6000825160005b818110156157a9576020818601810151858301520161578f565b818111156157b8576000828501525b509190910192915050565b805180151581146135b857600080fd5b6000806000606084860312156157e857600080fd5b6157f1846157c3565b925060208401519150615806604085016157c3565b90509250925092565b634e487b7160e01b600052603160045260246000fdfeaa997145358327b99ccedf396e9b7719eb7999623af1a7b38605739996c2ccfaa26469706673582212207f61fd037e634fe3fde4fedbf2ed8a01c179d30e77beb5b7e431a6366c07f5e764736f6c634300080d0033

Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading

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