Goerli Testnet

Contract

0x385D14f97508c9A5Ceb14fB7bD3e8E63B7724527

Overview

ETH Balance

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Value
0x6080604072706422022-07-22 11:13:38615 days ago1658488418IN
 Create: PositionsManager
0 ETH0.014827253

Latest 8 internal transactions

Advanced mode:
Parent Txn Hash Block From To Value
72771472022-07-23 14:23:22614 days ago1658586202
0x385D14f9...3B7724527
0 ETH
72771382022-07-23 14:21:07614 days ago1658586067
0x385D14f9...3B7724527
0 ETH
72771202022-07-23 14:16:36614 days ago1658585796
0x385D14f9...3B7724527
0 ETH
72770932022-07-23 14:09:51614 days ago1658585391
0x385D14f9...3B7724527
0 ETH
72769542022-07-23 13:34:59614 days ago1658583299
0x385D14f9...3B7724527
0 ETH
72766342022-07-23 12:14:38614 days ago1658578478
0x385D14f9...3B7724527
0 ETH
72766162022-07-23 12:10:08614 days ago1658578208
0x385D14f9...3B7724527
0 ETH
72765712022-07-23 11:58:53614 days ago1658577533
0x385D14f9...3B7724527
0 ETH
Loading...
Loading

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

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 23 : PositionsManager.sol
// SPDX-License-Identifier: GNU AGPLv3
pragma solidity 0.8.13;

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 remainingGasForMatching;
        uint256 remainingToWithdraw;
        uint256 poolSupplyIndex;
        uint256 p2pSupplyIndex;
        uint256 withdrawable;
        uint256 toWithdraw;
        ERC20 underlyingToken;
    }

    // Struct to avoid stack too deep.
    struct RepayVars {
        uint256 remainingGasForMatching;
        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.underlyingToken = _getUnderlying(_poolTokenAddress);
        vars.remainingToWithdraw = _amount;
        vars.remainingGasForMatching = _maxGasForMatching;
        vars.withdrawable = ICToken(_poolTokenAddress).balanceOfUnderlying(address(this));
        vars.poolSupplyIndex = ICToken(_poolTokenAddress).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;
            }

            if (vars.remainingToWithdraw == 0) {
                _updateSupplierInDS(_poolTokenAddress, _supplier);
                _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.remainingGasForMatching
            );
            if (vars.remainingGasForMatching <= gasConsumedInMatching)
                vars.remainingGasForMatching = 0;
            else vars.remainingGasForMatching -= 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,
                vars.remainingGasForMatching
            );

            // 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(
                    ICToken(_poolTokenAddress).borrowIndex()
                );
                emit P2PBorrowDeltaUpdated(_poolTokenAddress, delta.p2pBorrowDelta);
            }

            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.remainingGasForMatching = _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;
            }
        }

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

        // 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).
            // No need to subtract p2pBorrowDelta as it is zero.
            vars.feeToRepay = CompoundMath.safeSub(
                delta.p2pBorrowAmount.mul(vars.p2pBorrowIndex),
                (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
                );
            }
        }

        /// Transfer repay ///

        // 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.remainingGasForMatching
            );
            if (vars.remainingGasForMatching <= gasConsumedInMatching)
                vars.remainingGasForMatching = 0;
            else vars.remainingGasForMatching -= 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.remainingGasForMatching
            );

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

            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.13;

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.13;

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)
    {
        Types.SupplyBalance memory userSupplyBalance = supplyBalanceInOf[_poolTokenAddress][_user];
        return
            userSupplyBalance.inP2P.mul(p2pSupplyIndex[_poolTokenAddress]) +
            userSupplyBalance.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)
    {
        Types.BorrowBalance memory userBorrowBalance = borrowBalanceInOf[_poolTokenAddress][_user];
        return
            userBorrowBalance.inP2P.mul(p2pBorrowIndex[_poolTokenAddress]) +
            userBorrowBalance.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 {
    /*//////////////////////////////////////////////////////////////
                             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 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

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

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

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

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

        require(success, "TRANSFER_FAILED");
    }

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

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

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

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

        require(success, "APPROVE_FAILED");
    }
}

File 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.13;

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.
    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.
    bool public isClaimRewardsPaused; // Whether it's possible to claim rewards or not.

    /// 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 transferTokensToDao(address _token, 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 pool supply unit).
        uint256 p2pBorrowDelta; // Difference between the stored peer-to-peer borrow amount and the real peer-to-peer borrow amount (in pool borrow unit).
        uint256 p2pSupplyAmount; // Sum of all stored peer-to-peer supply (in peer-to-peer supply unit).
        uint256 p2pBorrowAmount; // Sum of all stored peer-to-peer borrow (in peer-to-peer borrow 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/goerli/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/solmate/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",
  "debug": {
    "revertStrings": "default"
  },
  "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":"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":[],"name":"isClaimRewardsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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"}]

60806040523480156200001157600080fd5b50600054610100900460ff166200002f5760005460ff161562000039565b62000039620000de565b620000a15760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b600054610100900460ff16158015620000c4576000805461ffff19166101011790555b8015620000d7576000805461ff00191690555b506200010b565b6000620000f630620000fc60201b62001c961760201c565b15905090565b6001600160a01b03163b151590565b6157f5806200011b6000396000f3fe608060405234801561001057600080fd5b50600436106102535760003560e01c8063a086fc2211610146578063d59c9eb6116100c3578063e501ed0411610087578063e501ed04146106bc578063e61c6d6f146106ee578063e8462e8f146106f7578063f2f4ca1614610700578063f2fde38b1461076c578063f4ea93d81461077f57600080fd5b8063d59c9eb6146105bf578063db0577fd14610614578063defe205314610683578063df6d921214610696578063e34b5145146106a957600080fd5b8063b24be6871161010a578063b24be68714610559578063b59ec4781461056c578063b6f2bf1c1461058c578063c2af97871461059f578063cb830d03146105b257600080fd5b8063a086fc22146104c3578063a44026a314610518578063ac0b4b121461052b578063af8b1c6f1461053e578063b0772d0b1461055157600080fd5b80637907016a116101d45780638da5cb5b116101985780638da5cb5b146103f5578063947574ac1461040657806396bd512c1461044d5780639df5a1f2146104965780639fab1036146104b057600080fd5b80637907016a1461037c5780637f3ad0561461038f578063854f7ebb146103a257806385d7334d146103c25780638ccb720b146103d557600080fd5b80635fe3b5671161021b5780635fe3b567146103115780636a14602414610324578063715018a614610333578063720ceb021461033b578063789caa3e1461036957600080fd5b806320c342d9146102585780632ebf4be0146102905780633528e4ce146102be57806352f0f814146102d35780635acff027146102fe575b600080fd5b61027b610266366004615369565b60a36020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6102b061029e366004615369565b60a56020526000908152604090205481565b604051908152602001610287565b6102d16102cc366004615369565b61079b565b005b60aa546102e6906001600160a01b031681565b6040516001600160a01b039091168152602001610287565b6102e661030c366004615395565b6107e3565b60ae546102e6906001600160a01b031681565b6102b0670de0b6b3a764000081565b6102d16108dc565b61027b6103493660046153ca565b60a060209081526000928352604080842090915290825290205460ff1681565b6102e6610377366004615403565b610947565b6102d161038a36600461544c565b610a45565b60ad546102e6906001600160a01b031681565b6102b06103b0366004615369565b60a46020526000908152604090205481565b6102d16103d036600461549d565b610e88565b6103e86103e3366004615369565b6113ae565b60405161028791906154f8565b6033546001600160a01b03166102e6565b6104386104143660046153ca565b609f6020908152600092835260408084209091529082529020805460019091015482565b60408051928352602083019190915201610287565b61047b61045b366004615369565b60a76020526000908152604090205461ffff808216916201000090041682565b6040805161ffff938416815292909116602083015201610287565b61049e600881565b60405160ff9091168152602001610287565b6102d16104be366004615545565b611424565b6104f86104d1366004615369565b60a96020526000908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610287565b60b0546102e6906001600160a01b031681565b6102e661053936600461557a565b6119ac565b60af546102e6906001600160a01b031681565b6103e86119d6565b60ab546102e6906001600160a01b031681565b6102b061057a366004615369565b60b26020526000908152604090205481565b6102d161059a366004615593565b611a38565b6102d16105ad36600461549d565b611aff565b60b35461027b9060ff1681565b6105f56105cd366004615369565b60a86020526000908152604090205460ff808216916101008104821691620100009091041683565b6040805193151584529115156020840152151590820152606001610287565b610657610622366004615369565b60a66020526000908152604090205463ffffffff8116906001600160701b036401000000008204811691600160901b90041683565b6040805163ffffffff90941684526001600160701b039283166020850152911690820152606001610287565b60ac546102e6906001600160a01b031681565b60b1546102e6906001600160a01b031681565b6102e66106b73660046155ee565b611b93565b6104386106ca3660046153ca565b609e6020908152600092835260408084209091529082529020805460019091015482565b6102b060975481565b6102b060985481565b6099546107389067ffffffffffffffff80821691680100000000000000008104821691600160801b8204811691600160c01b90041684565b6040805167ffffffffffffffff95861681529385166020850152918416918301919091529091166060820152608001610287565b6102d161077a366004615369565b611bcb565b61078861271081565b60405161ffff9091168152602001610287565b6001600160a01b038116600090815260a86020526040902054819060ff166107d6576040516396e1352960e01b815260040160405180910390fd5b6107df82611ca5565b5050565b6000808260038111156107f8576107f861561a565b03610823576001600160a01b038381166000908152609a6020526040902060010154165b90506108d6565b60018260038111156108375761083761561a565b0361085f576001600160a01b038381166000908152609b60205260409020600101541661081c565b60028260038111156108735761087361561a565b0361089b576001600160a01b038381166000908152609c60205260409020600101541661081c565b60038260038111156108af576108af61561a565b036108d6576001600160a01b038381166000908152609d6020526040902060010154165b90505b92915050565b6033546001600160a01b0316331461093b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6109456000611cf7565b565b60008083600381111561095c5761095c61561a565b0361098a576001600160a01b0384166000908152609a602052604090206109839083611d49565b9050610a3e565b600183600381111561099e5761099e61561a565b036109c5576001600160a01b0384166000908152609b602052604090206109839083611d49565b60028360038111156109d9576109d961561a565b03610a00576001600160a01b0384166000908152609c602052604090206109839083611d49565b6003836003811115610a1457610a1461561a565b03610a3e576001600160a01b0384166000908152609d60205260409020610a3b9083611d49565b90505b9392505050565b6001600160a01b03808516600090815260a0602090815260408083209386168352929052205460ff161580610aa057506001600160a01b03808416600090815260a0602090815260408083209386168352929052205460ff16155b15610abe576040516301187a4360e61b815260040160405180910390fd5b610ac784611ca5565b610ad083611ca5565b610ade826000806000611d6c565b610afb5760405163b3165ffd60e01b815260040160405180910390fd5b610b2d6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b610b378584611f2d565b60208083019190915260ae546040805163743aaa2360e11b81529051610bb8936001600160a01b039093169263e875544692600480820193918290030181865afa158015610b89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bad9190615630565b602083015190611fdd565b821115610bd857604051633b1b989f60e01b815260040160405180910390fd5b610be6853385856000611ffc565b60ae54604080516307dc0d1d60e41b815290516000926001600160a01b031691637dc0d1d09160048083019260209291908290030181865afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c549190615649565b60405163fc57d4df60e01b81526001600160a01b0387811660048301529192509082169063fc57d4df90602401602060405180830381865afa158015610c9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cc29190615630565b825260405163fc57d4df60e01b81526001600160a01b03878116600483015282169063fc57d4df90602401602060405180830381865afa158015610d0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2e9190615630565b606083015281511580610d4357506060820151155b15610d6157604051634b6b62e560e01b815260040160405180910390fd5b610e10610e018360000151610dfb8560600151610df560ae60009054906101000a90046001600160a01b03166001600160a01b0316634ada90af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee9190615630565b8990611fdd565b90611fdd565b90612999565b610e0b87876129cb565b612a69565b60808301819052610e2690869086336000612a7f565b60808201516040805133815260208101869052908101919091526001600160a01b0380871691888216918716907fc2c75a73164c2efcbb9f74bfa511cd0866489d90687831a7217b3dbeeb6970889060600160405180910390a4505050505050565b6001600160a01b038316610eaf5760405163867915ab60e01b815260040160405180910390fd5b81600003610ed0576040516310eb483f60e21b815260040160405180910390fd5b610ed985611ca5565b610ee3858461341a565b6000610eee866134a6565b9050610f056001600160a01b038216863086613537565b6001600160a01b038616600090815260a960209081526040808320815160608101835284815292830184905290820192909252876001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9a9190615630565b6020820152848152600182015415611096576000610fc982602001518460010154611fdd90919063ffffffff16565b825190915081111561101c578151604083018051610fe890839061567c565b90525060208201518251610ffb91612999565b83600101600082825461100e9190615694565b90915550506000825261104d565b808260400181815161102e919061567c565b90525060006001840155815181908390611049908390615694565b9052505b886001600160a01b03167f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df6846001015460405161108c91815260200190565b60405180910390a2505b8051158015906110bf57506001600160a01b038816600090815260a3602052604090205460ff16155b80156110fe57506001600160a01b0388166000908152609d602052604081206110f290600101546001600160a01b031690565b6001600160a01b031614155b15611185576000611114898360000151876135ba565b509050801561118357808260400181815161112f919061567c565b905250815181908390611143908390615694565b9052506001600160a01b038916600090815260a5602052604090205461116a908290612999565b83600301600082825461117d919061567c565b90915550505b505b604081015115611262576001600160a01b038816600090815260a4602052604080822054908301516111b691612999565b9050808360020160008282546111cc919061567c565b90915550506001600160a01b03808a166000908152609e60209081526040808320938b168352929052908120805483929061120890849061567c565b9250508190555061121e89858460400151613853565b886001600160a01b03166000805160206157a083398151915284600201548560030154604051611258929190918252602082015260400190565b60405180910390a2505b805115611324576112d6886001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ce9190615630565b825190612999565b6001600160a01b03808a166000908152609e60209081526040808320938b168352929052908120600101805490919061131090849061567c565b909155505080516113249089908590613a3a565b61132e8887613b86565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518c8152938401919091529082015291929091908a16907f11adb3570ba55fd255b1f04252ca0071ae6639c86d4fd69e7c1bf1688afb493f906060015b60405180910390a45050505050505050565b6001600160a01b038116600090815260a1602090815260409182902080548351818402810184019094528084526060939283018282801561141857602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113fa575b50505050509050919050565b81600003611445576040516310eb483f60e21b815260040160405180910390fd5b61144e83611ca5565b611458833361341a565b33600081815260b2602052604081204390556114779190859085611d6c565b156114955760405163df9db46360e01b815260040160405180910390fd5b60006114a0846134a6565b6001600160a01b038516600081815260a960209081526040808320815163182df0f560e01b815291519596508895939490938593919263182df0f59260048083019391928290030181865afa1580156114fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115219190615630565b604051633af9e66960e01b81523060048201529091506000906001600160a01b038a1690633af9e669906024016020604051808303816000875af115801561156d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115919190615630565b83549091501561166c5782546000906115aa9084611fdd565b9050858111806115b957508181115b1561160d5760006115ca8784612a69565b90506115d6818761567c565b95506115e28185612999565b8560000160008282546115f59190615694565b9091555061160590508188615694565b96505061162a565b611617818661567c565b6000855594506116278187615694565b95505b83546040519081526001600160a01b038b16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a2505b60008511801561169557506001600160a01b038916600090815260a3602052604090205460ff16155b80156116d457506001600160a01b0389166000908152609b602052604081206116c890600101546001600160a01b031690565b6001600160a01b031614155b1561176c5760006116f38a6116ed88610e0b8987615694565b8a613d99565b509050801561176a57611706818661567c565b94506117128187615694565b6001600160a01b038b16600090815260a46020526040902054909650611739908290612999565b6001600160a01b038b16600090815260a960205260408120600201805490919061176490849061567c565b90915550505b505b8315611863576001600160a01b038916600090815260a56020526040812054611796908690612999565b6001600160a01b038b16600090815260a960205260408120600301805492935083929091906117c690849061567c565b90915550506001600160a01b038a166000908152609f60209081526040808320338452909152812080548392906117fe90849061567c565b9091555050600284015460038501546040516001600160a01b038d16926000805160206157a08339815191529261183d92918252602082015260400190565b60405180910390a260006118518685612999565b1115611861576118618a86614014565b505b841561191b576118d5896001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ce9190615630565b8690612999565b6001600160a01b038a166000908152609f602090815260408083203384529091528120600101805490919061190b90849061567c565b9091555061191b90508986614120565b61192589336141a9565b6119396001600160a01b038716338a614386565b6001600160a01b0389166000818152609f6020908152604080832033808552908352928190206001810154905482518e81529384019190915282820152517fc1cba78646fef030830d099fc25cb498953709c9d47d883848f81fd207174c9f9181900360600190a3505050505050505050565b60a281815481106119bc57600080fd5b6000918252602090912001546001600160a01b0316905081565b606060a2805480602002602001604051908101604052809291908181526020018280548015611a2e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611a10575b5050505050905090565b83600003611a59576040516310eb483f60e21b815260040160405180910390fd5b6001600160a01b03808616600090815260a0602090815260408083209387168352929052205460ff16611a9f576040516301187a4360e61b815260040160405180910390fd5b611aa885611ca5565b6000611abd611ab787866129cb565b86612a69565b9050611acc8487836000611d6c565b15611aea57604051630cba3c5f60e21b815260040160405180910390fd5b611af78682868686612a7f565b505050505050565b81600003611b20576040516310eb483f60e21b815260040160405180910390fd5b6001600160a01b03808616600090815260a0602090815260408083209387168352929052205460ff16611b66576040516301187a4360e61b815260040160405180910390fd5b611b6f85611ca5565b6000611b84611b7e8786611f2d565b84612a69565b9050611af78686868486611ffc565b60a16020528160005260406000208181548110611baf57600080fd5b6000918252602090912001546001600160a01b03169150829050565b6033546001600160a01b03163314611c255760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610932565b6001600160a01b038116611c8a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610932565b611c9381611cf7565b50565b6001600160a01b03163b151590565b604080516001600160a01b038381166024808401919091528351808403909101815260449092019092526020810180516001600160e01b0316631a94726760e11b17905260ad546107df921690614404565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0390811660009081526020929092526040909120600101541690565b60008060ae60009054906101000a90046001600160a01b03166001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de69190615649565b6001600160a01b038716600090815260a16020908152604080832054815160a08101835284815292830184905290820183905260608201839052608082018390529293509080805b84811015611f1f576001600160a01b038b16600090815260a160205260408120805483908110611e6057611e606156ab565b6000918252602090912001546001600160a01b03169050611e828c828961449e565b9450846020015184611e94919061567c565b9350846040015183611ea6919061567c565b9250806001600160a01b03168b6001600160a01b031603611f16578815611ee4576060850151611ed7908a90611fdd565b611ee1908461567c565b92505b8915611f1657611f098560800151610df587606001518d611fdd90919063ffffffff16565b611f139085615694565b93505b50600101611e2e565b501198975050505050505050565b6001600160a01b038083166000818152609f602090815260408083209486168352938152838220845180860186528154815260019091015481830152845163aa5af0fd60e01b8152945192949093611fa793909263aa5af0fd92600480820193918290030181865afa158015610b89573d6000803e3d6000fd5b6001600160a01b038516600090815260a560205260409020548251611fcb91611fdd565b611fd5919061567c565b949350505050565b6000670de0b6b3a7640000611ff283856156c1565b6108d391906156e0565b6001600160a01b038316600090815260b260205260409020544390036120355760405163dff88f5160e01b815260040160405180910390fd5b6000612040866134a6565b90506120576001600160a01b038216863086613537565b6120a66040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60208082018590528382526040805163aa5af0fd60e01b815290516001600160a01b038a169263aa5af0fd92600480820193918290030181865afa1580156120f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121169190615630565b60608201526001600160a01b038088166000908152609f602090815260408083209389168352929052206001015460c08201819052156122c457606081015160c082015161216391611fdd565b6040820181905260208201511015612277576020810151610100820181905260c0820151606083015161219992610e0b91612999565b6001600160a01b038089166000908152609f60209081526040808320938a16835292905290812060010180549091906121d3908490615694565b909155506121e3905087866141a9565b6121f38783836101000151613853565b6121fd878661461f565b6001600160a01b038781166000818152609f602090815260408083208a8616808552908352928190206001810154905482518b815293840191909152828201525192939192918a16917f7b417e520d2b905fc5a1689d29d329358dd55efc60ed115aa165b0a2b64232c69181900360600190a45050612992565b60408101516101008201819052602082018051612295908390615694565b9052506001600160a01b038088166000908152609f602090815260408083209389168352929052908120600101555b6001600160a01b03808816600081815260a96020908152604080832060a4835281842054608088015284845260a583528184205460a08801908152948452609f8352818420958b1684529482529091205491519084015161232a9291610e0b9190612999565b6001600160a01b03808a166000908152609f60209081526040808320938b1683529290529081208054909190612361908490615694565b90915550612371905088876141a9565b60008260200151118015612389575060008160010154115b1561251f5760006123ab83606001518360010154611fdd90919063ffffffff16565b9050826020015181111561243557606083015160208401516123cc91612999565b8260010160008282546123df9190615694565b909155505060a083015160208401516123f791612999565b82600301600082825461240a9190615694565b909155505060208301516101008401805161242690839061567c565b90525060006020840152612494565b6000600183015560a083015161244c908290612999565b82600301600082825461245f9190615694565b92505081905550808361010001818151612479919061567c565b905250602083018051829190612490908390615694565b9052505b886001600160a01b03167f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df683600101546040516124d391815260200190565b60405180910390a2886001600160a01b03166000805160206157a083398151915283600201548460030154604051612515929190918252602082015260400190565b60405180910390a2505b602082015115612679576125d66125478360a001518360030154611fdd90919063ffffffff16565b6125b48a6001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612588573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125ac9190615630565b845490611fdd565b608085015160028501546125c791611fdd565b6125d19190615694565b614897565b60e08301819052156126795760006125f68360e001518460200151612a69565b9050808360200181815161260a9190615694565b90525060a083015161261d908290612999565b8260030160008282546126309190615694565b9091555050600282015460038301546040516001600160a01b038c16926000805160206157a08339815191529261266f92918252602082015260400190565b60405180910390a2505b600082602001511180156126a657506001600160a01b038816600090815260a3602052604090205460ff16155b80156126e557506001600160a01b0388166000908152609d602052604081206126d990600101546001600160a01b031690565b6001600160a01b031614155b15612765576000806127008a856020015186600001516135ba565b9150915080846000015111612718576000845261272e565b808460000181815161272a9190615694565b9052505b81156127625781846020018181516127469190615694565b9052506101008401805183919061275e90839061567c565b9052505b50505b6127758884846101000151613853565b60208201511561291657600061279489846020015185600001516148b2565b9050826020015181101561287057612817896001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128079190615630565b828560200151610dfb9190615694565b82600001600082825461282a919061567c565b909155505081546040519081526001600160a01b038a16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a25b6080830151612880908290612999565b8260020160008282546128939190615694565b909155505060a083015160208401516128ab91612999565b8260030160008282546128be9190615694565b9091555050600282015460038301546040516001600160a01b038c16926000805160206157a0833981519152926128fd92918252602082015260400190565b60405180910390a261291489858560200151613a3a565b505b612920888761461f565b6001600160a01b038881166000818152609f602090815260408083208b8616808552908352928190206001810154905482518c8152938401919091529082015291929091908a16907f7b417e520d2b905fc5a1689d29d329358dd55efc60ed115aa165b0a2b64232c69060600161139c565b5050505050565b6000670de0b6b3a7640000826129af85836156c1565b6129c190670de0b6b3a76400006156c1565b611ff291906156e0565b6001600160a01b038083166000818152609e602090815260408083209486168352938152838220845180860186528154815260019091015481830152845163182df0f560e01b8152945192949093612a4593909263182df0f592600480820193918290030181865afa158015610b89573d6000803e3d6000fd5b6001600160a01b038516600090815260a460205260409020548251611fcb91611fdd565b6000818310612a7857816108d3565b5090919050565b83600003612aa0576040516310eb483f60e21b815260040160405180910390fd5b612ae96040518060e0016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b031681525090565b612af2866134a6565b6001600160a01b0390811660c083015260208201869052828252604051633af9e66960e01b815230600482015290871690633af9e669906024016020604051808303816000875af1158015612b4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6f9190615630565b816080018181525050856001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bda9190615630565b60408201819052612bec908690612999565b600003612c0c576040516393c76c6f60e01b815260040160405180910390fd5b6001600160a01b038087166000908152609e60209081526040808320938816835292905220600101548015612e23576000612c54836040015183611fdd90919063ffffffff16565b90508260200151811180612c6b5750826080015181115b15612cf557612c8283602001518460800151612a69565b60a08401819052602084018051612c9a908390615694565b905250604083015160a0840151612cb091612999565b6001600160a01b03808a166000908152609e60209081526040808320938b1683529290529081206001018054909190612cea908490615694565b90915550612d3f9050565b60a08301819052602083018051829190612d10908390615694565b9052506001600160a01b038089166000908152609e60209081526040808320938a168352929052908120600101555b8260200151600003612e2157612d558887613b86565b612d5f888761461f565b6000612d7c84604001518560a0015161299990919063ffffffff16565b1115612d9057612d90888460a00151614014565b60c0830151612da9906001600160a01b03168689614386565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518e815293840191909152828201525192938916927f378f9d375cd79e36c19c26a9e57791fe7cd5953b61986c01ebf980c0efb928019181900360600190a4505050612992565b505b6001600160a01b03808816600081815260a96020908152604080832060a483528184205460608901908152948452609e8352818420958b16845294825290912054915190850151612e799291610e0b9190612999565b6001600160a01b03808a166000908152609e60209081526040808320938b1683529290529081208054909190612eb0908490615694565b90915550612ec090508887613b86565b60008360200151118015612ed45750805415155b156130a15760408301518154600091612eed9190611fdd565b90508360200151811180612f1357508360a001518460800151612f109190615694565b81115b15612fc4576000612f3685602001518660a001518760800151610e0b9190615694565b9050612f4f85604001518261299990919063ffffffff16565b836000016000828254612f629190615694565b90915550506060850151612f77908290612999565b836002016000828254612f8a9190615694565b92505081905550808560a001818151612fa3919061567c565b905250602085018051829190612fba908390615694565b90525061301d9050565b808460a001818151612fd6919061567c565b905250602084018051829190612fed908390615694565b905250600082556060840151613004908290612999565b8260020160008282546130179190615694565b90915550505b81546040519081526001600160a01b038a16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a2886001600160a01b03166000805160206157a083398151915283600201548460030154604051613097929190918252602082015260400190565b60405180910390a2505b600083602001511180156130ce57506001600160a01b038816600090815260a3602052604090205460ff16155b801561310d57506001600160a01b0388166000908152609b6020526040812061310190600101546001600160a01b031690565b6001600160a01b031614155b156131a05760008061313c8a61313587602001518860a001518960800151610e0b9190615694565b8751613d99565b9150915080856000015111613154576000855261316a565b80856000018181516131669190615694565b9052505b811561319d5781856020018181516131829190615694565b90525060a08501805183919061319990839061567c565b9052505b50505b60006131bd84604001518560a0015161299990919063ffffffff16565b11156131d1576131d1888460a00151614014565b6020830151156133885760006131f08985602001518660000151614b35565b905083602001518110156132cf57613273896001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561323f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132639190615630565b828660200151610dfb9190615694565b826001016000828254613286919061567c565b909155505060018201546040519081526001600160a01b038a16907f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df69060200160405180910390a25b606084015160208501516132e291612999565b8260020160008282546132f59190615694565b90915550506001600160a01b038916600090815260a5602052604090205461331e908290612999565b8260030160008282546133319190615694565b9091555050600282015460038301546040516001600160a01b038c16926000805160206157a08339815191529261337092918252602082015260400190565b60405180910390a2613386898560200151614120565b505b613392888761461f565b60c08301516133ab906001600160a01b03168689614386565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518e8152938401919091529082015291928816917f378f9d375cd79e36c19c26a9e57791fe7cd5953b61986c01ebf980c0efb928019060600161139c565b6001600160a01b03808316600090815260a0602090815260408083209385168352929052205460ff166107df576001600160a01b03808316600081815260a0602090815260408083209486168352938152838220805460ff1916600190811790915560a18252938220805494850181558252902090910180546001600160a01b03191690911790555050565b60b0546000906001600160a01b03908116908316036134d057505060b1546001600160a01b031690565b816001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561350e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d69190615649565b919050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806129925760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610932565b600080826000036135d05750600090508061384b565b6136026040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b856001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613640573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136649190615630565b6040808301919091526001600160a01b038716600090815260a5602052908120548252805a60808401525b86851080156136d457506001600160a01b0388166000908152609d602052604081206136c590600101546001600160a01b031690565b9250826001600160a01b031614155b80156136ee5750855a84608001516136ec9190615694565b105b1561383557506001600160a01b038088166000908152609f60209081526040808320938516835292905281902090830151600182015461372d91611fdd565b606084015260008080613740888b615694565b905080866060015111613782578551606087015161375d91612999565b8454613769919061567c565b915085606001518861377b919061567c565b97506137c3565b6040860151613792908290612999565b84600101546137a19190615694565b86519093506137b1908290612999565b84546137bd919061567c565b91508997505b600184018390558184556137d78b866141a9565b8a6001600160a01b0316856001600160a01b03167f0aec3812ec00f2d2f0eacc89fd13923091a68f30c3b3d0336e364544322b97588585604051613825929190918252602082015260400190565b60405180910390a350505061368f565b5a83608001516138459190615694565b93505050505b935093915050565b6040516305eff7ef60e21b81523060048201526138c39082906001600160a01b038616906317bfdfbc906024016020604051808303816000875af115801561389f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0b9190615630565b90508015613a355760b0546001600160a01b03908116908416036139985760b154604051632e1a7d4d60e01b8152600481018390526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b15801561392757600080fd5b505af115801561393b573d6000803e3d6000fd5b50505050826001600160a01b0316634e4d9fea826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561397a57600080fd5b505af115801561398e573d6000803e3d6000fd5b5050505050505050565b6139ac6001600160a01b0383168483614da2565b60405163073a938160e11b8152600481018290526001600160a01b03841690630e752702906024016020604051808303816000875af11580156139f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a179190615630565b15613a3557604051637112354b60e01b815260040160405180910390fd5b505050565b60b0546001600160a01b0390811690841603613ae95760b154604051632e1a7d4d60e01b8152600481018390526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015613a9657600080fd5b505af1158015613aaa573d6000803e3d6000fd5b50505050826001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561397a57600080fd5b613afd6001600160a01b0383168483614da2565b60405163140e25ad60e31b8152600481018290526001600160a01b0384169063a0712d68906024016020604051808303816000875af1158015613b44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b689190615630565b15613a355760405163757d648760e11b815260040160405180910390fd5b6001600160a01b038281166000818152609e6020908152604080832094861680845294825280832060018101549054858552609b84528285208786528452828520600290810154968652609a855283862097865296909352922090930154609854919392918411613c21576001600160a01b038087166000908152609e60209081526040808320938916835292905290812060010181905593505b838214613c7e578115613c50576001600160a01b0386166000908152609b60205260409020613c509086614e19565b8315613c7e576097546001600160a01b0387166000908152609b60205260409020613c7e9187908790614fae565b6098548311613cb4576001600160a01b038087166000908152609e60209081526040808320938916835292905290812081905592505b828114613d11578015613ce3576001600160a01b0386166000908152609a60205260409020613ce39086614e19565b8215613d11576097546001600160a01b0387166000908152609a60205260409020613d119187908690614fae565b60ac546001600160a01b031615611af75760ac5460405163636c55d360e01b81526001600160a01b0387811660048301528881166024830152604482018590529091169063636c55d3906064015b600060405180830381600087803b158015613d7957600080fd5b505af1158015613d8d573d6000803e3d6000fd5b50505050505050505050565b60008082600003613daf5750600090508061384b565b613de16040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b856001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015613e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e439190615630565b6040808301919091526001600160a01b038716600090815260a4602052908120548252805a60808401525b8685108015613eb357506001600160a01b0388166000908152609b60205260408120613ea490600101546001600160a01b031690565b9250826001600160a01b031614155b8015613ecd5750855a8460800151613ecb9190615694565b105b1561383557506001600160a01b038088166000908152609e602090815260408083209385168352929052819020908301516001820154613f0c91611fdd565b606084015260008080613f1f888b615694565b905080866060015111613f615785516060870151613f3c91612999565b8454613f48919061567c565b9150856060015188613f5a919061567c565b9750613fa2565b6040860151613f71908290612999565b8460010154613f809190615694565b8651909350613f90908290612999565b8454613f9c919061567c565b91508997505b60018401839055818455613fb68b86613b86565b8a6001600160a01b0316856001600160a01b03167f76908587112671ab2dcd9323f0d9b27d193156f95fe5e1251411a151c20e82dd8585604051614004929190918252602082015260400190565b60405180910390a3505050613e6e565b60405163852a12e360e01b8152600481018290526001600160a01b0383169063852a12e3906024016020604051808303816000875af115801561405b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061407f9190615630565b1561409d57604051637d47a0c360e01b815260040160405180910390fd5b60b0546001600160a01b03908116908316036107df5760b160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561410357600080fd5b505af1158015614117573d6000803e3d6000fd5b50505050505050565b60405163317afabb60e21b8152600481018290526001600160a01b0383169063c5ebeaec906024016020604051808303816000875af1158015614167573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061418b9190615630565b1561409d576040516302738d6b60e11b815260040160405180910390fd5b6001600160a01b038281166000818152609f6020908152604080832094861680845294825280832060018101549054858552609d84528285208786528452828520600290810154968652609c855283862097865296909352922090930154609854919392918411614244576001600160a01b038087166000908152609f60209081526040808320938916835292905290812060010181905593505b8382146142a1578115614273576001600160a01b0386166000908152609d602052604090206142739086614e19565b83156142a1576097546001600160a01b0387166000908152609d602052604090206142a19187908790614fae565b60985483116142d7576001600160a01b038087166000908152609f60209081526040808320938916835292905290812081905592505b828114614334578015614306576001600160a01b0386166000908152609c602052604090206143069086614e19565b8215614334576097546001600160a01b0387166000908152609c602052604090206143349187908690614fae565b60ac546001600160a01b031615611af75760ac5460405163a9de645d60e01b81526001600160a01b0387811660048301528881166024830152604482018590529091169063a9de645d90606401613d5f565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806143fe5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610932565b50505050565b6060600080846001600160a01b0316846040516144219190615702565b600060405180830381855af49150503d806000811461445c576040519150601f19603f3d011682016040523d82523d6000602084013e614461565b606091505b509150915081156144755791506108d69050565b8051156144855780518082602001fd5b60405163037b81af60e11b815260040160405180910390fd5b6144d06040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60405163fc57d4df60e01b81526001600160a01b03848116600483015283169063fc57d4df90602401602060405180830381865afa158015614516573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061453a9190615630565b6060820181905260000361456157604051634b6b62e560e01b815260040160405180910390fd5b60ae54604051638e8f294b60e01b81526001600160a01b03858116600483015290911690638e8f294b90602401606060405180830381865afa1580156145ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145cf919061574d565b5060808301525060608101516145e990610df585876129cb565b815260608101516145fe90610df58587611f2d565b60408201526080810151815161461391611fdd565b60208201529392505050565b6001600160a01b03808316600090815260a0602090815260408083209385168352929052205460ff16801561467757506001600160a01b038083166000908152609e6020908152604080832093851683529290522054155b80156146a957506001600160a01b038083166000908152609e6020908152604080832093851683529290522060010154155b80156146d857506001600160a01b038083166000908152609f6020908152604080832093851683529290522054155b801561470a57506001600160a01b038083166000908152609f6020908152604080832093851683529290522060010154155b156107df5760005b6001600160a01b03828116600090815260a16020526040902080549185169183908110614741576147416156ab565b6000918252602090912001546001600160a01b03161461476357600101614712565b6001600160a01b03808416600090815260a0602090815260408083209386168352928152828220805460ff1916905560a1905220546147a3600182615694565b8214614848576001600160a01b038316600090815260a1602052604090206147cc600183615694565b815481106147dc576147dc6156ab565b60009182526020808320909101546001600160a01b03868116845260a19092526040909220805491909216919084908110614819576148196156ab565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b6001600160a01b038316600090815260a16020526040902080548061486f5761486f615789565b600082815260209020810160001990810180546001600160a01b031916905501905550505050565b6000818310156148a85760006108d3565b6108d38284615694565b6000816000036148c457506000610a3e565b6148f66040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b846001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015614934573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149589190615630565b6040808301919091526001600160a01b038616600090815260a460205290812054825280855a60808501525b6000811180156149ca57506001600160a01b0388166000908152609a602052604081206149bb90600101546001600160a01b031690565b9350836001600160a01b031614155b80156149e45750855a85608001516149e29190615694565b105b15614b1f576001600160a01b038089166000908152609e6020908152604080832093871683529290522084518154919350614a1f9190611fdd565b6060850181905260009081908310614a6c5760408601516060870151614a4491612999565b8460010154614a53919061567c565b9150856060015183614a659190615694565b9250614aae565b6040860151614a7c908490612999565b8460010154614a8b919061567c565b8651909250614a9b908490612999565b8454614aa79190615694565b9050600092505b60018401829055808455614ac28a86613b86565b896001600160a01b0316856001600160a01b03167f76908587112671ab2dcd9323f0d9b27d193156f95fe5e1251411a151c20e82dd8484604051614b10929190918252602082015260400190565b60405180910390a35050614984565b614b298188615694565b98975050505050505050565b600081600003614b4757506000610a3e565b614b796040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b846001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614bb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bdb9190615630565b6040808301919091526001600160a01b038616600090815260a560205290812054825280855a60808501525b600081118015614c4d57506001600160a01b0388166000908152609c60205260408120614c3e90600101546001600160a01b031690565b9350836001600160a01b031614155b8015614c675750855a8560800151614c659190615694565b105b15614b1f576001600160a01b038089166000908152609f6020908152604080832093871683529290522084518154919350614ca29190611fdd565b6060850181905260009081908310614cef5760408601516060870151614cc791612999565b8460010154614cd6919061567c565b9150856060015183614ce89190615694565b9250614d31565b6040860151614cff908490612999565b8460010154614d0e919061567c565b8651909250614d1e908490612999565b8454614d2a9190615694565b9050600092505b60018401829055808455614d458a866141a9565b896001600160a01b0316856001600160a01b03167f0aec3812ec00f2d2f0eacc89fd13923091a68f30c3b3d0336e364544322b97588484604051614d93929190918252602082015260400190565b60405180910390a35050614c07565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806143fe5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610932565b6001600160a01b0381166000908152602083905260408120600201549003614e545760405163e76ea87f60e01b815260040160405180910390fd5b6001600160a01b038082166000908152602084815260409182902082516060810184528154851680825260018301549095169281019290925260020154918101919091529015614edd5760208181015182516001600160a01b03908116600090815292869052604090922060010180546001600160a01b03191692909116919091179055614f04565b60208101516001840180546001600160a01b0319166001600160a01b039092169190911790555b60208101516001600160a01b031615614f505780516020808301516001600160a01b03908116600090815291869052604090912080546001600160a01b03191691909216179055614f74565b80516002840180546001600160a01b0319166001600160a01b039092169190911790555b506001600160a01b031660009081526020919091526040812080546001600160a01b03199081168255600182018054909116905560020155565b81600003614fcf5760405163ba3b5b5960e01b815260040160405180910390fd5b6001600160a01b038316614ff65760405163867915ab60e01b815260040160405180910390fd5b6001600160a01b038316600090815260208590526040902060020154156150305760405163f5ab373160e01b815260040160405180910390fd5b60018401546000906001600160a01b03165b8282108015615061575060028601546001600160a01b03828116911614155b801561508857506001600160a01b0381166000908152602087905260409020600201548411155b156150b5576001600160a01b03908116600090815260208790526040902060019081015492019116615042565b6001600160a01b038116158015906150e757506001600160a01b03811660009081526020879052604090206002015484115b156152285760018601546001600160a01b039081169082160361518f576040805160608101825260008082526001600160a01b0384811660208085018281528587018b81528c8516808752928e9052878620965187549086166001600160a01b0319918216178855915160018089018054929097169184169190911790955551600290960195909555918b018054851683179055825292902080549091169091179055611af7565b604080516060810182526001600160a01b03808416600081815260208b81528582208054851686528186018481528688018c81528d8716808652938f9052888520975188549088166001600160a01b0319918216178955915160018981018054928a1692851692909217909155905160029098019790975581549095168352958220909401805484168517905552825416179055611af7565b60018601546001600160a01b03166152bf5760408051606081018252600080825260208083018281528385018981526001600160a01b038b8116808652938d905295909320935184549086166001600160a01b031991821617855590516001808601805492909716918316919091179095559151600293840155928901805482168417905590880180549091169091179055611af7565b505060408051606081018252600286810180546001600160a01b03908116845260006020808601828152868801998a529983168083529a9052858120945185549083166001600160a01b0319918216178655985160018087018054928516928c169290921790915597519490930193909355805490921681529190912090920180548416851790555080549091169091179055565b6001600160a01b0381168114611c9357600080fd5b60006020828403121561537b57600080fd5b8135610a3e81615354565b80356004811061353257600080fd5b600080604083850312156153a857600080fd5b82356153b381615354565b91506153c160208401615386565b90509250929050565b600080604083850312156153dd57600080fd5b82356153e881615354565b915060208301356153f881615354565b809150509250929050565b60008060006060848603121561541857600080fd5b833561542381615354565b925061543160208501615386565b9150604084013561544181615354565b809150509250925092565b6000806000806080858703121561546257600080fd5b843561546d81615354565b9350602085013561547d81615354565b9250604085013561548d81615354565b9396929550929360600135925050565b600080600080600060a086880312156154b557600080fd5b85356154c081615354565b945060208601356154d081615354565b935060408601356154e081615354565b94979396509394606081013594506080013592915050565b6020808252825182820181905260009190848201906040850190845b818110156155395783516001600160a01b031683529284019291840191600101615514565b50909695505050505050565b60008060006060848603121561555a57600080fd5b833561556581615354565b95602085013595506040909401359392505050565b60006020828403121561558c57600080fd5b5035919050565b600080600080600060a086880312156155ab57600080fd5b85356155b681615354565b94506020860135935060408601356155cd81615354565b925060608601356155dd81615354565b949793965091946080013592915050565b6000806040838503121561560157600080fd5b823561560c81615354565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b60006020828403121561564257600080fd5b5051919050565b60006020828403121561565b57600080fd5b8151610a3e81615354565b634e487b7160e01b600052601160045260246000fd5b6000821982111561568f5761568f615666565b500190565b6000828210156156a6576156a6615666565b500390565b634e487b7160e01b600052603260045260246000fd5b60008160001904831182151516156156db576156db615666565b500290565b6000826156fd57634e487b7160e01b600052601260045260246000fd5b500490565b6000825160005b818110156157235760208186018101518583015201615709565b81811115615732576000828501525b509190910192915050565b8051801515811461353257600080fd5b60008060006060848603121561576257600080fd5b61576b8461573d565b9250602084015191506157806040850161573d565b90509250925092565b634e487b7160e01b600052603160045260246000fdfeaa997145358327b99ccedf396e9b7719eb7999623af1a7b38605739996c2ccfaa26469706673582212202f243efffddf1230e77365bc816a0d7943a79e28337a7a9f1d10d92e414b847e64736f6c634300080d0033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102535760003560e01c8063a086fc2211610146578063d59c9eb6116100c3578063e501ed0411610087578063e501ed04146106bc578063e61c6d6f146106ee578063e8462e8f146106f7578063f2f4ca1614610700578063f2fde38b1461076c578063f4ea93d81461077f57600080fd5b8063d59c9eb6146105bf578063db0577fd14610614578063defe205314610683578063df6d921214610696578063e34b5145146106a957600080fd5b8063b24be6871161010a578063b24be68714610559578063b59ec4781461056c578063b6f2bf1c1461058c578063c2af97871461059f578063cb830d03146105b257600080fd5b8063a086fc22146104c3578063a44026a314610518578063ac0b4b121461052b578063af8b1c6f1461053e578063b0772d0b1461055157600080fd5b80637907016a116101d45780638da5cb5b116101985780638da5cb5b146103f5578063947574ac1461040657806396bd512c1461044d5780639df5a1f2146104965780639fab1036146104b057600080fd5b80637907016a1461037c5780637f3ad0561461038f578063854f7ebb146103a257806385d7334d146103c25780638ccb720b146103d557600080fd5b80635fe3b5671161021b5780635fe3b567146103115780636a14602414610324578063715018a614610333578063720ceb021461033b578063789caa3e1461036957600080fd5b806320c342d9146102585780632ebf4be0146102905780633528e4ce146102be57806352f0f814146102d35780635acff027146102fe575b600080fd5b61027b610266366004615369565b60a36020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6102b061029e366004615369565b60a56020526000908152604090205481565b604051908152602001610287565b6102d16102cc366004615369565b61079b565b005b60aa546102e6906001600160a01b031681565b6040516001600160a01b039091168152602001610287565b6102e661030c366004615395565b6107e3565b60ae546102e6906001600160a01b031681565b6102b0670de0b6b3a764000081565b6102d16108dc565b61027b6103493660046153ca565b60a060209081526000928352604080842090915290825290205460ff1681565b6102e6610377366004615403565b610947565b6102d161038a36600461544c565b610a45565b60ad546102e6906001600160a01b031681565b6102b06103b0366004615369565b60a46020526000908152604090205481565b6102d16103d036600461549d565b610e88565b6103e86103e3366004615369565b6113ae565b60405161028791906154f8565b6033546001600160a01b03166102e6565b6104386104143660046153ca565b609f6020908152600092835260408084209091529082529020805460019091015482565b60408051928352602083019190915201610287565b61047b61045b366004615369565b60a76020526000908152604090205461ffff808216916201000090041682565b6040805161ffff938416815292909116602083015201610287565b61049e600881565b60405160ff9091168152602001610287565b6102d16104be366004615545565b611424565b6104f86104d1366004615369565b60a96020526000908152604090208054600182015460028301546003909301549192909184565b604080519485526020850193909352918301526060820152608001610287565b60b0546102e6906001600160a01b031681565b6102e661053936600461557a565b6119ac565b60af546102e6906001600160a01b031681565b6103e86119d6565b60ab546102e6906001600160a01b031681565b6102b061057a366004615369565b60b26020526000908152604090205481565b6102d161059a366004615593565b611a38565b6102d16105ad36600461549d565b611aff565b60b35461027b9060ff1681565b6105f56105cd366004615369565b60a86020526000908152604090205460ff808216916101008104821691620100009091041683565b6040805193151584529115156020840152151590820152606001610287565b610657610622366004615369565b60a66020526000908152604090205463ffffffff8116906001600160701b036401000000008204811691600160901b90041683565b6040805163ffffffff90941684526001600160701b039283166020850152911690820152606001610287565b60ac546102e6906001600160a01b031681565b60b1546102e6906001600160a01b031681565b6102e66106b73660046155ee565b611b93565b6104386106ca3660046153ca565b609e6020908152600092835260408084209091529082529020805460019091015482565b6102b060975481565b6102b060985481565b6099546107389067ffffffffffffffff80821691680100000000000000008104821691600160801b8204811691600160c01b90041684565b6040805167ffffffffffffffff95861681529385166020850152918416918301919091529091166060820152608001610287565b6102d161077a366004615369565b611bcb565b61078861271081565b60405161ffff9091168152602001610287565b6001600160a01b038116600090815260a86020526040902054819060ff166107d6576040516396e1352960e01b815260040160405180910390fd5b6107df82611ca5565b5050565b6000808260038111156107f8576107f861561a565b03610823576001600160a01b038381166000908152609a6020526040902060010154165b90506108d6565b60018260038111156108375761083761561a565b0361085f576001600160a01b038381166000908152609b60205260409020600101541661081c565b60028260038111156108735761087361561a565b0361089b576001600160a01b038381166000908152609c60205260409020600101541661081c565b60038260038111156108af576108af61561a565b036108d6576001600160a01b038381166000908152609d6020526040902060010154165b90505b92915050565b6033546001600160a01b0316331461093b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064015b60405180910390fd5b6109456000611cf7565b565b60008083600381111561095c5761095c61561a565b0361098a576001600160a01b0384166000908152609a602052604090206109839083611d49565b9050610a3e565b600183600381111561099e5761099e61561a565b036109c5576001600160a01b0384166000908152609b602052604090206109839083611d49565b60028360038111156109d9576109d961561a565b03610a00576001600160a01b0384166000908152609c602052604090206109839083611d49565b6003836003811115610a1457610a1461561a565b03610a3e576001600160a01b0384166000908152609d60205260409020610a3b9083611d49565b90505b9392505050565b6001600160a01b03808516600090815260a0602090815260408083209386168352929052205460ff161580610aa057506001600160a01b03808416600090815260a0602090815260408083209386168352929052205460ff16155b15610abe576040516301187a4360e61b815260040160405180910390fd5b610ac784611ca5565b610ad083611ca5565b610ade826000806000611d6c565b610afb5760405163b3165ffd60e01b815260040160405180910390fd5b610b2d6040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b610b378584611f2d565b60208083019190915260ae546040805163743aaa2360e11b81529051610bb8936001600160a01b039093169263e875544692600480820193918290030181865afa158015610b89573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bad9190615630565b602083015190611fdd565b821115610bd857604051633b1b989f60e01b815260040160405180910390fd5b610be6853385856000611ffc565b60ae54604080516307dc0d1d60e41b815290516000926001600160a01b031691637dc0d1d09160048083019260209291908290030181865afa158015610c30573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c549190615649565b60405163fc57d4df60e01b81526001600160a01b0387811660048301529192509082169063fc57d4df90602401602060405180830381865afa158015610c9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610cc29190615630565b825260405163fc57d4df60e01b81526001600160a01b03878116600483015282169063fc57d4df90602401602060405180830381865afa158015610d0a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d2e9190615630565b606083015281511580610d4357506060820151155b15610d6157604051634b6b62e560e01b815260040160405180910390fd5b610e10610e018360000151610dfb8560600151610df560ae60009054906101000a90046001600160a01b03166001600160a01b0316634ada90af6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610dca573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610dee9190615630565b8990611fdd565b90611fdd565b90612999565b610e0b87876129cb565b612a69565b60808301819052610e2690869086336000612a7f565b60808201516040805133815260208101869052908101919091526001600160a01b0380871691888216918716907fc2c75a73164c2efcbb9f74bfa511cd0866489d90687831a7217b3dbeeb6970889060600160405180910390a4505050505050565b6001600160a01b038316610eaf5760405163867915ab60e01b815260040160405180910390fd5b81600003610ed0576040516310eb483f60e21b815260040160405180910390fd5b610ed985611ca5565b610ee3858461341a565b6000610eee866134a6565b9050610f056001600160a01b038216863086613537565b6001600160a01b038616600090815260a960209081526040808320815160608101835284815292830184905290820192909252876001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f76573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f9a9190615630565b6020820152848152600182015415611096576000610fc982602001518460010154611fdd90919063ffffffff16565b825190915081111561101c578151604083018051610fe890839061567c565b90525060208201518251610ffb91612999565b83600101600082825461100e9190615694565b90915550506000825261104d565b808260400181815161102e919061567c565b90525060006001840155815181908390611049908390615694565b9052505b886001600160a01b03167f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df6846001015460405161108c91815260200190565b60405180910390a2505b8051158015906110bf57506001600160a01b038816600090815260a3602052604090205460ff16155b80156110fe57506001600160a01b0388166000908152609d602052604081206110f290600101546001600160a01b031690565b6001600160a01b031614155b15611185576000611114898360000151876135ba565b509050801561118357808260400181815161112f919061567c565b905250815181908390611143908390615694565b9052506001600160a01b038916600090815260a5602052604090205461116a908290612999565b83600301600082825461117d919061567c565b90915550505b505b604081015115611262576001600160a01b038816600090815260a4602052604080822054908301516111b691612999565b9050808360020160008282546111cc919061567c565b90915550506001600160a01b03808a166000908152609e60209081526040808320938b168352929052908120805483929061120890849061567c565b9250508190555061121e89858460400151613853565b886001600160a01b03166000805160206157a083398151915284600201548560030154604051611258929190918252602082015260400190565b60405180910390a2505b805115611324576112d6886001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112ce9190615630565b825190612999565b6001600160a01b03808a166000908152609e60209081526040808320938b168352929052908120600101805490919061131090849061567c565b909155505080516113249089908590613a3a565b61132e8887613b86565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518c8152938401919091529082015291929091908a16907f11adb3570ba55fd255b1f04252ca0071ae6639c86d4fd69e7c1bf1688afb493f906060015b60405180910390a45050505050505050565b6001600160a01b038116600090815260a1602090815260409182902080548351818402810184019094528084526060939283018282801561141857602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116113fa575b50505050509050919050565b81600003611445576040516310eb483f60e21b815260040160405180910390fd5b61144e83611ca5565b611458833361341a565b33600081815260b2602052604081204390556114779190859085611d6c565b156114955760405163df9db46360e01b815260040160405180910390fd5b60006114a0846134a6565b6001600160a01b038516600081815260a960209081526040808320815163182df0f560e01b815291519596508895939490938593919263182df0f59260048083019391928290030181865afa1580156114fd573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115219190615630565b604051633af9e66960e01b81523060048201529091506000906001600160a01b038a1690633af9e669906024016020604051808303816000875af115801561156d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115919190615630565b83549091501561166c5782546000906115aa9084611fdd565b9050858111806115b957508181115b1561160d5760006115ca8784612a69565b90506115d6818761567c565b95506115e28185612999565b8560000160008282546115f59190615694565b9091555061160590508188615694565b96505061162a565b611617818661567c565b6000855594506116278187615694565b95505b83546040519081526001600160a01b038b16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a2505b60008511801561169557506001600160a01b038916600090815260a3602052604090205460ff16155b80156116d457506001600160a01b0389166000908152609b602052604081206116c890600101546001600160a01b031690565b6001600160a01b031614155b1561176c5760006116f38a6116ed88610e0b8987615694565b8a613d99565b509050801561176a57611706818661567c565b94506117128187615694565b6001600160a01b038b16600090815260a46020526040902054909650611739908290612999565b6001600160a01b038b16600090815260a960205260408120600201805490919061176490849061567c565b90915550505b505b8315611863576001600160a01b038916600090815260a56020526040812054611796908690612999565b6001600160a01b038b16600090815260a960205260408120600301805492935083929091906117c690849061567c565b90915550506001600160a01b038a166000908152609f60209081526040808320338452909152812080548392906117fe90849061567c565b9091555050600284015460038501546040516001600160a01b038d16926000805160206157a08339815191529261183d92918252602082015260400190565b60405180910390a260006118518685612999565b1115611861576118618a86614014565b505b841561191b576118d5896001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156118aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118ce9190615630565b8690612999565b6001600160a01b038a166000908152609f602090815260408083203384529091528120600101805490919061190b90849061567c565b9091555061191b90508986614120565b61192589336141a9565b6119396001600160a01b038716338a614386565b6001600160a01b0389166000818152609f6020908152604080832033808552908352928190206001810154905482518e81529384019190915282820152517fc1cba78646fef030830d099fc25cb498953709c9d47d883848f81fd207174c9f9181900360600190a3505050505050505050565b60a281815481106119bc57600080fd5b6000918252602090912001546001600160a01b0316905081565b606060a2805480602002602001604051908101604052809291908181526020018280548015611a2e57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611a10575b5050505050905090565b83600003611a59576040516310eb483f60e21b815260040160405180910390fd5b6001600160a01b03808616600090815260a0602090815260408083209387168352929052205460ff16611a9f576040516301187a4360e61b815260040160405180910390fd5b611aa885611ca5565b6000611abd611ab787866129cb565b86612a69565b9050611acc8487836000611d6c565b15611aea57604051630cba3c5f60e21b815260040160405180910390fd5b611af78682868686612a7f565b505050505050565b81600003611b20576040516310eb483f60e21b815260040160405180910390fd5b6001600160a01b03808616600090815260a0602090815260408083209387168352929052205460ff16611b66576040516301187a4360e61b815260040160405180910390fd5b611b6f85611ca5565b6000611b84611b7e8786611f2d565b84612a69565b9050611af78686868486611ffc565b60a16020528160005260406000208181548110611baf57600080fd5b6000918252602090912001546001600160a01b03169150829050565b6033546001600160a01b03163314611c255760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610932565b6001600160a01b038116611c8a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610932565b611c9381611cf7565b50565b6001600160a01b03163b151590565b604080516001600160a01b038381166024808401919091528351808403909101815260449092019092526020810180516001600160e01b0316631a94726760e11b17905260ad546107df921690614404565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001600160a01b0390811660009081526020929092526040909120600101541690565b60008060ae60009054906101000a90046001600160a01b03166001600160a01b0316637dc0d1d06040518163ffffffff1660e01b8152600401602060405180830381865afa158015611dc2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611de69190615649565b6001600160a01b038716600090815260a16020908152604080832054815160a08101835284815292830184905290820183905260608201839052608082018390529293509080805b84811015611f1f576001600160a01b038b16600090815260a160205260408120805483908110611e6057611e606156ab565b6000918252602090912001546001600160a01b03169050611e828c828961449e565b9450846020015184611e94919061567c565b9350846040015183611ea6919061567c565b9250806001600160a01b03168b6001600160a01b031603611f16578815611ee4576060850151611ed7908a90611fdd565b611ee1908461567c565b92505b8915611f1657611f098560800151610df587606001518d611fdd90919063ffffffff16565b611f139085615694565b93505b50600101611e2e565b501198975050505050505050565b6001600160a01b038083166000818152609f602090815260408083209486168352938152838220845180860186528154815260019091015481830152845163aa5af0fd60e01b8152945192949093611fa793909263aa5af0fd92600480820193918290030181865afa158015610b89573d6000803e3d6000fd5b6001600160a01b038516600090815260a560205260409020548251611fcb91611fdd565b611fd5919061567c565b949350505050565b6000670de0b6b3a7640000611ff283856156c1565b6108d391906156e0565b6001600160a01b038316600090815260b260205260409020544390036120355760405163dff88f5160e01b815260040160405180910390fd5b6000612040866134a6565b90506120576001600160a01b038216863086613537565b6120a66040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60208082018590528382526040805163aa5af0fd60e01b815290516001600160a01b038a169263aa5af0fd92600480820193918290030181865afa1580156120f2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121169190615630565b60608201526001600160a01b038088166000908152609f602090815260408083209389168352929052206001015460c08201819052156122c457606081015160c082015161216391611fdd565b6040820181905260208201511015612277576020810151610100820181905260c0820151606083015161219992610e0b91612999565b6001600160a01b038089166000908152609f60209081526040808320938a16835292905290812060010180549091906121d3908490615694565b909155506121e3905087866141a9565b6121f38783836101000151613853565b6121fd878661461f565b6001600160a01b038781166000818152609f602090815260408083208a8616808552908352928190206001810154905482518b815293840191909152828201525192939192918a16917f7b417e520d2b905fc5a1689d29d329358dd55efc60ed115aa165b0a2b64232c69181900360600190a45050612992565b60408101516101008201819052602082018051612295908390615694565b9052506001600160a01b038088166000908152609f602090815260408083209389168352929052908120600101555b6001600160a01b03808816600081815260a96020908152604080832060a4835281842054608088015284845260a583528184205460a08801908152948452609f8352818420958b1684529482529091205491519084015161232a9291610e0b9190612999565b6001600160a01b03808a166000908152609f60209081526040808320938b1683529290529081208054909190612361908490615694565b90915550612371905088876141a9565b60008260200151118015612389575060008160010154115b1561251f5760006123ab83606001518360010154611fdd90919063ffffffff16565b9050826020015181111561243557606083015160208401516123cc91612999565b8260010160008282546123df9190615694565b909155505060a083015160208401516123f791612999565b82600301600082825461240a9190615694565b909155505060208301516101008401805161242690839061567c565b90525060006020840152612494565b6000600183015560a083015161244c908290612999565b82600301600082825461245f9190615694565b92505081905550808361010001818151612479919061567c565b905250602083018051829190612490908390615694565b9052505b886001600160a01b03167f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df683600101546040516124d391815260200190565b60405180910390a2886001600160a01b03166000805160206157a083398151915283600201548460030154604051612515929190918252602082015260400190565b60405180910390a2505b602082015115612679576125d66125478360a001518360030154611fdd90919063ffffffff16565b6125b48a6001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612588573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125ac9190615630565b845490611fdd565b608085015160028501546125c791611fdd565b6125d19190615694565b614897565b60e08301819052156126795760006125f68360e001518460200151612a69565b9050808360200181815161260a9190615694565b90525060a083015161261d908290612999565b8260030160008282546126309190615694565b9091555050600282015460038301546040516001600160a01b038c16926000805160206157a08339815191529261266f92918252602082015260400190565b60405180910390a2505b600082602001511180156126a657506001600160a01b038816600090815260a3602052604090205460ff16155b80156126e557506001600160a01b0388166000908152609d602052604081206126d990600101546001600160a01b031690565b6001600160a01b031614155b15612765576000806127008a856020015186600001516135ba565b9150915080846000015111612718576000845261272e565b808460000181815161272a9190615694565b9052505b81156127625781846020018181516127469190615694565b9052506101008401805183919061275e90839061567c565b9052505b50505b6127758884846101000151613853565b60208201511561291657600061279489846020015185600001516148b2565b9050826020015181101561287057612817896001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127e3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128079190615630565b828560200151610dfb9190615694565b82600001600082825461282a919061567c565b909155505081546040519081526001600160a01b038a16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a25b6080830151612880908290612999565b8260020160008282546128939190615694565b909155505060a083015160208401516128ab91612999565b8260030160008282546128be9190615694565b9091555050600282015460038301546040516001600160a01b038c16926000805160206157a0833981519152926128fd92918252602082015260400190565b60405180910390a261291489858560200151613a3a565b505b612920888761461f565b6001600160a01b038881166000818152609f602090815260408083208b8616808552908352928190206001810154905482518c8152938401919091529082015291929091908a16907f7b417e520d2b905fc5a1689d29d329358dd55efc60ed115aa165b0a2b64232c69060600161139c565b5050505050565b6000670de0b6b3a7640000826129af85836156c1565b6129c190670de0b6b3a76400006156c1565b611ff291906156e0565b6001600160a01b038083166000818152609e602090815260408083209486168352938152838220845180860186528154815260019091015481830152845163182df0f560e01b8152945192949093612a4593909263182df0f592600480820193918290030181865afa158015610b89573d6000803e3d6000fd5b6001600160a01b038516600090815260a460205260409020548251611fcb91611fdd565b6000818310612a7857816108d3565b5090919050565b83600003612aa0576040516310eb483f60e21b815260040160405180910390fd5b612ae96040518060e0016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160006001600160a01b031681525090565b612af2866134a6565b6001600160a01b0390811660c083015260208201869052828252604051633af9e66960e01b815230600482015290871690633af9e669906024016020604051808303816000875af1158015612b4b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b6f9190615630565b816080018181525050856001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015612bb6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bda9190615630565b60408201819052612bec908690612999565b600003612c0c576040516393c76c6f60e01b815260040160405180910390fd5b6001600160a01b038087166000908152609e60209081526040808320938816835292905220600101548015612e23576000612c54836040015183611fdd90919063ffffffff16565b90508260200151811180612c6b5750826080015181115b15612cf557612c8283602001518460800151612a69565b60a08401819052602084018051612c9a908390615694565b905250604083015160a0840151612cb091612999565b6001600160a01b03808a166000908152609e60209081526040808320938b1683529290529081206001018054909190612cea908490615694565b90915550612d3f9050565b60a08301819052602083018051829190612d10908390615694565b9052506001600160a01b038089166000908152609e60209081526040808320938a168352929052908120600101555b8260200151600003612e2157612d558887613b86565b612d5f888761461f565b6000612d7c84604001518560a0015161299990919063ffffffff16565b1115612d9057612d90888460a00151614014565b60c0830151612da9906001600160a01b03168689614386565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518e815293840191909152828201525192938916927f378f9d375cd79e36c19c26a9e57791fe7cd5953b61986c01ebf980c0efb928019181900360600190a4505050612992565b505b6001600160a01b03808816600081815260a96020908152604080832060a483528184205460608901908152948452609e8352818420958b16845294825290912054915190850151612e799291610e0b9190612999565b6001600160a01b03808a166000908152609e60209081526040808320938b1683529290529081208054909190612eb0908490615694565b90915550612ec090508887613b86565b60008360200151118015612ed45750805415155b156130a15760408301518154600091612eed9190611fdd565b90508360200151811180612f1357508360a001518460800151612f109190615694565b81115b15612fc4576000612f3685602001518660a001518760800151610e0b9190615694565b9050612f4f85604001518261299990919063ffffffff16565b836000016000828254612f629190615694565b90915550506060850151612f77908290612999565b836002016000828254612f8a9190615694565b92505081905550808560a001818151612fa3919061567c565b905250602085018051829190612fba908390615694565b90525061301d9050565b808460a001818151612fd6919061567c565b905250602084018051829190612fed908390615694565b905250600082556060840151613004908290612999565b8260020160008282546130179190615694565b90915550505b81546040519081526001600160a01b038a16907f1cf8705a784a46d32023f3694b5e8149137d563085a870fde2f54a6cc5c59df79060200160405180910390a2886001600160a01b03166000805160206157a083398151915283600201548460030154604051613097929190918252602082015260400190565b60405180910390a2505b600083602001511180156130ce57506001600160a01b038816600090815260a3602052604090205460ff16155b801561310d57506001600160a01b0388166000908152609b6020526040812061310190600101546001600160a01b031690565b6001600160a01b031614155b156131a05760008061313c8a61313587602001518860a001518960800151610e0b9190615694565b8751613d99565b9150915080856000015111613154576000855261316a565b80856000018181516131669190615694565b9052505b811561319d5781856020018181516131829190615694565b90525060a08501805183919061319990839061567c565b9052505b50505b60006131bd84604001518560a0015161299990919063ffffffff16565b11156131d1576131d1888460a00151614014565b6020830151156133885760006131f08985602001518660000151614b35565b905083602001518110156132cf57613273896001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561323f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132639190615630565b828660200151610dfb9190615694565b826001016000828254613286919061567c565b909155505060018201546040519081526001600160a01b038a16907f8113f59ef078158acce9021327489b70d6ab15d0c107c36455c3505248648df69060200160405180910390a25b606084015160208501516132e291612999565b8260020160008282546132f59190615694565b90915550506001600160a01b038916600090815260a5602052604090205461331e908290612999565b8260030160008282546133319190615694565b9091555050600282015460038301546040516001600160a01b038c16926000805160206157a08339815191529261337092918252602082015260400190565b60405180910390a2613386898560200151614120565b505b613392888761461f565b60c08301516133ab906001600160a01b03168689614386565b6001600160a01b038881166000818152609e602090815260408083208b8616808552908352928190206001810154905482518e8152938401919091529082015291928816917f378f9d375cd79e36c19c26a9e57791fe7cd5953b61986c01ebf980c0efb928019060600161139c565b6001600160a01b03808316600090815260a0602090815260408083209385168352929052205460ff166107df576001600160a01b03808316600081815260a0602090815260408083209486168352938152838220805460ff1916600190811790915560a18252938220805494850181558252902090910180546001600160a01b03191690911790555050565b60b0546000906001600160a01b03908116908316036134d057505060b1546001600160a01b031690565b816001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa15801561350e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108d69190615649565b919050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806129925760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606401610932565b600080826000036135d05750600090508061384b565b6136026040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b856001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613640573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136649190615630565b6040808301919091526001600160a01b038716600090815260a5602052908120548252805a60808401525b86851080156136d457506001600160a01b0388166000908152609d602052604081206136c590600101546001600160a01b031690565b9250826001600160a01b031614155b80156136ee5750855a84608001516136ec9190615694565b105b1561383557506001600160a01b038088166000908152609f60209081526040808320938516835292905281902090830151600182015461372d91611fdd565b606084015260008080613740888b615694565b905080866060015111613782578551606087015161375d91612999565b8454613769919061567c565b915085606001518861377b919061567c565b97506137c3565b6040860151613792908290612999565b84600101546137a19190615694565b86519093506137b1908290612999565b84546137bd919061567c565b91508997505b600184018390558184556137d78b866141a9565b8a6001600160a01b0316856001600160a01b03167f0aec3812ec00f2d2f0eacc89fd13923091a68f30c3b3d0336e364544322b97588585604051613825929190918252602082015260400190565b60405180910390a350505061368f565b5a83608001516138459190615694565b93505050505b935093915050565b6040516305eff7ef60e21b81523060048201526138c39082906001600160a01b038616906317bfdfbc906024016020604051808303816000875af115801561389f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e0b9190615630565b90508015613a355760b0546001600160a01b03908116908416036139985760b154604051632e1a7d4d60e01b8152600481018390526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b15801561392757600080fd5b505af115801561393b573d6000803e3d6000fd5b50505050826001600160a01b0316634e4d9fea826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561397a57600080fd5b505af115801561398e573d6000803e3d6000fd5b5050505050505050565b6139ac6001600160a01b0383168483614da2565b60405163073a938160e11b8152600481018290526001600160a01b03841690630e752702906024016020604051808303816000875af11580156139f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a179190615630565b15613a3557604051637112354b60e01b815260040160405180910390fd5b505050565b60b0546001600160a01b0390811690841603613ae95760b154604051632e1a7d4d60e01b8152600481018390526001600160a01b0390911690632e1a7d4d90602401600060405180830381600087803b158015613a9657600080fd5b505af1158015613aaa573d6000803e3d6000fd5b50505050826001600160a01b0316631249c58b826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561397a57600080fd5b613afd6001600160a01b0383168483614da2565b60405163140e25ad60e31b8152600481018290526001600160a01b0384169063a0712d68906024016020604051808303816000875af1158015613b44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613b689190615630565b15613a355760405163757d648760e11b815260040160405180910390fd5b6001600160a01b038281166000818152609e6020908152604080832094861680845294825280832060018101549054858552609b84528285208786528452828520600290810154968652609a855283862097865296909352922090930154609854919392918411613c21576001600160a01b038087166000908152609e60209081526040808320938916835292905290812060010181905593505b838214613c7e578115613c50576001600160a01b0386166000908152609b60205260409020613c509086614e19565b8315613c7e576097546001600160a01b0387166000908152609b60205260409020613c7e9187908790614fae565b6098548311613cb4576001600160a01b038087166000908152609e60209081526040808320938916835292905290812081905592505b828114613d11578015613ce3576001600160a01b0386166000908152609a60205260409020613ce39086614e19565b8215613d11576097546001600160a01b0387166000908152609a60205260409020613d119187908690614fae565b60ac546001600160a01b031615611af75760ac5460405163636c55d360e01b81526001600160a01b0387811660048301528881166024830152604482018590529091169063636c55d3906064015b600060405180830381600087803b158015613d7957600080fd5b505af1158015613d8d573d6000803e3d6000fd5b50505050505050505050565b60008082600003613daf5750600090508061384b565b613de16040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b856001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015613e1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613e439190615630565b6040808301919091526001600160a01b038716600090815260a4602052908120548252805a60808401525b8685108015613eb357506001600160a01b0388166000908152609b60205260408120613ea490600101546001600160a01b031690565b9250826001600160a01b031614155b8015613ecd5750855a8460800151613ecb9190615694565b105b1561383557506001600160a01b038088166000908152609e602090815260408083209385168352929052819020908301516001820154613f0c91611fdd565b606084015260008080613f1f888b615694565b905080866060015111613f615785516060870151613f3c91612999565b8454613f48919061567c565b9150856060015188613f5a919061567c565b9750613fa2565b6040860151613f71908290612999565b8460010154613f809190615694565b8651909350613f90908290612999565b8454613f9c919061567c565b91508997505b60018401839055818455613fb68b86613b86565b8a6001600160a01b0316856001600160a01b03167f76908587112671ab2dcd9323f0d9b27d193156f95fe5e1251411a151c20e82dd8585604051614004929190918252602082015260400190565b60405180910390a3505050613e6e565b60405163852a12e360e01b8152600481018290526001600160a01b0383169063852a12e3906024016020604051808303816000875af115801561405b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061407f9190615630565b1561409d57604051637d47a0c360e01b815260040160405180910390fd5b60b0546001600160a01b03908116908316036107df5760b160009054906101000a90046001600160a01b03166001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004016000604051808303818588803b15801561410357600080fd5b505af1158015614117573d6000803e3d6000fd5b50505050505050565b60405163317afabb60e21b8152600481018290526001600160a01b0383169063c5ebeaec906024016020604051808303816000875af1158015614167573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061418b9190615630565b1561409d576040516302738d6b60e11b815260040160405180910390fd5b6001600160a01b038281166000818152609f6020908152604080832094861680845294825280832060018101549054858552609d84528285208786528452828520600290810154968652609c855283862097865296909352922090930154609854919392918411614244576001600160a01b038087166000908152609f60209081526040808320938916835292905290812060010181905593505b8382146142a1578115614273576001600160a01b0386166000908152609d602052604090206142739086614e19565b83156142a1576097546001600160a01b0387166000908152609d602052604090206142a19187908790614fae565b60985483116142d7576001600160a01b038087166000908152609f60209081526040808320938916835292905290812081905592505b828114614334578015614306576001600160a01b0386166000908152609c602052604090206143069086614e19565b8215614334576097546001600160a01b0387166000908152609c602052604090206143349187908690614fae565b60ac546001600160a01b031615611af75760ac5460405163a9de645d60e01b81526001600160a01b0387811660048301528881166024830152604482018590529091169063a9de645d90606401613d5f565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806143fe5760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606401610932565b50505050565b6060600080846001600160a01b0316846040516144219190615702565b600060405180830381855af49150503d806000811461445c576040519150601f19603f3d011682016040523d82523d6000602084013e614461565b606091505b509150915081156144755791506108d69050565b8051156144855780518082602001fd5b60405163037b81af60e11b815260040160405180910390fd5b6144d06040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b60405163fc57d4df60e01b81526001600160a01b03848116600483015283169063fc57d4df90602401602060405180830381865afa158015614516573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061453a9190615630565b6060820181905260000361456157604051634b6b62e560e01b815260040160405180910390fd5b60ae54604051638e8f294b60e01b81526001600160a01b03858116600483015290911690638e8f294b90602401606060405180830381865afa1580156145ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906145cf919061574d565b5060808301525060608101516145e990610df585876129cb565b815260608101516145fe90610df58587611f2d565b60408201526080810151815161461391611fdd565b60208201529392505050565b6001600160a01b03808316600090815260a0602090815260408083209385168352929052205460ff16801561467757506001600160a01b038083166000908152609e6020908152604080832093851683529290522054155b80156146a957506001600160a01b038083166000908152609e6020908152604080832093851683529290522060010154155b80156146d857506001600160a01b038083166000908152609f6020908152604080832093851683529290522054155b801561470a57506001600160a01b038083166000908152609f6020908152604080832093851683529290522060010154155b156107df5760005b6001600160a01b03828116600090815260a16020526040902080549185169183908110614741576147416156ab565b6000918252602090912001546001600160a01b03161461476357600101614712565b6001600160a01b03808416600090815260a0602090815260408083209386168352928152828220805460ff1916905560a1905220546147a3600182615694565b8214614848576001600160a01b038316600090815260a1602052604090206147cc600183615694565b815481106147dc576147dc6156ab565b60009182526020808320909101546001600160a01b03868116845260a19092526040909220805491909216919084908110614819576148196156ab565b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b6001600160a01b038316600090815260a16020526040902080548061486f5761486f615789565b600082815260209020810160001990810180546001600160a01b031916905501905550505050565b6000818310156148a85760006108d3565b6108d38284615694565b6000816000036148c457506000610a3e565b6148f66040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b846001600160a01b031663182df0f56040518163ffffffff1660e01b8152600401602060405180830381865afa158015614934573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906149589190615630565b6040808301919091526001600160a01b038616600090815260a460205290812054825280855a60808501525b6000811180156149ca57506001600160a01b0388166000908152609a602052604081206149bb90600101546001600160a01b031690565b9350836001600160a01b031614155b80156149e45750855a85608001516149e29190615694565b105b15614b1f576001600160a01b038089166000908152609e6020908152604080832093871683529290522084518154919350614a1f9190611fdd565b6060850181905260009081908310614a6c5760408601516060870151614a4491612999565b8460010154614a53919061567c565b9150856060015183614a659190615694565b9250614aae565b6040860151614a7c908490612999565b8460010154614a8b919061567c565b8651909250614a9b908490612999565b8454614aa79190615694565b9050600092505b60018401829055808455614ac28a86613b86565b896001600160a01b0316856001600160a01b03167f76908587112671ab2dcd9323f0d9b27d193156f95fe5e1251411a151c20e82dd8484604051614b10929190918252602082015260400190565b60405180910390a35050614984565b614b298188615694565b98975050505050505050565b600081600003614b4757506000610a3e565b614b796040518060a0016040528060008152602001600081526020016000815260200160008152602001600081525090565b846001600160a01b031663aa5af0fd6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614bb7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190614bdb9190615630565b6040808301919091526001600160a01b038616600090815260a560205290812054825280855a60808501525b600081118015614c4d57506001600160a01b0388166000908152609c60205260408120614c3e90600101546001600160a01b031690565b9350836001600160a01b031614155b8015614c675750855a8560800151614c659190615694565b105b15614b1f576001600160a01b038089166000908152609f6020908152604080832093871683529290522084518154919350614ca29190611fdd565b6060850181905260009081908310614cef5760408601516060870151614cc791612999565b8460010154614cd6919061567c565b9150856060015183614ce89190615694565b9250614d31565b6040860151614cff908490612999565b8460010154614d0e919061567c565b8651909250614d1e908490612999565b8454614d2a9190615694565b9050600092505b60018401829055808455614d458a866141a9565b896001600160a01b0316856001600160a01b03167f0aec3812ec00f2d2f0eacc89fd13923091a68f30c3b3d0336e364544322b97588484604051614d93929190918252602082015260400190565b60405180910390a35050614c07565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806143fe5760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606401610932565b6001600160a01b0381166000908152602083905260408120600201549003614e545760405163e76ea87f60e01b815260040160405180910390fd5b6001600160a01b038082166000908152602084815260409182902082516060810184528154851680825260018301549095169281019290925260020154918101919091529015614edd5760208181015182516001600160a01b03908116600090815292869052604090922060010180546001600160a01b03191692909116919091179055614f04565b60208101516001840180546001600160a01b0319166001600160a01b039092169190911790555b60208101516001600160a01b031615614f505780516020808301516001600160a01b03908116600090815291869052604090912080546001600160a01b03191691909216179055614f74565b80516002840180546001600160a01b0319166001600160a01b039092169190911790555b506001600160a01b031660009081526020919091526040812080546001600160a01b03199081168255600182018054909116905560020155565b81600003614fcf5760405163ba3b5b5960e01b815260040160405180910390fd5b6001600160a01b038316614ff65760405163867915ab60e01b815260040160405180910390fd5b6001600160a01b038316600090815260208590526040902060020154156150305760405163f5ab373160e01b815260040160405180910390fd5b60018401546000906001600160a01b03165b8282108015615061575060028601546001600160a01b03828116911614155b801561508857506001600160a01b0381166000908152602087905260409020600201548411155b156150b5576001600160a01b03908116600090815260208790526040902060019081015492019116615042565b6001600160a01b038116158015906150e757506001600160a01b03811660009081526020879052604090206002015484115b156152285760018601546001600160a01b039081169082160361518f576040805160608101825260008082526001600160a01b0384811660208085018281528587018b81528c8516808752928e9052878620965187549086166001600160a01b0319918216178855915160018089018054929097169184169190911790955551600290960195909555918b018054851683179055825292902080549091169091179055611af7565b604080516060810182526001600160a01b03808416600081815260208b81528582208054851686528186018481528688018c81528d8716808652938f9052888520975188549088166001600160a01b0319918216178955915160018981018054928a1692851692909217909155905160029098019790975581549095168352958220909401805484168517905552825416179055611af7565b60018601546001600160a01b03166152bf5760408051606081018252600080825260208083018281528385018981526001600160a01b038b8116808652938d905295909320935184549086166001600160a01b031991821617855590516001808601805492909716918316919091179095559151600293840155928901805482168417905590880180549091169091179055611af7565b505060408051606081018252600286810180546001600160a01b03908116845260006020808601828152868801998a529983168083529a9052858120945185549083166001600160a01b0319918216178655985160018087018054928516928c169290921790915597519490930193909355805490921681529190912090920180548416851790555080549091169091179055565b6001600160a01b0381168114611c9357600080fd5b60006020828403121561537b57600080fd5b8135610a3e81615354565b80356004811061353257600080fd5b600080604083850312156153a857600080fd5b82356153b381615354565b91506153c160208401615386565b90509250929050565b600080604083850312156153dd57600080fd5b82356153e881615354565b915060208301356153f881615354565b809150509250929050565b60008060006060848603121561541857600080fd5b833561542381615354565b925061543160208501615386565b9150604084013561544181615354565b809150509250925092565b6000806000806080858703121561546257600080fd5b843561546d81615354565b9350602085013561547d81615354565b9250604085013561548d81615354565b9396929550929360600135925050565b600080600080600060a086880312156154b557600080fd5b85356154c081615354565b945060208601356154d081615354565b935060408601356154e081615354565b94979396509394606081013594506080013592915050565b6020808252825182820181905260009190848201906040850190845b818110156155395783516001600160a01b031683529284019291840191600101615514565b50909695505050505050565b60008060006060848603121561555a57600080fd5b833561556581615354565b95602085013595506040909401359392505050565b60006020828403121561558c57600080fd5b5035919050565b600080600080600060a086880312156155ab57600080fd5b85356155b681615354565b94506020860135935060408601356155cd81615354565b925060608601356155dd81615354565b949793965091946080013592915050565b6000806040838503121561560157600080fd5b823561560c81615354565b946020939093013593505050565b634e487b7160e01b600052602160045260246000fd5b60006020828403121561564257600080fd5b5051919050565b60006020828403121561565b57600080fd5b8151610a3e81615354565b634e487b7160e01b600052601160045260246000fd5b6000821982111561568f5761568f615666565b500190565b6000828210156156a6576156a6615666565b500390565b634e487b7160e01b600052603260045260246000fd5b60008160001904831182151516156156db576156db615666565b500290565b6000826156fd57634e487b7160e01b600052601260045260246000fd5b500490565b6000825160005b818110156157235760208186018101518583015201615709565b81811115615732576000828501525b509190910192915050565b8051801515811461353257600080fd5b60008060006060848603121561576257600080fd5b61576b8461573d565b9250602084015191506157806040850161573d565b90509250925092565b634e487b7160e01b600052603160045260246000fdfeaa997145358327b99ccedf396e9b7719eb7999623af1a7b38605739996c2ccfaa26469706673582212202f243efffddf1230e77365bc816a0d7943a79e28337a7a9f1d10d92e414b847e64736f6c634300080d0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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