ETH Price: $2,177.91 (+2.11%)

Contract

0xE710Cff4ebbB4f027fE2Daf47b8A23EA10cbC389
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Set Addresses242794742026-01-21 0:07:5963 days ago1768954079IN
0xE710Cff4...A10cbC389
0 ETH0.000002840.0811325
New Strategy242794732026-01-21 0:07:4763 days ago1768954067IN
0xE710Cff4...A10cbC389
0 ETH0.000339370.07635113
New Strategy242794732026-01-21 0:07:4763 days ago1768954067IN
0xE710Cff4...A10cbC389
0 ETH0.000338430.07635113
New Strategy242794692026-01-21 0:06:5963 days ago1768954019IN
0xE710Cff4...A10cbC389
0 ETH0.000309860.06976857

Latest 3 internal transactions

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x6101a060242794732026-01-21 0:07:4763 days ago1768954067
0xE710Cff4...A10cbC389
 Contract Creation0 ETH
0x6101a060242794732026-01-21 0:07:4763 days ago1768954067
0xE710Cff4...A10cbC389
 Contract Creation0 ETH
0x6101a060242794692026-01-21 0:06:5963 days ago1768954019
0xE710Cff4...A10cbC389
 Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
MorphoBlueLenderBorrowerFactory

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;

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

contract MorphoBlueLenderBorrowerFactory {
    event NewStrategy(address indexed strategy, Id indexed marketId);

    address public immutable GOV;
    address public immutable morpho;
    address public immutable router;

    address public management;
    address public performanceFeeRecipient;
    address public keeper;
    address public emergencyAdmin;

    /// @notice Track deployments by market id.
    mapping(Id => address) public deployments;

    constructor(
        address _management,
        address _performanceFeeRecipient,
        address _keeper,
        address _emergencyAdmin,
        address _gov,
        address _morpho,
        address _router
    ) {
        require(_gov != address(0));
        management = _management;
        performanceFeeRecipient = _performanceFeeRecipient;
        keeper = _keeper;
        emergencyAdmin = _emergencyAdmin;
        GOV = _gov;
        morpho = _morpho;
        router = _router;
    }

    function newStrategy(
        address _asset,
        string calldata _name,
        address _borrowToken,
        address _lenderVault,
        Id _marketId,
        address _borrowUsdOracle
    ) external virtual returns (address) {
        require(deployments[_marketId] == address(0));
        IStrategyInterface _newStrategy = IStrategyInterface(
            address(
                new MorphoBlueLenderBorrower(
                    _asset,
                    _name,
                    _borrowToken,
                    _lenderVault,
                    GOV,
                    morpho,
                    _marketId,
                    _borrowUsdOracle,
                    router
                )
            )
        );

        _newStrategy.setPerformanceFeeRecipient(performanceFeeRecipient);
        _newStrategy.setKeeper(keeper);
        _newStrategy.setPendingManagement(management);
        _newStrategy.setEmergencyAdmin(emergencyAdmin);

        emit NewStrategy(address(_newStrategy), _marketId);
        deployments[_marketId] = address(_newStrategy);
        return address(_newStrategy);
    }

    function setAddresses(
        address _management,
        address _performanceFeeRecipient,
        address _keeper,
        address _emergencyAdmin
    ) external {
        require(msg.sender == management, "!management");
        management = _management;
        performanceFeeRecipient = _performanceFeeRecipient;
        keeper = _keeper;
        emergencyAdmin = _emergencyAdmin;
    }


}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {BaseLenderBorrower} from "./BaseLenderBorrower.sol";
import {IMorpho, Id, MarketParams, Position} from "./interfaces/morpho/IMorpho.sol";
import {IChainlinkAggregator} from "./interfaces/IChainlinkAggregator.sol";
import {IOracle} from "./interfaces/morpho/IOracle.sol";
import {MorphoBalancesLib, MorphoLib} from "./libraries/morpho/periphery/MorphoBalancesLib.sol";
import {SharesMathLib} from "./libraries/morpho/SharesMathLib.sol";
import {UniswapV3Swapper} from "@periphery/swappers/UniswapV3Swapper.sol";

contract MorphoBlueLenderBorrower is BaseLenderBorrower, UniswapV3Swapper {
    using SafeERC20 for ERC20;
    using MorphoBalancesLib for IMorpho;
    using MorphoLib for IMorpho;

    uint256 internal constant ORACLE_PRICE_SCALE = 1e36;

    IMorpho public immutable morpho;
    Id public immutable marketId;
    MarketParams public marketParams;

    address public immutable GOV;

    uint256 internal immutable COLL_SCALE;

    uint256 internal immutable BORROW_SCALE;

    /// @notice USD price feed (1e8) for borrow token.
    /// @dev Collateral price is derived using Morpho's oracle (collateral -> borrow) then this oracle (borrow -> USD).
    address public borrowUsdOracle;

    constructor(
        address _asset,
        string memory _name,
        address _borrowToken,
        address _lenderVault,
        address _gov,
        address _morpho,
        Id _marketId,
        address _borrowUsdOracle,
        address _router
    ) BaseLenderBorrower(_asset, _name, _borrowToken, _lenderVault) {
        GOV = _gov;
        morpho = IMorpho(_morpho);
        marketId = _marketId;

        marketParams = morpho.idToMarketParams(_marketId);
        require(
            marketParams.loanToken == _borrowToken &&
                marketParams.collateralToken == _asset
        );
        COLL_SCALE = 10 ** ERC20(_asset).decimals();
        BORROW_SCALE = 10 ** ERC20(_borrowToken).decimals();

        ERC20(_asset).forceApprove(_morpho, type(uint256).max);
        ERC20(_borrowToken).forceApprove(_morpho, type(uint256).max);

        _setMinAmountToSell(1e4);
        router = _router;

        require(IChainlinkAggregator(_borrowUsdOracle).decimals() == 8);
        borrowUsdOracle = _borrowUsdOracle;
    }

    /*//////////////////////////////////////////////////////////////
                            WRITE FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function _supplyCollateral(uint256 amount) internal virtual override {
        if (amount == 0) return;
        morpho.supplyCollateral(marketParams, amount, address(this), "");
    }

    function _withdrawCollateral(uint256 amount) internal virtual override {
        if (amount == 0) return;
        morpho.withdrawCollateral(
            marketParams,
            amount,
            address(this),
            address(this)
        );
    }

    function _borrow(uint256 amount) internal virtual override {
        if (amount == 0) return;
        morpho.borrow(marketParams, amount, 0, address(this), address(this));
    }

    // Need to give shares as input to avoid rounding errors on full repays.
    function _repay(uint256 amount) internal virtual override {
        if (amount == 0) return;
        (
            ,
            ,
            uint256 totalBorrowAssets,
            uint256 totalBorrowShares
        ) = MorphoBalancesLib.expectedMarketBalances(morpho, marketParams);

        uint256 shares = Math.min(
            SharesMathLib.toSharesDown(
                amount,
                totalBorrowAssets,
                totalBorrowShares
            ),
            morpho.borrowShares(marketId, address(this))
        );

        morpho.repay(marketParams, 0, shares, address(this), "");
    }

    /*//////////////////////////////////////////////////////////////
                            VIEW FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function _getPrice(
        address _asset
    ) internal view virtual override returns (uint256 price) {
        if (_asset == borrowToken) {
            price = _readUsdOracle();
        } else if (_asset == address(asset)) {
            // Use Morpho's oracle to get collateral price in borrow token, then convert to USD
            // Morpho oracle returns: (collateral amount) / (borrow token amount) scaled by 1e36 relative to decimals.
            uint256 borrowUsd = _readUsdOracle();
            uint256 ratio = IOracle(marketParams.oracle).price();
            price = (ratio * borrowUsd * COLL_SCALE) / (BORROW_SCALE * ORACLE_PRICE_SCALE);
        } else {
            revert();
        }
    }

    function _isSupplyPaused() internal view virtual override returns (bool) {
        return false;
    }

    function _isBorrowPaused() internal view virtual override returns (bool) {
        return false;
    }

    function _isLiquidatable() internal view virtual override returns (bool) {
        Position memory p = morpho.position(marketId, address(this));
        if (p.borrowShares == 0) return false;

        uint256 collateralValue = (uint256(p.collateral) *
            IOracle(marketParams.oracle).price()) / ORACLE_PRICE_SCALE;
        uint256 maxBorrow = (collateralValue * marketParams.lltv) / WAD;

        return balanceOfDebt() > maxBorrow;
    }

    function _maxCollateralDeposit()
        internal
        view
        virtual
        override
        returns (uint256)
    {
        return type(uint256).max;
    }

    function _maxBorrowAmount()
        internal
        view
        virtual
        override
        returns (uint256)
    {
        (uint256 totalSupplyAssets, , uint256 totalBorrowAssets, ) = morpho
            .expectedMarketBalances(marketParams);
        return
            totalSupplyAssets > totalBorrowAssets
                ? totalSupplyAssets - totalBorrowAssets
                : 0;
    }

    function getNetBorrowApr(
        uint256 /* newAmount */
    ) public view virtual override returns (uint256) {
        return 1;
    }

    function getNetRewardApr(
        uint256 /* newAmount */
    ) public view virtual override returns (uint256) {
        // Hardcoded high reward APR to keep borrowing favored over costs.
        return 1e20;
    }

    function getLiquidateCollateralFactor()
        public
        view
        virtual
        override
        returns (uint256)
    {
        return marketParams.lltv;
    }

    function balanceOfCollateral()
        public
        view
        virtual
        override
        returns (uint256)
    {
        Position memory p = morpho.position(marketId, address(this));
        return p.collateral;
    }

    function balanceOfDebt() public view virtual override returns (uint256) {
        return morpho.expectedBorrowAssets(marketParams, address(this));
    }

    /*//////////////////////////////////////////////////////////////
                        HARVEST / SWAP LOGIC
    //////////////////////////////////////////////////////////////*/

    function _claimRewards() internal virtual override {}

    function _claimAndSellRewards() internal virtual override {
        uint256 have = balanceOfLentAssets() + balanceOfBorrowToken();
        uint256 owe = balanceOfDebt();

        if (have > owe) {
            uint256 amountToSell = have - owe;
            _withdrawFromLender(amountToSell);
            _sellBorrowToken(Math.min(amountToSell, balanceOfBorrowToken()));
        }
    }

    function _buyBorrowToken() internal virtual override {
        uint256 _amount = borrowTokenOwedBalance();

        uint256 maxAssetIn = (_fromUsd(
            _toUsd(_amount, borrowToken),
            address(asset)
        ) * (MAX_BPS + slippage)) / MAX_BPS;
        if (maxAssetIn == 0) return;

        _swapTo(address(asset), borrowToken, _amount, maxAssetIn);
    }

    function _sellBorrowToken(uint256 _amount) internal virtual override {
        if (_amount == 0) return;

        _swapFrom(
            borrowToken,
            address(asset),
            _amount,
            _getAmountOut(_amount, borrowToken, address(asset))
        );
    }

    /*//////////////////////////////////////////////////////////////
                        MANAGEMENT UTILITIES
    //////////////////////////////////////////////////////////////*/

    function setUniFees(
        address _token0,
        address _token1,
        uint24 _fee
    ) external onlyManagement {
        _setUniFees(_token0, _token1, _fee);
    }

    function setUniBase(address _base) external onlyManagement {
        base = _base;
    }

    function setMinAmountToSell(
        uint256 _minAmountToSell
    ) external onlyManagement {
        _setMinAmountToSell(_minAmountToSell);
    }

    function setBorrowUsdOracle(
        address _borrowUsdOracle
    ) external onlyManagement {
        require(IChainlinkAggregator(_borrowUsdOracle).decimals() == 8);
        borrowUsdOracle = _borrowUsdOracle;
    }

    /// @notice Sweep of non-asset ERC20 tokens to governance
    /// @param _token The ERC20 token to sweep
    function sweep(address _token) external {
        require(msg.sender == GOV, "!gov");
        require(
            _token != address(asset) &&
                _token != address(borrowToken) &&
                _token != address(lenderVault),
            "!sweep"
        );
        ERC20(_token).safeTransfer(GOV, ERC20(_token).balanceOf(address(this)));
    }

    function _readUsdOracle() internal view returns (uint256) {
        int256 answer = IChainlinkAggregator(borrowUsdOracle).latestAnswer();
        require(answer > 0, "0");
        return uint256(answer);
    }
}

// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.18;

import {IStrategy} from "@tokenized-strategy/interfaces/IStrategy.sol";
import {ILenderBorrower} from "./ILenderBorrower.sol";
import {IMorpho} from "./morpho/IMorpho.sol";
import {Id} from "./morpho/IMorpho.sol";

interface IStrategyInterface is IStrategy, ILenderBorrower {
    //TODO: Add your specific implementation interface in here.

    function GOV() external view returns (address);

    function sweep(address _token) external;

    // Morpho-specific views
    function morpho() external view returns (IMorpho);

    function marketId() external view returns (Id);

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

    function lenderVault() external view returns (address);

    function base() external view returns (address);

    function router() external view returns (address);

    // Morpho-specific management setters
    function setRewardAprOracle(address _oracle) external;

    function setUsdOracles(
        address _assetUsdOracle,
        address _borrowUsdOracle
    ) external;

    function setUniFees(address _token0, address _token1, uint24 _fee) external;

    function setUniBase(address _base) external;

    function setMinAmountToSell(uint256 _minAmountToSell) external;

    function setAuction(address _auction) external;

    function setUseAuction(bool _useAuction) external;
}

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

type Id is bytes32;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /// @notice Creates the market `marketParams`.
    /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
    /// Morpho behaves as expected:
    /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
    /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
    /// burn functions are not supported.
    /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
    /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
    /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
    /// - The IRM should not re-enter Morpho.
    /// - The oracle should return a price with the correct scaling.
    /// - The oracle price should not be able to change instantly such that the new price is less than the old price
    /// multiplied by LLTV*LIF. In particular, if the loan asset is a vault that can receive donations, the oracle
    /// should not price its shares using the AUM.
    /// @dev Here is a list of assumptions on the market's dependencies which, if broken, could break Morpho's liveness
    /// properties (funds could get stuck):
    /// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
    /// - The amount of assets supplied and borrowed should not be too high (max ~1e32), otherwise the number of shares
    /// might not fit within 128 bits.
    /// - The IRM should not revert on `borrowRate`.
    /// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
    /// `_accrueInterest` can overflow).
    /// - The oracle should not revert `price`.
    /// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
    /// `assetsRepaid` in `liquidate` can overflow).
    /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
    /// the point where `totalBorrowShares` is very large and borrowing overflows.
    function createMarket(MarketParams memory marketParams) external;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @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 == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 8 of 36 : BaseLenderBorrower.sol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";

import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BaseHealthCheck, ERC20} from "@periphery/Bases/HealthCheck/BaseHealthCheck.sol";

/**
 * @title Base Lender Borrower
 */
abstract contract BaseLenderBorrower is BaseHealthCheck {
    using SafeERC20 for ERC20;

    uint256 internal constant WAD = 1e18;

    /// The token we will be borrowing/supplying.
    address public immutable borrowToken;

    /// If set to true, the strategy will not try to repay debt by selling rewards or asset.
    bool public leaveDebtBehind;

    /// @notice Target Loan-To-Value (LTV) multiplier in Basis Points
    /// @dev Represents the ratio up to which we will borrow, relative to the liquidation threshold.
    /// LTV is the debt-to-collateral ratio. Default is set to 70% of the liquidation LTV.
    uint16 public targetLTVMultiplier;

    /// @notice Warning Loan-To-Value (LTV) multiplier in Basis Points
    /// @dev Represents the ratio at which we will start repaying the debt to avoid liquidation
    /// Default is set to 80% of the liquidation LTV
    uint16 public warningLTVMultiplier; // 80% of liquidation LTV

    /// @notice Slippage tolerance (in basis points) for swaps
    uint64 public slippage;

    /// @notice Deposit limit for the strategy.
    uint256 public depositLimit;

    /// The max the base fee (in gwei) will be for a tend
    uint256 public maxGasPriceToTend;

    /// Thresholds: lower limit on how much base token can be borrowed at a time.
    uint256 internal minAmountToBorrow;

    /// The lender vault that will be used to lend and borrow.
    IERC4626 public immutable lenderVault;

    /**
     * @param _asset The address of the asset we are lending/borrowing.
     * @param _name The name of the strategy.
     * @param _borrowToken The address of the borrow token.
     */
    constructor(
        address _asset,
        string memory _name,
        address _borrowToken,
        address _lenderVault
    ) BaseHealthCheck(_asset, _name) {
        borrowToken = _borrowToken;

        // Set default variables
        depositLimit = type(uint256).max;
        targetLTVMultiplier = 7_000;
        warningLTVMultiplier = 8_000;
        leaveDebtBehind = false;
        maxGasPriceToTend = 200 * 1e9;
        slippage = 500;

        // Allow for address(0) for versions that don't use 4626 vault.
        if (_lenderVault != address(0)) {
            lenderVault = IERC4626(_lenderVault);
            require(lenderVault.asset() == _borrowToken, "!lenderVault");
            ERC20(_borrowToken).forceApprove(_lenderVault, type(uint256).max);
        }
    }

    /// ----------------- SETTERS -----------------

    /**
     * @notice Set the deposit limit for the strategy
     * @param _depositLimit New deposit limit
     */
    function setDepositLimit(uint256 _depositLimit) external onlyManagement {
        depositLimit = _depositLimit;
    }

    /**
     * @notice Set the target and warning LTV multipliers
     * @param _targetLTVMultiplier New target LTV multiplier
     * @param _warningLTVMultiplier New warning LTV multiplier
     * @dev Target must be less than warning, warning must be <= 9000, target cannot be 0
     */
    function setLtvMultipliers(
        uint16 _targetLTVMultiplier,
        uint16 _warningLTVMultiplier
    ) external onlyManagement {
        require(
            _warningLTVMultiplier <= 9_000 &&
                _targetLTVMultiplier < _warningLTVMultiplier &&
                _targetLTVMultiplier != 0,
            "invalid LTV"
        );
        targetLTVMultiplier = _targetLTVMultiplier;
        warningLTVMultiplier = _warningLTVMultiplier;
    }

    /**
     * @notice Set whether to leave debt behind
     * @param _leaveDebtBehind New leave debt behind setting
     */
    function setLeaveDebtBehind(bool _leaveDebtBehind) external onlyManagement {
        leaveDebtBehind = _leaveDebtBehind;
    }

    /**
     * @notice Set the maximum gas price for tending
     * @param _maxGasPriceToTend New maximum gas price
     */
    function setMaxGasPriceToTend(
        uint256 _maxGasPriceToTend
    ) external onlyManagement {
        maxGasPriceToTend = _maxGasPriceToTend;
    }

    /**
     * @notice Set the slippage tolerance
     * @param _slippage New slippage tolerance in basis points
     */
    function setSlippage(uint256 _slippage) external onlyManagement {
        require(_slippage < MAX_BPS, "slippage");
        slippage = uint64(_slippage);
    }

    /*//////////////////////////////////////////////////////////////
                NEEDED TO BE OVERRIDDEN BY STRATEGIST
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Should deploy up to '_amount' of 'asset' in the yield source.
     *
     * This function is called at the end of a {deposit} or {mint}
     * call. Meaning that unless a whitelist is implemented it will
     * be entirely permissionless and thus can be sandwiched or otherwise
     * manipulated.
     *
     * @param _amount The amount of 'asset' that the strategy should attempt
     * to deposit in the yield source.
     */
    function _deployFunds(uint256 _amount) internal virtual override {
        _leveragePosition(_amount);
    }

    /**
     * @dev Will attempt to free the '_amount' of 'asset'.
     *
     * The amount of 'asset' that is already loose has already
     * been accounted for.
     *
     * This function is called during {withdraw} and {redeem} calls.
     * Meaning that unless a whitelist is implemented it will be
     * entirely permissionless and thus can be sandwiched or otherwise
     * manipulated.
     *
     * Should not rely on asset.balanceOf(address(this)) calls other than
     * for diff accounting purposes.
     *
     * Any difference between `_amount` and what is actually freed will be
     * counted as a loss and passed on to the withdrawer. This means
     * care should be taken in times of illiquidity. It may be better to revert
     * if withdraws are simply illiquid so not to realize incorrect losses.
     *
     * @param _amount, The amount of 'asset' to be freed.
     */
    function _freeFunds(uint256 _amount) internal virtual override {
        _liquidatePosition(_amount);
    }

    /**
     * @dev Internal function to harvest all rewards, redeploy any idle
     * funds and return an accurate accounting of all funds currently
     * held by the Strategy.
     *
     * This should do any needed harvesting, rewards selling, accrual,
     * redepositing etc. to get the most accurate view of current assets.
     *
     * NOTE: All applicable assets including loose assets should be
     * accounted for in this function.
     *
     * Care should be taken when relying on oracles or swap values rather
     * than actual amounts as all Strategy profit/loss accounting will
     * be done based on this returned value.
     *
     * This can still be called post a shutdown, a strategist can check
     * `TokenizedStrategy.isShutdown()` to decide if funds should be
     * redeployed or simply realize any profits/losses.
     *
     * @return _totalAssets A trusted and accurate account for the total
     * amount of 'asset' the strategy currently holds including idle funds.
     */
    function _harvestAndReport()
        internal
        virtual
        override
        returns (uint256 _totalAssets)
    {
        /// 1. claim rewards, 2. even borrowToken deposits and borrows 3. sell remainder of rewards to asset.
        _claimAndSellRewards();

        /// Leverage all the asset we have or up to the supply cap.
        /// We want check our leverage even if balance of asset is 0.
        _leveragePosition(
            Math.min(balanceOfAsset(), availableDepositLimit(address(this)))
        );

        /// Base token owed should be 0 here but we count it just in case
        _totalAssets =
            balanceOfAsset() +
            balanceOfCollateral() -
            _borrowTokenOwedInAsset();
    }

    /*//////////////////////////////////////////////////////////////
                    OPTIONAL TO OVERRIDE BY STRATEGIST
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Optional function for strategist to override that can
     *  be called in between reports.
     *
     * If '_tend' is used tendTrigger() will also need to be overridden.
     *
     * This call can only be called by a permissioned role so may be
     * through protected relays.
     *
     * This can be used to harvest and compound rewards, deposit idle funds,
     * perform needed position maintenance or anything else that doesn't need
     * a full report for.
     *
     *   EX: A strategy that can not deposit funds without getting
     *       sandwiched can use the tend when a certain threshold
     *       of idle to totalAssets has been reached.
     *
     * The TokenizedStrategy contract will do all needed debt and idle updates
     * after this has finished and will have no effect on PPS of the strategy
     * till report() is called.
     *
     * @param _totalIdle The current amount of idle funds that are available to deploy.
     */
    function _tend(uint256 _totalIdle) internal virtual override {
        /// If the cost to borrow > rewards rate we will pull out all funds to not report a loss
        if (getNetBorrowApr(0) > getNetRewardApr(0)) {
            /// Liquidate everything so not to report a loss
            _liquidatePosition(balanceOfCollateral());
            /// Return since we don't asset to do anything else
            return;
        }

        /// Else we need to either adjust LTV up or down.
        _leveragePosition(
            Math.min(_totalIdle, availableDepositLimit(address(this)))
        );
    }

    /**
     * @dev Optional trigger to override if tend() will be used by the strategy.
     * This must be implemented if the strategy hopes to invoke _tend().
     *
     * @return . Should return true if tend() should be called by keeper or false if not.
     */
    function _tendTrigger() internal view virtual override returns (bool) {
        /// If we are in danger of being liquidated tend no matter what
        if (_isLiquidatable()) return true;

        if (TokenizedStrategy.totalAssets() == 0) return false;

        /// We adjust position if:
        /// 1. LTV ratios are not in the HEALTHY range (either we take on more debt or repay debt)
        /// 2. costs are acceptable
        uint256 collateralInUsd = _toUsd(balanceOfCollateral(), address(asset));
        uint256 debtInUsd = _toUsd(balanceOfDebt(), borrowToken);
        uint256 currentLTV = collateralInUsd > 0
            ? (debtInUsd * WAD) / collateralInUsd
            : 0;

        /// Check if we are over our warning LTV
        if (currentLTV > _getWarningLTV()) {
            return true;
        }

        if (_isSupplyPaused() || _isBorrowPaused()) return false;

        uint256 targetLTV = _getTargetLTV();

        /// If we are still levered and Borrowing costs are too high.
        if (currentLTV != 0 && getNetBorrowApr(0) > getNetRewardApr(0)) {
            /// Tend if base fee is acceptable.
            return _isBaseFeeAcceptable();

            /// IF we are lower than our target. (we need a 10% (1000bps) difference)
        } else if ((currentLTV < targetLTV && targetLTV - currentLTV > 1e17)) {
            /// Make sure the increase in debt would keep borrowing costs healthy.
            uint256 targetDebtUsd = (collateralInUsd * targetLTV) / WAD;

            uint256 amountToBorrowUsd;
            unchecked {
                amountToBorrowUsd = targetDebtUsd - debtInUsd; // safe bc we checked ratios
            }

            /// Convert to borrowToken
            uint256 amountToBorrowBT = Math.min(
                _fromUsd(amountToBorrowUsd, borrowToken),
                Math.min(_lenderMaxDeposit(), _maxBorrowAmount())
            );

            if (amountToBorrowBT == 0) return false;

            /// We want to make sure that the reward apr > borrow apr so we don't report a loss
            /// Borrowing will cause the borrow apr to go up and the rewards apr to go down
            if (
                getNetBorrowApr(amountToBorrowBT) <
                getNetRewardApr(amountToBorrowBT)
            ) {
                /// Borrowing costs are healthy and WE NEED TO TAKE ON MORE DEBT
                return _isBaseFeeAcceptable();
            }
        }

        return false;
    }

    /**
     * @notice Gets the max amount of `asset` that an address can deposit.
     * @dev Defaults to an unlimited amount for any address. But can
     * be overridden by strategists.
     *
     * This function will be called before any deposit or mints to enforce
     * any limits desired by the strategist. This can be used for either a
     * traditional deposit limit or for implementing a whitelist etc.
     *
     *   EX:
     *      if(isAllowed[_owner]) return super.availableDepositLimit(_owner);
     *
     * This does not need to take into account any conversion rates
     * from shares to assets. But should know that any non max uint256
     * amounts may be converted to shares. So it is recommended to keep
     * custom amounts low enough as not to cause overflow when multiplied
     * by `totalSupply`.
     *
     * @param . The address that is depositing into the strategy.
     * @return . The available amount the `_owner` can deposit in terms of `asset`
     */
    function availableDepositLimit(
        address /*_owner*/
    ) public view virtual override returns (uint256) {
        /// We need to be able to both supply and withdraw on deposits.
        if (_isSupplyPaused() || _isBorrowPaused()) return 0;

        uint256 currentAssets = TokenizedStrategy.totalAssets();
        uint256 limit = depositLimit > currentAssets
            ? depositLimit - currentAssets
            : 0;

        uint256 maxDeposit = Math.min(_maxCollateralDeposit(), limit);
        uint256 maxBorrow = Math.min(_lenderMaxDeposit(), _maxBorrowAmount());

        // Either the max supply or the max we could borrow / targetLTV.
        return
            Math.min(
                maxDeposit,
                _fromUsd(
                    (_toUsd(maxBorrow, borrowToken) * WAD) / _getTargetLTV(),
                    address(asset)
                )
            );
    }

    /**
     * @notice Gets the max amount of `asset` that can be withdrawn.
     * @dev Defaults to an unlimited amount for any address. But can
     * be overridden by strategists.
     *
     * This function will be called before any withdraw or redeem to enforce
     * any limits desired by the strategist. This can be used for illiquid
     * or sandwichable strategies. It should never be lower than `totalIdle`.
     *
     *   EX:
     *       return TokenIzedStrategy.totalIdle();
     *
     * This does not need to take into account the `_owner`'s share balance
     * or conversion rates from shares to assets.
     *
     * @param . The address that is withdrawing from the strategy.
     * @return . The available amount that can be withdrawn in terms of `asset`
     */
    function availableWithdrawLimit(
        address /*_owner*/
    ) public view virtual override returns (uint256) {
        /// Default liquidity is the balance of collateral + 1 for rounding.
        uint256 liquidity = balanceOfCollateral() + 1;
        uint256 lenderLiquidity = _lenderMaxWithdraw();

        /// If we can't withdraw or supply, set liquidity = 0.
        if (lenderLiquidity < balanceOfLentAssets()) {
            /// Adjust liquidity based on withdrawing the full amount of debt.
            unchecked {
                liquidity = ((_fromUsd(
                    _toUsd(lenderLiquidity, borrowToken),
                    address(asset)
                ) * WAD) / _getTargetLTV());
            }
        }

        return balanceOfAsset() + liquidity;
    }

    /// ----------------- INTERNAL FUNCTIONS SUPPORT ----------------- \\

    /**
     * @notice Adjusts the leverage position of the strategy based on current and target Loan-to-Value (LTV) ratios.
     * @dev All debt and collateral calculations are done in USD terms. LTV values are represented in 1e18 format.
     * @param _amount The amount to be supplied to adjust the leverage position,
     */
    function _leveragePosition(uint256 _amount) internal virtual {
        /// Supply the given amount to the strategy.
        // This function internally checks for zero amounts.
        _supplyCollateral(_amount);

        uint256 collateralInUsd = _toUsd(balanceOfCollateral(), address(asset));

        /// Convert debt to USD
        uint256 debtInUsd = _toUsd(balanceOfDebt(), borrowToken);

        /// LTV numbers are always in WAD
        uint256 currentLTV = collateralInUsd > 0
            ? (debtInUsd * WAD) / collateralInUsd
            : 0;
        uint256 targetLTV = _getTargetLTV(); // 70% under default liquidation Threshold

        /// decide in which range we are and act accordingly:
        /// SUBOPTIMAL(borrow) (e.g. from 0 to 70% liqLTV)
        /// HEALTHY(do nothing) (e.g. from 70% to 80% liqLTV)
        /// UNHEALTHY(repay) (e.g. from 80% to 100% liqLTV)
        if (targetLTV > currentLTV) {
            /// SUBOPTIMAL RATIO: our current Loan-to-Value is lower than what we want

            /// we need to take on more debt
            uint256 targetDebtUsd = (collateralInUsd * targetLTV) / WAD;

            uint256 amountToBorrowUsd;
            unchecked {
                amountToBorrowUsd = targetDebtUsd - debtInUsd; // safe bc we checked ratios
            }

            /// convert to borrowToken
            uint256 amountToBorrowBT = Math.min(
                _fromUsd(amountToBorrowUsd, borrowToken),
                Math.min(_lenderMaxDeposit(), _maxBorrowAmount())
            );

            /// We want to make sure that the reward apr > borrow apr so we don't report a loss
            /// Borrowing will cause the borrow apr to go up and the rewards apr to go down
            if (
                getNetBorrowApr(amountToBorrowBT) >
                getNetRewardApr(amountToBorrowBT)
            ) {
                /// If we would push it over the limit don't borrow anything
                amountToBorrowBT = 0;
            }

            /// Need to have at least the min threshold
            if (amountToBorrowBT > minAmountToBorrow) {
                _borrow(amountToBorrowBT);
            }
        } else if (currentLTV > _getWarningLTV()) {
            /// UNHEALTHY RATIO
            /// we repay debt to set it to targetLTV
            uint256 targetDebtUsd = (targetLTV * collateralInUsd) / WAD;

            /// Withdraw the difference from the Depositor
            _withdrawFromLender(
                _fromUsd(debtInUsd - targetDebtUsd, borrowToken)
            );

            /// Repay the borrowToken debt.
            _repayTokenDebt();
        }

        // Deposit any loose base token that was borrowed.
        uint256 borrowTokenBalance = balanceOfBorrowToken();
        if (borrowTokenBalance > 0) {
            _lendBorrowToken(borrowTokenBalance);
        }
    }

    /**
     * @notice Liquidates the position to ensure the needed amount while maintaining healthy ratios.
     * @dev All debt, collateral, and needed amounts are calculated in USD. The needed amount is represented in the asset.
     * @param _needed The amount required in the asset.
     */
    function _liquidatePosition(uint256 _needed) internal virtual {
        /// Cache balance for withdraw checks
        uint256 balance = balanceOfAsset();

        /// We first repay whatever we need to repay to keep healthy ratios
        _withdrawFromLender(_calculateAmountToRepay(_needed));

        /// we repay the borrowToken debt with the amount withdrawn from the vault
        _repayTokenDebt();

        // Withdraw as much as we can up to the amount needed while maintaining a health ltv
        _withdrawCollateral(Math.min(_needed, _maxWithdrawal()));

        /// We check if we withdrew less than expected, and we do have not more borrowToken
        /// left AND should harvest or buy borrowToken with asset (potentially realising losses)
        if (
            /// if we didn't get enough
            _needed > balanceOfAsset() - balance &&
            /// still some debt remaining
            balanceOfDebt() > 0 &&
            /// but no capital to repay
            balanceOfLentAssets() == 0 &&
            /// And the leave debt flag is false.
            !leaveDebtBehind
        ) {
            /// using this part of code may result in losses but it is necessary to unlock full collateral
            /// in case of wind down. This should only occur when depleting the strategy so we buy the full
            /// amount of our remaining debt. We buy borrowToken first with available rewards then with asset.
            _buyBorrowToken();

            /// we repay debt to actually unlock collateral
            /// after this, balanceOfDebt should be 0
            _repayTokenDebt();

            /// then we try withdraw once more
            /// still withdraw with target LTV since management can potentially save any left over manually
            _withdrawCollateral(_maxWithdrawal());
        }
    }

    /**
     * @notice Calculates max amount that can be withdrawn while maintaining healthy LTV ratio
     * @dev Considers current collateral and debt amounts
     * @return The max amount of collateral available for withdrawal
     */
    function _maxWithdrawal() internal view virtual returns (uint256) {
        uint256 collateral = balanceOfCollateral();
        uint256 debt = balanceOfDebt();

        /// If there is no debt we can withdraw everything
        if (debt == 0) return collateral;

        uint256 debtInUsd = _toUsd(debt, borrowToken);

        /// What we need to maintain a health LTV
        uint256 neededCollateral = _fromUsd(
            (debtInUsd * WAD) / _getTargetLTV(),
            address(asset)
        );

        /// We need more collateral so we cant withdraw anything
        if (neededCollateral > collateral) {
            return 0;
        }

        /// Return the difference in terms of asset
        unchecked {
            return collateral - neededCollateral;
        }
    }

    /**
     * @notice Calculates amount of debt to repay to maintain healthy LTV ratio
     * @dev Considers target LTV, amount being withdrawn, and current collateral/debt
     * @param amount The withdrawal amount
     * @return The amount of debt to repay
     */
    function _calculateAmountToRepay(
        uint256 amount
    ) internal view virtual returns (uint256) {
        if (amount == 0) return 0;
        uint256 collateral = balanceOfCollateral();
        /// To unlock all collateral we must repay all the debt
        if (amount >= collateral) return balanceOfDebt();

        /// We check if the collateral that we are withdrawing leaves us in a risky range, we then take action
        uint256 newCollateralUsd = _toUsd(collateral - amount, address(asset));

        uint256 targetDebtUsd = (newCollateralUsd * _getTargetLTV()) / WAD;
        uint256 targetDebt = _fromUsd(targetDebtUsd, borrowToken);
        uint256 currentDebt = balanceOfDebt();
        /// Repay only if our target debt is lower than our current debt
        return targetDebt < currentDebt ? currentDebt - targetDebt : 0;
    }

    /**
     * @notice Repays outstanding debt with available base tokens
     * @dev Repays debt by supplying base tokens up to the min of available balance and debt amount
     */
    function _repayTokenDebt() internal virtual {
        /// We cannot pay more than loose balance or more than we owe
        _repay(Math.min(balanceOfBorrowToken(), balanceOfDebt()));
    }

    /**
     * @notice Withdraws a specified amount of `borrowToken` from the lender.
     * @param amount The amount of the borrowToken to withdraw.
     */
    function _withdrawFromLender(uint256 amount) internal virtual {
        uint256 balancePrior = balanceOfBorrowToken();
        /// Only withdraw what we don't already have free
        amount = balancePrior >= amount ? 0 : amount - balancePrior;

        /// Make sure we have enough balance.
        amount = Math.min(amount, _lenderMaxWithdraw());

        if (amount == 0) return;

        _withdrawBorrowToken(amount);
    }

    // ----------------- INTERNAL WRITE FUNCTIONS ----------------- \\

    /**
     * @notice Supplies a specified amount of `asset` as collateral.
     * @param amount The amount of the asset to supply.
     */
    function _supplyCollateral(uint256 amount) internal virtual;

    /**
     * @notice Withdraws a specified amount of collateral.
     * @param amount The amount of the collateral to withdraw.
     */
    function _withdrawCollateral(uint256 amount) internal virtual;

    /**
     * @notice Borrows a specified amount of `borrowToken`.
     * @param amount The amount of the borrowToken to borrow.
     */
    function _borrow(uint256 amount) internal virtual;

    /**
     * @notice Repays a specified amount of `borrowToken`.
     * @param amount The amount of the borrowToken to repay.
     */
    function _repay(uint256 amount) internal virtual;

    /**
     * @notice Lends a specified amount of `borrowToken`.
     * @param amount The amount of the borrowToken to lend.
     */
    function _lendBorrowToken(uint256 amount) internal virtual {
        lenderVault.deposit(amount, address(this));
    }

    /**
     * @notice Withdraws a specified amount of `borrowToken`.
     * @param amount The amount of the borrowToken to withdraw.
     */
    function _withdrawBorrowToken(uint256 amount) internal virtual {
        // Use previewWithdraw to round up.
        uint256 shares = Math.min(
            lenderVault.previewWithdraw(amount),
            lenderVault.balanceOf(address(this))
        );
        lenderVault.redeem(shares, address(this), address(this));
    }

    // ----------------- INTERNAL VIEW FUNCTIONS ----------------- \\

    /**
     * @notice Gets asset price returned 1e8
     * @param _asset The asset address
     * @return price asset price
     */
    function _getPrice(
        address _asset
    ) internal view virtual returns (uint256 price);

    /**
     * @notice Checks if lending or borrowing is paused
     * @return True if paused, false otherwise
     */
    function _isSupplyPaused() internal view virtual returns (bool);

    /**
     * @notice Checks if borrowing is paused
     * @return True if paused, false otherwise
     */
    function _isBorrowPaused() internal view virtual returns (bool);

    /**
     * @notice Checks if the strategy is liquidatable
     * @return True if liquidatable, false otherwise
     */
    function _isLiquidatable() internal view virtual returns (bool);

    /**
     * @notice Gets the supply cap for the collateral asset if any
     * @return The supply cap
     */
    function _maxCollateralDeposit() internal view virtual returns (uint256);

    /**
     * @notice Gets the max amount of `borrowToken` that could be borrowed
     * @return The max borrow amount
     */
    function _maxBorrowAmount() internal view virtual returns (uint256);

    /**
     * @notice Gets the max amount of `borrowToken` that could be deposited to the lender
     * @return The max deposit amount
     */
    function _lenderMaxDeposit() internal view virtual returns (uint256) {
        return lenderVault.maxDeposit(address(this));
    }

    /**
     * @notice Gets the amount of borrowToken that could be withdrawn from the lender
     * @return The lender liquidity
     */
    function _lenderMaxWithdraw() internal view virtual returns (uint256) {
        return
            lenderVault.convertToAssets(lenderVault.maxRedeem(address(this)));
    }

    /**
     * @notice Gets net borrow APR from depositor
     * @param newAmount Simulated supply amount
     * @return Net borrow APR
     */
    function getNetBorrowApr(
        uint256 newAmount
    ) public view virtual returns (uint256);

    /**
     * @notice Gets net reward APR from depositor
     * @param newAmount Simulated supply amount
     * @return Net reward APR
     */
    function getNetRewardApr(
        uint256 newAmount
    ) public view virtual returns (uint256);

    /**
     * @notice Gets liquidation collateral factor for asset
     * @return Liquidation collateral factor
     */
    function getLiquidateCollateralFactor()
        public
        view
        virtual
        returns (uint256);

    /**
     * @notice Gets supplied collateral balance
     * @return Collateral balance
     */
    function balanceOfCollateral() public view virtual returns (uint256);

    /**
     * @notice Gets current borrow balance
     * @return Borrow balance
     */
    function balanceOfDebt() public view virtual returns (uint256);

    /**
     * @notice Gets full depositor balance
     * @return Depositor balance
     */
    function balanceOfLentAssets() public view virtual returns (uint256) {
        return
            lenderVault.convertToAssets(lenderVault.balanceOf(address(this)));
    }

    /**
     * @notice Gets available balance of asset token
     * @return The asset token balance
     */
    function balanceOfAsset() public view virtual returns (uint256) {
        return asset.balanceOf(address(this));
    }

    /**
     * @notice Gets available base token balance
     * @return Base token balance
     */
    function balanceOfBorrowToken() public view virtual returns (uint256) {
        return ERC20(borrowToken).balanceOf(address(this));
    }

    /**
     * @notice Gets net owed base tokens (borrowed - supplied)
     * @return Net base tokens owed
     */
    function borrowTokenOwedBalance() public view virtual returns (uint256) {
        uint256 have = balanceOfLentAssets() + balanceOfBorrowToken();
        uint256 owe = balanceOfDebt();

        /// If they are the same or supply > debt return 0
        if (have >= owe) return 0;

        unchecked {
            return owe - have;
        }
    }

    /**
     * @notice Gets base tokens owed in asset terms
     * @return owed tokens owed in asset value
     */
    function _borrowTokenOwedInAsset()
        internal
        view
        virtual
        returns (uint256 owed)
    {
        /// Don't do conversions unless it's a non-zero false.
        uint256 owedInBase = borrowTokenOwedBalance();
        if (owedInBase != 0) {
            owed = _fromUsd(_toUsd(owedInBase, borrowToken), address(asset));
        }
    }

    /**
     * @notice Calculates current loan-to-value ratio
     * @dev Converts collateral and debt values to USD
     * @return Current LTV in 1e18 format
     */
    function getCurrentLTV() external view virtual returns (uint256) {
        uint256 collateral = balanceOfCollateral();

        if (collateral == 0) return 0;

        unchecked {
            return
                (_toUsd(balanceOfDebt(), borrowToken) * WAD) /
                _toUsd(collateral, address(asset));
        }
    }

    /**
     * @notice Gets target loan-to-value ratio
     * @dev Calculates based on liquidation threshold and multiplier
     * @return Target LTV in 1e18 format
     */
    function _getTargetLTV() internal view virtual returns (uint256) {
        unchecked {
            return
                (getLiquidateCollateralFactor() * targetLTVMultiplier) /
                MAX_BPS;
        }
    }

    /**
     * @notice Gets warning loan-to-value ratio
     * @dev Calculates based on liquidation threshold and multiplier
     * @return Warning LTV in 1e18 format
     */
    function _getWarningLTV() internal view virtual returns (uint256) {
        unchecked {
            return
                (getLiquidateCollateralFactor() * warningLTVMultiplier) /
                MAX_BPS;
        }
    }

    /**
     * @notice Converts a token amount to USD value
     * @dev This assumes _getPrice returns constants 1e8 price
     * @param _amount The token amount
     * @param _token The token address
     * @return The USD value scaled by 1e8
     */
    function _toUsd(
        uint256 _amount,
        address _token
    ) internal view virtual returns (uint256) {
        if (_amount == 0) return 0;
        unchecked {
            return
                (_amount * _getPrice(_token)) /
                (10 ** ERC20(_token).decimals());
        }
    }

    /**
     * @notice Converts a USD amount to token value
     * @dev This assumes _getPrice returns constants 1e8 price
     * @param _amount The USD amount (scaled by 1e8)
     * @param _token The token address
     * @return The token amount
     */
    function _fromUsd(
        uint256 _amount,
        address _token
    ) internal view virtual returns (uint256) {
        if (_amount == 0) return 0;
        unchecked {
            return
                (_amount * (10 ** ERC20(_token).decimals())) /
                _getPrice(_token);
        }
    }

    /// ----------------- HARVEST / TOKEN CONVERSIONS -----------------

    /**
     * @notice Claims reward tokens.
     */
    function _claimRewards() internal virtual;

    /**
     * @notice Claims and sells available reward tokens
     * @dev Handles claiming, selling rewards for borrow tokens if needed, and selling remaining rewards for asset
     */
    function _claimAndSellRewards() internal virtual;

    /**
     * @dev Buys the borrow token using the strategy's assets.
     * This function should only ever be called when withdrawing all funds from the strategy if there is debt left over.
     * Initially, it tries to sell rewards for the needed amount of base token, then it will swap assets.
     * Using this function in a standard withdrawal can cause it to be sandwiched, which is why rewards are used first.
     */
    function _buyBorrowToken() internal virtual;

    /**
     * @dev Will swap from the base token => underlying asset.
     */
    function _sellBorrowToken(uint256 _amount) internal virtual;

    /**
     * @notice Estimates swap output accounting for slippage
     * @param _amount Input amount
     * @param _from Input token
     * @param _to Output token
     * @return Estimated output amount
     */
    function _getAmountOut(
        uint256 _amount,
        address _from,
        address _to
    ) internal view virtual returns (uint256) {
        if (_amount == 0) return 0;

        return
            (_fromUsd(_toUsd(_amount, _from), _to) * (MAX_BPS - slippage)) /
            MAX_BPS;
    }

    /**
     * @notice Checks if base fee is acceptable
     * @return True if base fee is below threshold
     */
    function _isBaseFeeAcceptable() internal view virtual returns (bool) {
        return block.basefee <= maxGasPriceToTend;
    }

    /**
     * @dev Optional function for a strategist to override that will
     * allow management to manually withdraw deployed funds from the
     * yield source if a strategy is shutdown.
     *
     * This should attempt to free `_amount`, noting that `_amount` may
     * be more than is currently deployed.
     *
     * NOTE: This will not realize any profits or losses. A separate
     * {report} will be needed in order to record any profit/loss. If
     * a report may need to be called after a shutdown it is important
     * to check if the strategy is shutdown during {_harvestAndReport}
     * so that it does not simply re-deploy all funds that had been freed.
     *
     * EX:
     *   if(freeAsset > 0 && !TokenizedStrategy.isShutdown()) {
     *       depositFunds...
     *    }
     *
     * @param _amount The amount of asset to attempt to free.
     */
    function _emergencyWithdraw(uint256 _amount) internal virtual override {
        _amount = Math.min(_amount, _lenderMaxWithdraw());
        if (_amount > 0) _withdrawBorrowToken(_amount);

        // Repay everything we can.
        _repayTokenDebt();

        // Withdraw all that makes sense.
        _withdrawCollateral(_maxWithdrawal());
    }

    // Manually Sell rewards
    function claimAndSellRewards() external virtual onlyEmergencyAuthorized {
        _claimAndSellRewards();
    }

    /// @notice Sell a specific amount of `borrowToken` -> asset.
    ///     The amount of borrowToken should be loose in the strategy before this is called
    ///     max uint input will sell any excess borrowToken we have.
    function sellBorrowToken(
        uint256 _amount
    ) external virtual onlyEmergencyAuthorized {
        if (_amount == type(uint256).max) {
            uint256 _balanceOfBorrowToken = balanceOfBorrowToken();
            uint256 have = balanceOfLentAssets() + _balanceOfBorrowToken;
            uint256 owe = balanceOfDebt();
            // Only sell excess if we have more than we owe
            _amount = have > owe
                ? Math.min(have - owe, _balanceOfBorrowToken)
                : 0;
        }
        _sellBorrowToken(_amount);
    }

    /// @notice Withdraw a specific amount of `_token`
    function manualWithdraw(
        address _token,
        uint256 _amount
    ) external virtual onlyEmergencyAuthorized {
        if (_token == borrowToken) {
            _withdrawBorrowToken(_amount);
        } else {
            _withdrawCollateral(_amount);
        }
    }

    // Manually repay debt with loose borrowToken already in the strategy.
    function manualRepayDebt() external virtual onlyEmergencyAuthorized {
        _repayTokenDebt();
    }
}

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

interface IChainlinkAggregator {
    function latestAnswer() external view returns (int256);

    function decimals() external view returns (uint8);
}

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

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

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

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

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

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

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

        uint256 elapsed = block.timestamp - market.lastUpdate;

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

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

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

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

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

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

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

        ) = expectedMarketBalances(morpho, marketParams);

        return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares);
    }

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {ISwapRouter} from "../interfaces/Uniswap/V3/ISwapRouter.sol";
import {BaseSwapper} from "./BaseSwapper.sol";

/**
 *   @title UniswapV3Swapper
 *   @author Yearn.finance
 *   @dev This is a simple contract that can be inherited by any tokenized
 *   strategy that would like to use Uniswap V3 for swaps. It hold all needed
 *   logic to perform both exact input and exact output swaps.
 *
 *   The global address variables default to the ETH mainnet addresses but
 *   remain settable by the inheriting contract to allow for customization
 *   based on needs or chain its used on.
 *
 *   The only variables that are required to be set are the specific fees
 *   for each token pair. The inheriting contract can use the {_setUniFees}
 *   function to easily set this for any token pairs needed.
 */
contract UniswapV3Swapper is BaseSwapper {
    using SafeERC20 for ERC20;
    // Defaults to WETH on mainnet.
    address public base = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    // Defaults to Uniswap V3 router on mainnet.
    address public router = 0xE592427A0AEce92De3Edee1F18E0157C05861564;

    // Fees for the Uni V3 pools. Each fee should get set each way in
    // the mapping so no matter the direction the correct fee will get
    // returned for any two tokens.
    mapping(address => mapping(address => uint24)) public uniFees;

    /**
     * @dev All fess will default to 0 on creation. A strategist will need
     * To set the mapping for the tokens expected to swap. This function
     * is to help set the mapping. It can be called internally during
     * initialization, through permissioned functions etc.
     */
    function _setUniFees(
        address _token0,
        address _token1,
        uint24 _fee
    ) internal virtual {
        uniFees[_token0][_token1] = _fee;
        uniFees[_token1][_token0] = _fee;
    }

    /**
     * @dev Used to swap a specific amount of `_from` to `_to`.
     * This will check and handle all allowances as well as not swapping
     * unless `_amountIn` is greater than the set `_minAmountOut`
     *
     * If one of the tokens matches with the `base` token it will do only
     * one jump, otherwise will do two jumps.
     *
     * The corresponding uniFees for each token pair will need to be set
     * other wise this function will revert.
     *
     * @param _from The token we are swapping from.
     * @param _to The token we are swapping to.
     * @param _amountIn The amount of `_from` we will swap.
     * @param _minAmountOut The min of `_to` to get out.
     * @return _amountOut The actual amount of `_to` that was swapped to
     */
    function _swapFrom(
        address _from,
        address _to,
        uint256 _amountIn,
        uint256 _minAmountOut
    ) internal virtual returns (uint256 _amountOut) {
        if (_amountIn != 0 && _amountIn >= minAmountToSell) {
            _checkAllowance(router, _from, _amountIn);
            if (_from == base || _to == base) {
                ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
                    .ExactInputSingleParams(
                        _from, // tokenIn
                        _to, // tokenOut
                        uniFees[_from][_to], // from-to fee
                        address(this), // recipient
                        block.timestamp, // deadline
                        _amountIn, // amountIn
                        _minAmountOut, // amountOut
                        0 // sqrtPriceLimitX96
                    );

                _amountOut = ISwapRouter(router).exactInputSingle(params);
            } else {
                bytes memory path = abi.encodePacked(
                    _from, // tokenIn
                    uniFees[_from][base], // from-base fee
                    base, // base token
                    uniFees[base][_to], // base-to fee
                    _to // tokenOut
                );

                _amountOut = ISwapRouter(router).exactInput(
                    ISwapRouter.ExactInputParams(
                        path,
                        address(this),
                        block.timestamp,
                        _amountIn,
                        _minAmountOut
                    )
                );
            }
        }
    }

    /**
     * @dev Used to swap a specific amount of `_to` from `_from` unless
     * it takes more than `_maxAmountFrom`.
     *
     * This will check and handle all allowances as well as not swapping
     * unless `_maxAmountFrom` is greater than the set `minAmountToSell`
     *
     * If one of the tokens matches with the `base` token it will do only
     * one jump, otherwise will do two jumps.
     *
     * The corresponding uniFees for each token pair will need to be set
     * other wise this function will revert.
     *
     * @param _from The token we are swapping from.
     * @param _to The token we are swapping to.
     * @param _amountTo The amount of `_to` we need out.
     * @param _maxAmountFrom The max of `_from` we will swap.
     * @return _amountIn The actual amount of `_from` swapped.
     */
    function _swapTo(
        address _from,
        address _to,
        uint256 _amountTo,
        uint256 _maxAmountFrom
    ) internal virtual returns (uint256 _amountIn) {
        if (_maxAmountFrom != 0 && _maxAmountFrom >= minAmountToSell) {
            _checkAllowance(router, _from, _maxAmountFrom);
            if (_from == base || _to == base) {
                ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter
                    .ExactOutputSingleParams(
                        _from, // tokenIn
                        _to, // tokenOut
                        uniFees[_from][_to], // from-to fee
                        address(this), // recipient
                        block.timestamp, // deadline
                        _amountTo, // amountOut
                        _maxAmountFrom, // maxAmountIn
                        0 // sqrtPriceLimitX96
                    );

                _amountIn = ISwapRouter(router).exactOutputSingle(params);
            } else {
                bytes memory path = abi.encodePacked(
                    _to,
                    uniFees[base][_to], // base-to fee
                    base,
                    uniFees[_from][base], // from-base fee
                    _from
                );

                _amountIn = ISwapRouter(router).exactOutput(
                    ISwapRouter.ExactOutputParams(
                        path,
                        address(this),
                        block.timestamp,
                        _amountTo, // How much we want out
                        _maxAmountFrom
                    )
                );
            }
        }
    }

    /**
     * @dev Internal safe function to make sure the contract you want to
     * interact with has enough allowance to pull the desired tokens.
     *
     * @param _contract The address of the contract that will move the token.
     * @param _token The ERC-20 token that will be getting spent.
     * @param _amount The amount of `_token` to be spent.
     */
    function _checkAllowance(
        address _contract,
        address _token,
        uint256 _amount
    ) internal virtual {
        if (ERC20(_token).allowance(address(this), _contract) < _amount) {
            ERC20(_token).forceApprove(_contract, 0);
            ERC20(_token).forceApprove(_contract, _amount);
        }
    }
}

File 14 of 36 : IStrategy.sol
// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;

import {ITokenizedStrategy} from "./ITokenizedStrategy.sol";
import {IBaseStrategy} from "./IBaseStrategy.sol";

interface IStrategy is IBaseStrategy, ITokenizedStrategy {}

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;

import {IBaseHealthCheck} from "@periphery/Bases/HealthCheck/IBaseHealthCheck.sol";

interface ILenderBorrower is IBaseHealthCheck {
    // Public Variables
    function borrowToken() external view returns (address);

    function leaveDebtBehind() external view returns (bool);

    function depositLimit() external view returns (uint256);

    function targetLTVMultiplier() external view returns (uint16);

    function warningLTVMultiplier() external view returns (uint16);

    function maxGasPriceToTend() external view returns (uint256);

    function slippage() external view returns (uint256);

    // External Functions
    function setDepositLimit(uint256 _depositLimit) external;

    function setLtvMultipliers(
        uint16 _targetLTVMultiplier,
        uint16 _warningLTVMultiplier
    ) external;

    function setLeaveDebtBehind(bool _leaveDebtBehind) external;

    function setMaxGasPriceToTend(uint256 _maxGasPriceToTend) external;

    function setSlippage(uint256 _slippage) external;

    // Public View Functions
    function getCurrentLTV() external view returns (uint256);

    function getNetBorrowApr(uint256 newAmount) external view returns (uint256);

    function getNetRewardApr(uint256 newAmount) external view returns (uint256);

    function getLiquidateCollateralFactor() external view returns (uint256);

    function balanceOfCollateral() external view returns (uint256);

    function balanceOfDebt() external view returns (uint256);

    function balanceOfLentAssets() external view returns (uint256);

    function balanceOfAsset() external view returns (uint256);

    function balanceOfBorrowToken() external view returns (uint256);

    function borrowTokenOwedBalance() external view returns (uint256);

    // Emergency Functions
    function claimAndSellRewards() external;

    function sellBorrowToken(uint256 _amount) external;

    function manualWithdraw(address _token, uint256 _amount) external;

    function manualRepayDebt() external;
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

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

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

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

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

pragma solidity ^0.8.0;

/**
 * @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 Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @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
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [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://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/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 functionCallWithValue(target, data, 0, "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");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, 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) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or 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 {
            _revert(returndata, errorMessage);
        }
    }

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

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

pragma solidity ^0.8.0;

import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;

import {BaseStrategy, ERC20} from "@tokenized-strategy/BaseStrategy.sol";

/**
 *   @title Base Health Check
 *   @author Yearn.finance
 *   @notice This contract can be inherited by any Yearn
 *   V3 strategy wishing to implement a health check during
 *   the `report` function in order to prevent any unexpected
 *   behavior from being permanently recorded as well as the
 *   `checkHealth` modifier.
 *
 *   A strategist simply needs to inherit this contract. Set
 *   the limit ratios to the desired amounts and then
 *   override `_harvestAndReport()` just as they otherwise
 *  would. If the profit or loss that would be recorded is
 *   outside the acceptable bounds the tx will revert.
 *
 *   The healthcheck does not prevent a strategy from reporting
 *   losses, but rather can make sure manual intervention is
 *   needed before reporting an unexpected loss or profit.
 */
abstract contract BaseHealthCheck is BaseStrategy {
    // Can be used to determine if a healthcheck should be called.
    // Defaults to true;
    bool public doHealthCheck = true;

    uint256 internal constant MAX_BPS = 10_000;

    // Default profit limit to 100%.
    uint16 private _profitLimitRatio = uint16(MAX_BPS);

    // Defaults loss limit to 0.
    uint16 private _lossLimitRatio;

    constructor(
        address _asset,
        string memory _name
    ) BaseStrategy(_asset, _name) {}

    /**
     * @notice Returns the current profit limit ratio.
     * @dev Use a getter function to keep the variable private.
     * @return . The current profit limit ratio.
     */
    function profitLimitRatio() public view returns (uint256) {
        return _profitLimitRatio;
    }

    /**
     * @notice Returns the current loss limit ratio.
     * @dev Use a getter function to keep the variable private.
     * @return . The current loss limit ratio.
     */
    function lossLimitRatio() public view returns (uint256) {
        return _lossLimitRatio;
    }

    /**
     * @notice Set the `profitLimitRatio`.
     * @dev Denominated in basis points. I.E. 1_000 == 10%.
     * @param _newProfitLimitRatio The mew profit limit ratio.
     */
    function setProfitLimitRatio(
        uint256 _newProfitLimitRatio
    ) external onlyManagement {
        _setProfitLimitRatio(_newProfitLimitRatio);
    }

    /**
     * @dev Internally set the profit limit ratio. Denominated
     * in basis points. I.E. 1_000 == 10%.
     * @param _newProfitLimitRatio The mew profit limit ratio.
     */
    function _setProfitLimitRatio(uint256 _newProfitLimitRatio) internal {
        require(_newProfitLimitRatio > 0, "!zero profit");
        require(_newProfitLimitRatio <= type(uint16).max, "!too high");
        _profitLimitRatio = uint16(_newProfitLimitRatio);
    }

    /**
     * @notice Set the `lossLimitRatio`.
     * @dev Denominated in basis points. I.E. 1_000 == 10%.
     * @param _newLossLimitRatio The new loss limit ratio.
     */
    function setLossLimitRatio(
        uint256 _newLossLimitRatio
    ) external onlyManagement {
        _setLossLimitRatio(_newLossLimitRatio);
    }

    /**
     * @dev Internally set the loss limit ratio. Denominated
     * in basis points. I.E. 1_000 == 10%.
     * @param _newLossLimitRatio The new loss limit ratio.
     */
    function _setLossLimitRatio(uint256 _newLossLimitRatio) internal {
        require(_newLossLimitRatio < MAX_BPS, "!loss limit");
        _lossLimitRatio = uint16(_newLossLimitRatio);
    }

    /**
     * @notice Turns the healthcheck on and off.
     * @dev If turned off the next report will auto turn it back on.
     * @param _doHealthCheck Bool if healthCheck should be done.
     */
    function setDoHealthCheck(bool _doHealthCheck) public onlyManagement {
        doHealthCheck = _doHealthCheck;
    }

    /**
     * @notice OVerrides the default {harvestAndReport} to include a healthcheck.
     * @return _totalAssets New totalAssets post report.
     */
    function harvestAndReport()
        external
        override
        onlySelf
        returns (uint256 _totalAssets)
    {
        // Let the strategy report.
        _totalAssets = _harvestAndReport();

        // Run the healthcheck on the amount returned.
        _executeHealthCheck(_totalAssets);
    }

    /**
     * @dev To be called during a report to make sure the profit
     * or loss being recorded is within the acceptable bound.
     *
     * @param _newTotalAssets The amount that will be reported.
     */
    function _executeHealthCheck(uint256 _newTotalAssets) internal virtual {
        if (!doHealthCheck) {
            doHealthCheck = true;
            return;
        }

        // Get the current total assets from the implementation.
        uint256 currentTotalAssets = TokenizedStrategy.totalAssets();

        if (_newTotalAssets > currentTotalAssets) {
            require(
                ((_newTotalAssets - currentTotalAssets) <=
                    (currentTotalAssets * uint256(_profitLimitRatio)) /
                        MAX_BPS),
                "healthCheck"
            );
        } else if (currentTotalAssets > _newTotalAssets) {
            require(
                (currentTotalAssets - _newTotalAssets <=
                    ((currentTotalAssets * uint256(_lossLimitRatio)) /
                        MAX_BPS)),
                "healthCheck"
            );
        }
    }
}

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

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

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

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

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

uint256 constant WAD = 1e18;

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

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

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

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

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

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

        return firstTerm + secondTerm + thirdTerm;
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 27 of 36 : MarketParamsLib.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

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

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

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

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;

import "./IUniswapV3SwapCallback.sol";

/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
    struct ExactInputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInputSingle(
        ExactInputSingleParams calldata params
    ) external payable returns (uint256 amountOut);

    struct ExactInputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountIn;
        uint256 amountOutMinimum;
    }

    /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
    /// @return amountOut The amount of the received token
    function exactInput(
        ExactInputParams calldata params
    ) external payable returns (uint256 amountOut);

    struct ExactOutputSingleParams {
        address tokenIn;
        address tokenOut;
        uint24 fee;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
        uint160 sqrtPriceLimitX96;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another token
    /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutputSingle(
        ExactOutputSingleParams calldata params
    ) external payable returns (uint256 amountIn);

    struct ExactOutputParams {
        bytes path;
        address recipient;
        uint256 deadline;
        uint256 amountOut;
        uint256 amountInMaximum;
    }

    /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
    /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
    /// @return amountIn The amount of the input token
    function exactOutput(
        ExactOutputParams calldata params
    ) external payable returns (uint256 amountIn);

    // Taken from https://soliditydeveloper.com/uniswap3
    // Manually added to the interface
    function refundETH() external payable;
}

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;

/**
 * @title BaseSwapper
 * @author yearn.fi
 * @dev Base contract for all swapper contracts except TradeFactorySwapper.
 *      Contains the common minAmountToSell variable that most swappers need.
 */
contract BaseSwapper {
    /// @notice Minimum amount of tokens to sell in a swap.
    uint256 public minAmountToSell;

    /**
     * @dev Set the minimum amount to sell in a swap.
     * @param _minAmountToSell Minimum amount of tokens needed to execute a swap.
     */
    function _setMinAmountToSell(uint256 _minAmountToSell) internal virtual {
        minAmountToSell = _minAmountToSell;
    }
}

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

// Interface that implements the 4626 standard and the implementation functions
interface ITokenizedStrategy is IERC4626, IERC20Permit {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event StrategyShutdown();

    event NewTokenizedStrategy(
        address indexed strategy,
        address indexed asset,
        string apiVersion
    );

    event Reported(
        uint256 profit,
        uint256 loss,
        uint256 protocolFees,
        uint256 performanceFees
    );

    event UpdatePerformanceFeeRecipient(
        address indexed newPerformanceFeeRecipient
    );

    event UpdateKeeper(address indexed newKeeper);

    event UpdatePerformanceFee(uint16 newPerformanceFee);

    event UpdateManagement(address indexed newManagement);

    event UpdateEmergencyAdmin(address indexed newEmergencyAdmin);

    event UpdateProfitMaxUnlockTime(uint256 newProfitMaxUnlockTime);

    event UpdatePendingManagement(address indexed newPendingManagement);

    /*//////////////////////////////////////////////////////////////
                           INITIALIZATION
    //////////////////////////////////////////////////////////////*/

    function initialize(
        address _asset,
        string memory _name,
        address _management,
        address _performanceFeeRecipient,
        address _keeper
    ) external;

    /*//////////////////////////////////////////////////////////////
                    NON-STANDARD 4626 OPTIONS
    //////////////////////////////////////////////////////////////*/

    function withdraw(
        uint256 assets,
        address receiver,
        address owner,
        uint256 maxLoss
    ) external returns (uint256);

    function redeem(
        uint256 shares,
        address receiver,
        address owner,
        uint256 maxLoss
    ) external returns (uint256);

    function maxWithdraw(
        address owner,
        uint256 /*maxLoss*/
    ) external view returns (uint256);

    function maxRedeem(
        address owner,
        uint256 /*maxLoss*/
    ) external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                        MODIFIER HELPERS
    //////////////////////////////////////////////////////////////*/

    function requireManagement(address _sender) external view;

    function requireKeeperOrManagement(address _sender) external view;

    function requireEmergencyAuthorized(address _sender) external view;

    /*//////////////////////////////////////////////////////////////
                        KEEPERS FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function tend() external;

    function report() external returns (uint256 _profit, uint256 _loss);

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

    function MAX_FEE() external view returns (uint16);

    function FACTORY() external view returns (address);

    /*//////////////////////////////////////////////////////////////
                            GETTERS
    //////////////////////////////////////////////////////////////*/

    function apiVersion() external view returns (string memory);

    function pricePerShare() external view returns (uint256);

    function management() external view returns (address);

    function pendingManagement() external view returns (address);

    function keeper() external view returns (address);

    function emergencyAdmin() external view returns (address);

    function performanceFee() external view returns (uint16);

    function performanceFeeRecipient() external view returns (address);

    function fullProfitUnlockDate() external view returns (uint256);

    function profitUnlockingRate() external view returns (uint256);

    function profitMaxUnlockTime() external view returns (uint256);

    function lastReport() external view returns (uint256);

    function isShutdown() external view returns (bool);

    function unlockedShares() external view returns (uint256);

    /*//////////////////////////////////////////////////////////////
                            SETTERS
    //////////////////////////////////////////////////////////////*/

    function setPendingManagement(address) external;

    function acceptManagement() external;

    function setKeeper(address _keeper) external;

    function setEmergencyAdmin(address _emergencyAdmin) external;

    function setPerformanceFee(uint16 _performanceFee) external;

    function setPerformanceFeeRecipient(
        address _performanceFeeRecipient
    ) external;

    function setProfitMaxUnlockTime(uint256 _profitMaxUnlockTime) external;

    function setName(string calldata _newName) external;

    function shutdownStrategy() external;

    function emergencyWithdraw(uint256 _amount) external;
}

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;

interface IBaseStrategy {
    function tokenizedStrategyAddress() external view returns (address);

    /*//////////////////////////////////////////////////////////////
                            IMMUTABLE FUNCTIONS
    //////////////////////////////////////////////////////////////*/

    function availableDepositLimit(
        address _owner
    ) external view returns (uint256);

    function availableWithdrawLimit(
        address _owner
    ) external view returns (uint256);

    function deployFunds(uint256 _assets) external;

    function freeFunds(uint256 _amount) external;

    function harvestAndReport() external returns (uint256);

    function tendThis(uint256 _totalIdle) external;

    function shutdownWithdraw(uint256 _amount) external;

    function tendTrigger() external view returns (bool, bytes memory);
}

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;

import {IStrategy} from "@tokenized-strategy/interfaces/IStrategy.sol";

interface IBaseHealthCheck is IStrategy {
    function doHealthCheck() external view returns (bool);

    function profitLimitRatio() external view returns (uint256);

    function lossLimitRatio() external view returns (uint256);

    function setProfitLimitRatio(uint256 _newProfitLimitRatio) external;

    function setLossLimitRatio(uint256 _newLossLimitRatio) external;

    function setDoHealthCheck(bool _doHealthCheck) external;
}

// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

// TokenizedStrategy interface used for internal view delegateCalls.
import {ITokenizedStrategy} from "./interfaces/ITokenizedStrategy.sol";

/**
 * @title YearnV3 Base Strategy
 * @author yearn.finance
 * @notice
 *  BaseStrategy implements all of the required functionality to
 *  seamlessly integrate with the `TokenizedStrategy` implementation contract
 *  allowing anyone to easily build a fully permissionless ERC-4626 compliant
 *  Vault by inheriting this contract and overriding three simple functions.

 *  It utilizes an immutable proxy pattern that allows the BaseStrategy
 *  to remain simple and small. All standard logic is held within the
 *  `TokenizedStrategy` and is reused over any n strategies all using the
 *  `fallback` function to delegatecall the implementation so that strategists
 *  can only be concerned with writing their strategy specific code.
 *
 *  This contract should be inherited and the three main abstract methods
 *  `_deployFunds`, `_freeFunds` and `_harvestAndReport` implemented to adapt
 *  the Strategy to the particular needs it has to generate yield. There are
 *  other optional methods that can be implemented to further customize
 *  the strategy if desired.
 *
 *  All default storage for the strategy is controlled and updated by the
 *  `TokenizedStrategy`. The implementation holds a storage struct that
 *  contains all needed global variables in a manual storage slot. This
 *  means strategists can feel free to implement their own custom storage
 *  variables as they need with no concern of collisions. All global variables
 *  can be viewed within the Strategy by a simple call using the
 *  `TokenizedStrategy` variable. IE: TokenizedStrategy.globalVariable();.
 */
abstract contract BaseStrategy {
    /*//////////////////////////////////////////////////////////////
                            MODIFIERS
    //////////////////////////////////////////////////////////////*/
    /**
     * @dev Used on TokenizedStrategy callback functions to make sure it is post
     * a delegateCall from this address to the TokenizedStrategy.
     */
    modifier onlySelf() {
        _onlySelf();
        _;
    }

    /**
     * @dev Use to assure that the call is coming from the strategies management.
     */
    modifier onlyManagement() {
        TokenizedStrategy.requireManagement(msg.sender);
        _;
    }

    /**
     * @dev Use to assure that the call is coming from either the strategies
     * management or the keeper.
     */
    modifier onlyKeepers() {
        TokenizedStrategy.requireKeeperOrManagement(msg.sender);
        _;
    }

    /**
     * @dev Use to assure that the call is coming from either the strategies
     * management or the emergency admin.
     */
    modifier onlyEmergencyAuthorized() {
        TokenizedStrategy.requireEmergencyAuthorized(msg.sender);
        _;
    }

    /**
     * @dev Require that the msg.sender is this address.
     */
    function _onlySelf() internal view {
        require(msg.sender == address(this), "!self");
    }

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

    /**
     * @dev This is the address of the TokenizedStrategy implementation
     * contract that will be used by all strategies to handle the
     * accounting, logic, storage etc.
     *
     * Any external calls to the that don't hit one of the functions
     * defined in this base or the strategy will end up being forwarded
     * through the fallback function, which will delegateCall this address.
     *
     * This address should be the same for every strategy, never be adjusted
     * and always be checked before any integration with the Strategy.
     */
    address public constant tokenizedStrategyAddress =
        0xD377919FA87120584B21279a491F82D5265A139c;

    /*//////////////////////////////////////////////////////////////
                            IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Underlying asset the Strategy is earning yield on.
     * Stored here for cheap retrievals within the strategy.
     */
    ERC20 internal immutable asset;

    /**
     * @dev This variable is set to address(this) during initialization of each strategy.
     *
     * This can be used to retrieve storage data within the strategy
     * contract as if it were a linked library.
     *
     *       i.e. uint256 totalAssets = TokenizedStrategy.totalAssets()
     *
     * Using address(this) will mean any calls using this variable will lead
     * to a call to itself. Which will hit the fallback function and
     * delegateCall that to the actual TokenizedStrategy.
     */
    ITokenizedStrategy internal immutable TokenizedStrategy;

    /**
     * @notice Used to initialize the strategy on deployment.
     *
     * This will set the `TokenizedStrategy` variable for easy
     * internal view calls to the implementation. As well as
     * initializing the default storage variables based on the
     * parameters and using the deployer for the permissioned roles.
     *
     * @param _asset Address of the underlying asset.
     * @param _name Name the strategy will use.
     */
    constructor(address _asset, string memory _name) {
        asset = ERC20(_asset);

        // Set instance of the implementation for internal use.
        TokenizedStrategy = ITokenizedStrategy(address(this));

        // Initialize the strategy's storage variables.
        _delegateCall(
            abi.encodeCall(
                ITokenizedStrategy.initialize,
                (_asset, _name, msg.sender, msg.sender, msg.sender)
            )
        );

        // Store the tokenizedStrategyAddress at the standard implementation
        // address storage slot so etherscan picks up the interface. This gets
        // stored on initialization and never updated.
        assembly {
            sstore(
                // keccak256('eip1967.proxy.implementation' - 1)
                0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
                tokenizedStrategyAddress
            )
        }
    }

    /*//////////////////////////////////////////////////////////////
                NEEDED TO BE OVERRIDDEN BY STRATEGIST
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Can deploy up to '_amount' of 'asset' in the yield source.
     *
     * This function is called at the end of a {deposit} or {mint}
     * call. Meaning that unless a whitelist is implemented it will
     * be entirely permissionless and thus can be sandwiched or otherwise
     * manipulated.
     *
     * @param _amount The amount of 'asset' that the strategy can attempt
     * to deposit in the yield source.
     */
    function _deployFunds(uint256 _amount) internal virtual;

    /**
     * @dev Should attempt to free the '_amount' of 'asset'.
     *
     * NOTE: The amount of 'asset' that is already loose has already
     * been accounted for.
     *
     * This function is called during {withdraw} and {redeem} calls.
     * Meaning that unless a whitelist is implemented it will be
     * entirely permissionless and thus can be sandwiched or otherwise
     * manipulated.
     *
     * Should not rely on asset.balanceOf(address(this)) calls other than
     * for diff accounting purposes.
     *
     * Any difference between `_amount` and what is actually freed will be
     * counted as a loss and passed on to the withdrawer. This means
     * care should be taken in times of illiquidity. It may be better to revert
     * if withdraws are simply illiquid so not to realize incorrect losses.
     *
     * @param _amount, The amount of 'asset' to be freed.
     */
    function _freeFunds(uint256 _amount) internal virtual;

    /**
     * @dev Internal function to harvest all rewards, redeploy any idle
     * funds and return an accurate accounting of all funds currently
     * held by the Strategy.
     *
     * This should do any needed harvesting, rewards selling, accrual,
     * redepositing etc. to get the most accurate view of current assets.
     *
     * NOTE: All applicable assets including loose assets should be
     * accounted for in this function.
     *
     * Care should be taken when relying on oracles or swap values rather
     * than actual amounts as all Strategy profit/loss accounting will
     * be done based on this returned value.
     *
     * This can still be called post a shutdown, a strategist can check
     * `TokenizedStrategy.isShutdown()` to decide if funds should be
     * redeployed or simply realize any profits/losses.
     *
     * @return _totalAssets A trusted and accurate account for the total
     * amount of 'asset' the strategy currently holds including idle funds.
     */
    function _harvestAndReport()
        internal
        virtual
        returns (uint256 _totalAssets);

    /*//////////////////////////////////////////////////////////////
                    OPTIONAL TO OVERRIDE BY STRATEGIST
    //////////////////////////////////////////////////////////////*/

    /**
     * @dev Optional function for strategist to override that can
     *  be called in between reports.
     *
     * If '_tend' is used tendTrigger() will also need to be overridden.
     *
     * This call can only be called by a permissioned role so may be
     * through protected relays.
     *
     * This can be used to harvest and compound rewards, deposit idle funds,
     * perform needed position maintenance or anything else that doesn't need
     * a full report for.
     *
     *   EX: A strategy that can not deposit funds without getting
     *       sandwiched can use the tend when a certain threshold
     *       of idle to totalAssets has been reached.
     *
     * This will have no effect on PPS of the strategy till report() is called.
     *
     * @param _totalIdle The current amount of idle funds that are available to deploy.
     */
    function _tend(uint256 _totalIdle) internal virtual {}

    /**
     * @dev Optional trigger to override if tend() will be used by the strategy.
     * This must be implemented if the strategy hopes to invoke _tend().
     *
     * @return . Should return true if tend() should be called by keeper or false if not.
     */
    function _tendTrigger() internal view virtual returns (bool) {
        return false;
    }

    /**
     * @notice Returns if tend() should be called by a keeper.
     *
     * @return . Should return true if tend() should be called by keeper or false if not.
     * @return . Calldata for the tend call.
     */
    function tendTrigger() external view virtual returns (bool, bytes memory) {
        return (
            // Return the status of the tend trigger.
            _tendTrigger(),
            // And the needed calldata either way.
            abi.encodeWithSelector(ITokenizedStrategy.tend.selector)
        );
    }

    /**
     * @notice Gets the max amount of `asset` that an address can deposit.
     * @dev Defaults to an unlimited amount for any address. But can
     * be overridden by strategists.
     *
     * This function will be called before any deposit or mints to enforce
     * any limits desired by the strategist. This can be used for either a
     * traditional deposit limit or for implementing a whitelist etc.
     *
     *   EX:
     *      if(isAllowed[_owner]) return super.availableDepositLimit(_owner);
     *
     * This does not need to take into account any conversion rates
     * from shares to assets. But should know that any non max uint256
     * amounts may be converted to shares. So it is recommended to keep
     * custom amounts low enough as not to cause overflow when multiplied
     * by `totalSupply`.
     *
     * @param . The address that is depositing into the strategy.
     * @return . The available amount the `_owner` can deposit in terms of `asset`
     */
    function availableDepositLimit(
        address /*_owner*/
    ) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /**
     * @notice Gets the max amount of `asset` that can be withdrawn.
     * @dev Defaults to an unlimited amount for any address. But can
     * be overridden by strategists.
     *
     * This function will be called before any withdraw or redeem to enforce
     * any limits desired by the strategist. This can be used for illiquid
     * or sandwichable strategies. It should never be lower than `totalIdle`.
     *
     *   EX:
     *       return TokenIzedStrategy.totalIdle();
     *
     * This does not need to take into account the `_owner`'s share balance
     * or conversion rates from shares to assets.
     *
     * @param . The address that is withdrawing from the strategy.
     * @return . The available amount that can be withdrawn in terms of `asset`
     */
    function availableWithdrawLimit(
        address /*_owner*/
    ) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    /**
     * @dev Optional function for a strategist to override that will
     * allow management to manually withdraw deployed funds from the
     * yield source if a strategy is shutdown.
     *
     * This should attempt to free `_amount`, noting that `_amount` may
     * be more than is currently deployed.
     *
     * NOTE: This will not realize any profits or losses. A separate
     * {report} will be needed in order to record any profit/loss. If
     * a report may need to be called after a shutdown it is important
     * to check if the strategy is shutdown during {_harvestAndReport}
     * so that it does not simply re-deploy all funds that had been freed.
     *
     * EX:
     *   if(freeAsset > 0 && !TokenizedStrategy.isShutdown()) {
     *       depositFunds...
     *    }
     *
     * @param _amount The amount of asset to attempt to free.
     */
    function _emergencyWithdraw(uint256 _amount) internal virtual {}

    /*//////////////////////////////////////////////////////////////
                        TokenizedStrategy HOOKS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Can deploy up to '_amount' of 'asset' in yield source.
     * @dev Callback for the TokenizedStrategy to call during a {deposit}
     * or {mint} to tell the strategy it can deploy funds.
     *
     * Since this can only be called after a {deposit} or {mint}
     * delegateCall to the TokenizedStrategy msg.sender == address(this).
     *
     * Unless a whitelist is implemented this will be entirely permissionless
     * and thus can be sandwiched or otherwise manipulated.
     *
     * @param _amount The amount of 'asset' that the strategy can
     * attempt to deposit in the yield source.
     */
    function deployFunds(uint256 _amount) external virtual onlySelf {
        _deployFunds(_amount);
    }

    /**
     * @notice Should attempt to free the '_amount' of 'asset'.
     * @dev Callback for the TokenizedStrategy to call during a withdraw
     * or redeem to free the needed funds to service the withdraw.
     *
     * This can only be called after a 'withdraw' or 'redeem' delegateCall
     * to the TokenizedStrategy so msg.sender == address(this).
     *
     * @param _amount The amount of 'asset' that the strategy should attempt to free up.
     */
    function freeFunds(uint256 _amount) external virtual onlySelf {
        _freeFunds(_amount);
    }

    /**
     * @notice Returns the accurate amount of all funds currently
     * held by the Strategy.
     * @dev Callback for the TokenizedStrategy to call during a report to
     * get an accurate accounting of assets the strategy controls.
     *
     * This can only be called after a report() delegateCall to the
     * TokenizedStrategy so msg.sender == address(this).
     *
     * @return . A trusted and accurate account for the total amount
     * of 'asset' the strategy currently holds including idle funds.
     */
    function harvestAndReport() external virtual onlySelf returns (uint256) {
        return _harvestAndReport();
    }

    /**
     * @notice Will call the internal '_tend' when a keeper tends the strategy.
     * @dev Callback for the TokenizedStrategy to initiate a _tend call in the strategy.
     *
     * This can only be called after a tend() delegateCall to the TokenizedStrategy
     * so msg.sender == address(this).
     *
     * We name the function `tendThis` so that `tend` calls are forwarded to
     * the TokenizedStrategy.

     * @param _totalIdle The amount of current idle funds that can be
     * deployed during the tend
     */
    function tendThis(uint256 _totalIdle) external virtual onlySelf {
        _tend(_totalIdle);
    }

    /**
     * @notice Will call the internal '_emergencyWithdraw' function.
     * @dev Callback for the TokenizedStrategy during an emergency withdraw.
     *
     * This can only be called after a emergencyWithdraw() delegateCall to
     * the TokenizedStrategy so msg.sender == address(this).
     *
     * We name the function `shutdownWithdraw` so that `emergencyWithdraw`
     * calls are forwarded to the TokenizedStrategy.
     *
     * @param _amount The amount of asset to attempt to free.
     */
    function shutdownWithdraw(uint256 _amount) external virtual onlySelf {
        _emergencyWithdraw(_amount);
    }

    /**
     * @dev Function used to delegate call the TokenizedStrategy with
     * certain `_calldata` and return any return values.
     *
     * This is used to setup the initial storage of the strategy, and
     * can be used by strategist to forward any other call to the
     * TokenizedStrategy implementation.
     *
     * @param _calldata The abi encoded calldata to use in delegatecall.
     * @return . The return value if the call was successful in bytes.
     */
    function _delegateCall(
        bytes memory _calldata
    ) internal returns (bytes memory) {
        // Delegate call the tokenized strategy with provided calldata.
        (bool success, bytes memory result) = tokenizedStrategyAddress
            .delegatecall(_calldata);

        // If the call reverted. Return the error.
        if (!success) {
            assembly {
                let ptr := mload(0x40)
                let size := returndatasize()
                returndatacopy(ptr, 0, size)
                revert(ptr, size)
            }
        }

        // Return the result.
        return result;
    }

    /**
     * @dev Execute a function on the TokenizedStrategy and return any value.
     *
     * This fallback function will be executed when any of the standard functions
     * defined in the TokenizedStrategy are called since they wont be defined in
     * this contract.
     *
     * It will delegatecall the TokenizedStrategy implementation with the exact
     * calldata and return any relevant values.
     *
     */
    fallback() external {
        // load our target address
        address _tokenizedStrategyAddress = tokenizedStrategyAddress;
        // Execute external function using delegatecall and return any value.
        assembly {
            // Copy function selector and any arguments.
            calldatacopy(0, 0, calldatasize())
            // Execute function delegatecall.
            let result := delegatecall(
                gas(),
                _tokenizedStrategyAddress,
                0,
                calldatasize(),
                0,
                0
            )
            // Get any return value
            returndatacopy(0, 0, returndatasize())
            // Return any return value or error back to the caller
            switch result
            case 0 {
                revert(0, returndatasize())
            }
            default {
                return(0, returndatasize())
            }
        }
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /* SLOT OFFSETS */

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

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

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

    /* GETTERS */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
    /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
    /// @dev In the implementation you must pay the pool tokens owed for the swap.
    /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
    /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
    /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
    /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
    /// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
    /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
    function uniswapV3SwapCallback(
        int256 amount0Delta,
        int256 amount1Delta,
        bytes calldata data
    ) external;
}

Settings
{
  "remappings": [
    "@openzeppelin/=lib/openzeppelin-contracts/",
    "forge-std/=lib/forge-std/src/",
    "@tokenized-strategy/=lib/tokenized-strategy/src/",
    "@periphery/=lib/tokenized-strategy-periphery/src/",
    "@yearn-vaults/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/contracts/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/tokenized-strategy/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/openzeppelin-contracts/contracts/",
    "tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/",
    "tokenized-strategy/=lib/tokenized-strategy/",
    "yearn-vaults-v3/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_management","type":"address"},{"internalType":"address","name":"_performanceFeeRecipient","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_emergencyAdmin","type":"address"},{"internalType":"address","name":"_gov","type":"address"},{"internalType":"address","name":"_morpho","type":"address"},{"internalType":"address","name":"_router","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":true,"internalType":"Id","name":"marketId","type":"bytes32"}],"name":"NewStrategy","type":"event"},{"inputs":[],"name":"GOV","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Id","name":"","type":"bytes32"}],"name":"deployments","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"management","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morpho","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_borrowToken","type":"address"},{"internalType":"address","name":"_lenderVault","type":"address"},{"internalType":"Id","name":"_marketId","type":"bytes32"},{"internalType":"address","name":"_borrowUsdOracle","type":"address"}],"name":"newStrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"performanceFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_management","type":"address"},{"internalType":"address","name":"_performanceFeeRecipient","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_emergencyAdmin","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60e060405234801561000f575f80fd5b5060405161616238038061616283398101604081905261002e916100bf565b6001600160a01b038316610040575f80fd5b5f80546001600160a01b03199081166001600160a01b03998a16179091556001805482169789169790971790965560028054871695881695909517909455600380549095169286169290921790935591831660805290821660a0521660c052610140565b80516001600160a01b03811681146100ba575f80fd5b919050565b5f805f805f805f60e0888a0312156100d5575f80fd5b6100de886100a4565b96506100ec602089016100a4565b95506100fa604089016100a4565b9450610108606089016100a4565b9350610116608089016100a4565b925061012460a089016100a4565b915061013260c089016100a4565b905092959891949750929550565b60805160a05160c051615fe461017e5f395f81816101c8015261025701525f818161018c015261023401525f818160e501526102130152615fe45ff3fe608060405234801562000010575f80fd5b5060043610620000a8575f3560e01c806388a8d602116200006b57806388a8d602146200015f578063aced16611462000172578063d8fbc8331462000186578063ed27f7c914620001ae578063f887ea4014620001c2575f80fd5b80630b18f8a514620000ac578063180cb47f14620000df5780633740401714620001075780634a945f8d146200013257806370905dce146200014b575b5f80fd5b620000c3620000bd36600462000552565b620001ea565b6040516001600160a01b03909116815260200160405180910390f35b620000c37f000000000000000000000000000000000000000000000000000000000000000081565b620000c36200011836600462000615565b60046020525f90815260409020546001600160a01b031681565b62000149620001433660046200062d565b6200048d565b005b600354620000c3906001600160a01b031681565b5f54620000c3906001600160a01b031681565b600254620000c3906001600160a01b031681565b620000c37f000000000000000000000000000000000000000000000000000000000000000081565b600154620000c3906001600160a01b031681565b620000c37f000000000000000000000000000000000000000000000000000000000000000081565b5f828152600460205260408120546001600160a01b0316156200020b575f80fd5b5f88888888887f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008a8a7f0000000000000000000000000000000000000000000000000000000000000000604051620002849062000528565b620002999a9998979695949392919062000687565b604051809103905ff080158015620002b3573d5f803e3d5ffd5b5060015460405163352f8d5160e11b81526001600160a01b039182166004820152919250821690636a5f1aa2906024015f604051808303815f87803b158015620002fb575f80fd5b505af11580156200030e573d5f803e3d5ffd5b5050600254604051633a43a3f360e11b81526001600160a01b039182166004820152908416925063748747e691506024015f604051808303815f87803b15801562000357575f80fd5b505af11580156200036a573d5f803e3d5ffd5b50505f54604051630f629b7960e41b81526001600160a01b039182166004820152908416925063f629b79091506024015f604051808303815f87803b158015620003b2575f80fd5b505af1158015620003c5573d5f803e3d5ffd5b5050600354604051630d768ce560e21b81526001600160a01b03918216600482015290841692506335da339491506024015f604051808303815f87803b1580156200040e575f80fd5b505af115801562000421573d5f803e3d5ffd5b50506040518692506001600160a01b03841691507f0df9ebba034d173a99a9f19984c4f6b69c45f86fdf65be5234f4040127fb1b17905f90a35f9384526004602052604090932080546001600160a01b0319166001600160a01b03851617905550909695505050505050565b5f546001600160a01b03163314620004d95760405162461bcd60e51b815260206004820152600b60248201526a085b585b9859d95b595b9d60aa1b604482015260640160405180910390fd5b5f80546001600160a01b039586166001600160a01b0319918216179091556001805494861694821694909417909355600280549285169284169290921790915560038054919093169116179055565b6158a7806200070883390190565b80356001600160a01b03811681146200054d575f80fd5b919050565b5f805f805f805f60c0888a03121562000569575f80fd5b620005748862000536565b9650602088013567ffffffffffffffff8082111562000591575f80fd5b818a0191508a601f830112620005a5575f80fd5b813581811115620005b4575f80fd5b8b6020828501011115620005c6575f80fd5b602083019850809750505050620005e06040890162000536565b9350620005f06060890162000536565b9250608088013591506200060760a0890162000536565b905092959891949750929550565b5f6020828403121562000626575f80fd5b5035919050565b5f805f806080858703121562000641575f80fd5b6200064c8562000536565b93506200065c6020860162000536565b92506200066c6040860162000536565b91506200067c6060860162000536565b905092959194509250565b6001600160a01b038b811682526101206020830181905282018a90525f90610140908b8d838601375f848d01830152998a16604084015297891660608301525094871660808601529290951660a084015260c08301526001600160a01b0393841660e0830152909216610100830152601f909201601f191601019291505056fe6101a06040525f80546227100162ffffff19909116179055600580546001600160a01b031990811673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2179091556006805490911673e592427a0aece92de3edee1f18e0157c058615641790553480156200006b575f80fd5b50604051620058a7380380620058a78339810160408190526200008e9162000a73565b6001600160a01b0389166080523060a052604051899089908990899084908490829082906200010390620000cf908490849033908190819060240162000bc4565b60408051601f198184030181529190526020810180516001600160e01b03908116634b839d7360e11b179091526200056716565b505073d377919fa87120584b21279a491f82d5265a139c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc555050506001600160a01b0382811660c0525f196001555f8054642e90edd000600255600160281b600160901b0319166b01f41f401b580000000000001790558116156200026e57806001600160a01b031660e0816001600160a01b031681525050816001600160a01b031660e0516001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001e6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200020c919062000c06565b6001600160a01b031614620002575760405162461bcd60e51b815260206004820152600c60248201526b085b195b99195c95985d5b1d60a21b60448201526064015b60405180910390fd5b6200026e6001600160a01b038316825f19620005f6565b505050506001600160a01b03858116610140528416610100819052610120849052604051632c3c915760e01b815260048101859052632c3c91579060240160a060405180830381865afa158015620002c8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620002ee919062000c22565b8051600880546001600160a01b03199081166001600160a01b0393841690811790925560208401516009805483169185169190911790556040840151600a805483169185169190911790556060840151600b8054909216908416179055608090920151600c5588161480156200037157506009546001600160a01b038a81169116145b6200037a575f80fd5b886001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003b7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620003dd919062000cb5565b620003ea90600a62000de4565b6101608181525050866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200042f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000455919062000cb5565b6200046290600a62000de4565b610180526200047d6001600160a01b038a16855f19620005f6565b620004946001600160a01b038816855f19620005f6565b620004a0612710600455565b8060065f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000503573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000529919062000cb5565b60ff1660081462000538575f80fd5b50600d80546001600160a01b0319166001600160a01b03929092169190911790555062000e4695505050505050565b60605f8073d377919fa87120584b21279a491f82d5265a139c6001600160a01b03168460405162000599919062000df4565b5f60405180830381855af49150503d805f8114620005d3576040519150601f19603f3d011682016040523d82523d5f602084013e620005d8565b606091505b509150915081620005ef576040513d805f833e8082fd5b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152620006509085908390620006cb16565b620006c5576040516001600160a01b03841660248201525f6044820152620006b990859063095ea7b360e01b9060640160408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b03938416179052906200077716565b620006c5848262000777565b50505050565b5f805f846001600160a01b031684604051620006e8919062000df4565b5f604051808303815f865af19150503d805f811462000723576040519150601f19603f3d011682016040523d82523d5f602084013e62000728565b606091505b5091509150818015620007565750805115806200075657508080602001905181019062000756919062000e11565b80156200076c57506001600160a01b0385163b15155b925050505b92915050565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201525f90620007c5906001600160a01b0385169084906200084e565b905080515f1480620007e8575080806020019051810190620007e8919062000e11565b620008495760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016200024e565b505050565b60606200085e84845f8562000866565b949350505050565b606082471015620008c95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016200024e565b5f80866001600160a01b03168587604051620008e6919062000df4565b5f6040518083038185875af1925050503d805f811462000922576040519150601f19603f3d011682016040523d82523d5f602084013e62000927565b606091505b5090925090506200093b8783838762000946565b979650505050505050565b60608315620009b95782515f03620009b1576001600160a01b0385163b620009b15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200024e565b50816200085e565b6200085e8383815115620009d05781518083602001fd5b8060405162461bcd60e51b81526004016200024e919062000e32565b80516001600160a01b038116811462000a03575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171562000a475762000a4762000a08565b604052919050565b5f5b8381101562000a6b57818101518382015260200162000a51565b50505f910152565b5f805f805f805f805f6101208a8c03121562000a8d575f80fd5b62000a988a620009ec565b60208b01519099506001600160401b038082111562000ab5575f80fd5b818c0191508c601f83011262000ac9575f80fd5b81518181111562000ade5762000ade62000a08565b62000af3601f8201601f191660200162000a1c565b91508082528d602082850101111562000b0a575f80fd5b62000b1d81602084016020860162000a4f565b50985062000b30905060408b01620009ec565b965062000b4060608b01620009ec565b955062000b5060808b01620009ec565b945062000b6060a08b01620009ec565b935060c08a0151925062000b7760e08b01620009ec565b915062000b886101008b01620009ec565b90509295985092959850929598565b5f815180845262000bb081602086016020860162000a4f565b601f01601f19169290920160200192915050565b5f60018060a01b03808816835260a0602084015262000be760a084018862000b97565b9581166040840152938416606083015250911660809091015292915050565b5f6020828403121562000c17575f80fd5b620005ef82620009ec565b5f60a0828403121562000c33575f80fd5b60405160a081016001600160401b038111828210171562000c585762000c5862000a08565b60405262000c6683620009ec565b815262000c7660208401620009ec565b602082015262000c8960408401620009ec565b604082015262000c9c60608401620009ec565b6060820152608083015160808201528091505092915050565b5f6020828403121562000cc6575f80fd5b815160ff81168114620005ef575f80fd5b634e487b7160e01b5f52601160045260245ffd5b600181815b8085111562000d2b57815f190482111562000d0f5762000d0f62000cd7565b8085161562000d1d57918102915b93841c939080029062000cf0565b509250929050565b5f8262000d435750600162000771565b8162000d5157505f62000771565b816001811462000d6a576002811462000d755762000d95565b600191505062000771565b60ff84111562000d895762000d8962000cd7565b50506001821b62000771565b5060208310610133831016604e8410600b841016171562000dba575081810a62000771565b62000dc6838362000ceb565b805f190482111562000ddc5762000ddc62000cd7565b029392505050565b5f620005ef60ff84168362000d33565b5f825162000e0781846020870162000a4f565b9190910192915050565b5f6020828403121562000e22575f80fd5b81518015158114620005ef575f80fd5b602081525f620005ef602083018462000b97565b60805160a05160c05160e051610100516101205161014051610160516101805161482d6200107a5f395f6128d301525f6128f901525f8181610444015281816108a90152610a0801525f8181610612015281816112f901528181612f66015261322c01525f818161079801528181610fb00152818161133101528181611e730152818161248601528181612f9e015281816131c30152818161320a0152818161326b01528181613a000152613a5b01525f81816103f10152818161098501528181610df501528181611ba001528181611dee015281816122de0152818161235c015281816123f30152613af401525f81816105160152818161094701528181610b18015281816110d5015281816112760152818161149f0152818161192a015281816121ff015281816126250152818161266c015281816127c101528181612e3f01528181612ec101528181612f2c01528181613578015281816138c30152818161394e01526139b901525f8181610ba401528181610ca701528181610d2301528181610ee601528181610fee0152818161115c015281816113c20152818161143f0152818161154f015281816115cb01528181611657015281816116dd01528181611758015281816117ee015281816118690152818161197801528181611a4e01528181611f2b015261207201525f818161090a01528181610b3e01528181611503015281816118f001528181612109015281816126460152818161268d015281816128020152613998015261482d5ff3fe608060405234801561000f575f80fd5b5060043610610376575f3560e01c80636ed71ede116101d1578063bdc8144b11610102578063ebf27802116100a0578063f0fa55a91161007a578063f0fa55a914610822578063f3ce280a14610835578063f887ea4014610878578063fde813a81461088b57610376565b8063ebf27802146107f3578063ecf7085814610806578063ee8bfa311461080f57610376565b8063d8fbc833116100dc578063d8fbc83314610793578063da769c47146107ba578063e4d746fb146107cf578063e8621149146107eb57610376565b8063bdc8144b14610752578063d19a3bb814610765578063d69686011461078057610376565b8063950b3d731161016f578063ac00ff2611610149578063ac00ff261461071b578063b1ea668d1461072e578063b2a1913714610736578063b6a165061461074957610376565b8063950b3d73146106e25780639b90fb16146106f55780639d7fb70c1461070857610376565b8063797bf343116101ab578063797bf3431461064f5780637b9e68f2146106575780637d969932146106bc5780638e72e31b146106ce57610376565b80636ed71ede1461060d578063702651db14610634578063735fd1891461063c57610376565b80633e032a3b116102ab57806355b90362116102495780636353f822116102235780636353f822146105ba5780636687500e146105e15780636718835f146105e95780636be36a1d1461060557610376565b806355b90362146105895780635bc34efe1461059c5780635d265d3f146105a457610376565b806349317f1d1161028557806349317f1d1461054b5780634a5d0943146105535780635001f3b514610563578063503160d91461057657610376565b80633e032a3b146104de578063456dc17a1461051157806346aa2f121461053857610376565b8063278b73121161031857806331b7f9b2116102f257806331b7f9b2146104a857806338dbd359146104b0578063392f7a70146104c35780633d6cb575146104cb57610376565b8063278b73121461046f5780632d49be91146104825780632e3ecf2c1461049557610376565b806306876d191161035457806306876d19146103ec578063085c884f1461042b578063180cb47f1461043f578063181443671461046657610376565b806301681a62146103ab57806302402ae0146103be57806304bd4629146103d9575b73d377919fa87120584b21279a491f82d5265a139c365f80375f80365f845af43d5f803e8080156103a5573d5ff35b3d5ffd5b005b6103a96103b936600461406a565b61089e565b6103c6610a8f565b6040519081526020015b60405180910390f35b6103c66103e736600461406a565b610acd565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103d0565b6103c6610439366004614083565b50600190565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6103c660045481565b6103a961047d36600461406a565b610b8f565b6103a9610490366004614083565b610c92565b6103a96104a336600461409a565b610d0e565b6103c6610dde565b6103a96104be3660046140f2565b610ed1565b6103c6610f66565b6103a96104d9366004614083565b610fd9565b5f546104f890600160501b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103d0565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6103c661054636600461406a565b610fea565b6103c6611117565b5f54610100900461ffff166103c6565b600554610413906001600160a01b031681565b6103a9610584366004614083565b611136565b6103a961059736600461411e565b611147565b6103c661125f565b6105ac6112ad565b6040516103d092919061419c565b5f546105ce90600160401b900461ffff1681565b60405161ffff90911681526020016103d0565b6103c66112ea565b5f546105f59060ff1681565b60405190151581526020016103d0565b600c546103c6565b6103c67f000000000000000000000000000000000000000000000000000000000000000081565b6103a96113ad565b6103a961064a3660046141b6565b61142a565b6103c66114ec565b600854600954600a54600b54600c54610681946001600160a01b0390811694811693811692169085565b604080516001600160a01b0396871681529486166020860152928516928401929092529092166060820152608081019190915260a0016103d0565b5f546301000000900461ffff166103c6565b5f546105ce90600160301b900461ffff1681565b6103a96106f0366004614083565b61153a565b6103a9610703366004614083565b6115b6565b6103a9610716366004614083565b611631565b6103a96107293660046140f2565b611642565b6103a96116c8565b6103a961074436600461406a565b611743565b6103c660025481565b6103a9610760366004614083565b6117d9565b61041373d377919fa87120584b21279a491f82d5265a139c81565b6103a961078e366004614083565b611854565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b5f546105f59065010000000000900460ff1681565b6103c66107dd366004614083565b5068056bc75e2d6310000090565b6103c66118d0565b6103a9610801366004614083565b611963565b6103c660015481565b600d54610413906001600160a01b031681565b6103a9610830366004614083565b611a39565b6108646108433660046141de565b600760209081525f928352604080842090915290825290205462ffffff1681565b60405162ffffff90911681526020016103d0565b600654610413906001600160a01b031681565b6103a9610899366004614083565b611b15565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109085760405162461bcd60e51b81526004016108ff9060208082526004908201526310b3b7bb60e11b604082015260600190565b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b03161415801561097c57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b80156109ba57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b6109ef5760405162461bcd60e51b815260206004820152600660248201526502173776565760d41b60448201526064016108ff565b6040516370a0823160e01b8152306004820152610a8c907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b038416906370a0823190602401602060405180830381865afa158015610a57573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a7b9190614206565b6001600160a01b0384169190611b26565b50565b5f80610a9961125f565b610aa1610dde565b610aab9190614231565b90505f610ab6610f66565b9050808210610ac7575f9250505090565b03919050565b5f80610ad76112ea565b610ae2906001614231565b90505f610aed611b89565b9050610af7610dde565b811015610b7457610b06611bdf565b670de0b6b3a7640000610b62610b3c847f0000000000000000000000000000000000000000000000000000000000000000611c0a565b7f0000000000000000000000000000000000000000000000000000000000000000611ca1565b0281610b7057610b70614244565b0491505b81610b7d6114ec565b610b879190614231565b949350505050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610bec575f80fd5b505afa158015610bfe573d5f803e3d5ffd5b50505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c3e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c629190614258565b60ff16600814610c70575f80fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610cef575f80fd5b505afa158015610d01573d5f803e3d5ffd5b50505050610a8c81600455565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610d6b575f80fd5b505afa158015610d7d573d5f803e3d5ffd5b5050506001600160a01b038085165f8181526007602081815260408084209589168452948152848320805462ffffff1990811662ffffff8a16908117909255928252858420948452939052929020805490921617905550505050565b505050565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a9082906370a08231906024015b602060405180830381865afa158015610e4b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e6f9190614206565b6040518263ffffffff1660e01b8152600401610e8d91815260200190565b602060405180830381865afa158015610ea8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ecc9190614206565b905090565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610f2e575f80fd5b505afa158015610f40573d5f803e3d5ffd5b50505f8054931515650100000000000265ff000000000019909416939093179092555050565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b5482166060820152600c5460808201525f91610ecc917f00000000000000000000000000000000000000000000000000000000000000009091169030611d2e565b610fe1611d82565b610a8c81611db9565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611048573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061106c9190614206565b90505f816001541161107e575f61108c565b8160015461108c9190614278565b90505f61109a5f1983611dc2565b90505f6110b56110a8611dd7565b6110b0611e25565b611dc2565b905061110d826110b06110c6611bdf565b670de0b6b3a76400006110f9867f0000000000000000000000000000000000000000000000000000000000000000611c0a565b611103919061428b565b610b3c91906142a2565b9695505050505050565b5f611120611d82565b611128611ebf565b905061113381611f10565b90565b61113e611d82565b610a8c81612053565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156111a4575f80fd5b505afa1580156111b6573d5f803e3d5ffd5b505050506123288161ffff16111580156111d757508061ffff168261ffff16105b80156111e6575061ffff821615155b6112205760405162461bcd60e51b815260206004820152600b60248201526a34b73b30b634b210262a2b60a91b60448201526064016108ff565b5f805469ffffffff0000000000001916600160301b61ffff9485160269ffff0000000000000000191617600160401b9290931691909102919091179055565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401610e8d565b5f60606112b861205c565b6040805160048152602481019091526020810180516001600160e01b031663440368a360e01b17905290939092509050565b6040516349e2903160e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906393c5206290604401606060405180830381865afa158015611376573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061139a919061431c565b604001516001600160801b031692915050565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b15801561140a575f80fd5b505afa15801561141c573d5f803e3d5ffd5b50505050611428612260565b565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b158015611487575f80fd5b505afa158015611499573d5f803e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036114e3576114df816122ba565b5050565b6114df81612465565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401610e8d565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611597575f80fd5b505afa1580156115a9573d5f803e3d5ffd5b50505050610a8c816124f2565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611613575f80fd5b505afa158015611625573d5f803e3d5ffd5b50505060029190915550565b611639611d82565b610a8c8161258b565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b15801561169f575f80fd5b505afa1580156116b1573d5f803e3d5ffd5b50505f805460ff1916931515939093179092555050565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b158015611725575f80fd5b505afa158015611737573d5f803e3d5ffd5b5050505061142861259b565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156117a0575f80fd5b505afa1580156117b2573d5f803e3d5ffd5b5050600580546001600160a01b0319166001600160a01b0394909416939093179092555050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611836575f80fd5b505afa158015611848573d5f803e3d5ffd5b50505060019190915550565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156118b1575f80fd5b505afa1580156118c3573d5f803e3d5ffd5b50505050610a8c816125b6565b5f806118da6112ea565b9050805f036118ea575f91505090565b611914817f0000000000000000000000000000000000000000000000000000000000000000611c0a565b670de0b6b3a764000061194e611928610f66565b7f0000000000000000000000000000000000000000000000000000000000000000611c0a565b028161195c5761195c614244565b0491505090565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b1580156119c0575f80fd5b505afa1580156119d2573d5f803e3d5ffd5b505050505f198103611a30575f6119e761125f565b90505f816119f3610dde565b6119fd9190614231565b90505f611a08610f66565b9050808211611a17575f611a2a565b611a2a611a248284614278565b84611dc2565b93505050505b610a8c81612616565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611a96575f80fd5b505afa158015611aa8573d5f803e3d5ffd5b505050506127108110611ae85760405162461bcd60e51b8152602060048201526008602482015267736c69707061676560c01b60448201526064016108ff565b5f805467ffffffffffffffff909216600160501b0267ffffffffffffffff60501b19909216919091179055565b611b1d611d82565b610a8c816126b6565b6040516001600160a01b038316602482015260448101829052610dd990849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526126eb565b604051636c82bbbf60e11b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a90829063d905777e90602401610e30565b5f8054600c5461271091600160301b900461ffff16905b0281611c0457611c04614244565b04905090565b5f825f03611c1957505f611c9b565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c55573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c799190614258565b60ff16600a0a611c88836127be565b840281611c9757611c97614244565b0490505b92915050565b5f825f03611cb057505f611c9b565b611cb9826127be565b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d199190614258565b60ff16600a0a840281611c9757611c97614244565b5f80611d3b8460a0902090565b90505f611d526001600160a01b038716838661293f565b90505f80611d6088886129f1565b9094509250611d7491508490508383612ce7565b9450505050505b9392505050565b3330146114285760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b60448201526064016108ff565b610a8c81612d0b565b5f818310611dd05781611d7b565b5090919050565b60405163402d267d60e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063402d267d90602401610e8d565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b5482166060820152600c5460808201525f9182918291611e9b917f0000000000000000000000000000000000000000000000000000000000000000909116906129f1565b509250509150808211611eae575f611eb8565b611eb88183614278565b9250505090565b5f611ec8612260565b611ee4611edf611ed66114ec565b6110b030610fea565b612dad565b611eec612f11565b611ef46112ea565b611efc6114ec565b611f069190614231565b610ecc9190614278565b5f5460ff16611f2857505f805460ff19166001179055565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fa99190614206565b90508082111561201c575f5461271090611fcc90610100900461ffff168361428b565b611fd691906142a2565b611fe08284614278565b11156114df5760405162461bcd60e51b815260206004820152600b60248201526a6865616c7468436865636b60a81b60448201526064016108ff565b818111156114df575f546127109061203f906301000000900461ffff168361428b565b61204991906142a2565b611fe08383614278565b610a8c81612dad565b5f612065612f57565b156120705750600190565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120cc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120f09190614206565b5f036120fb57505f90565b5f61212d6121076112ea565b7f0000000000000000000000000000000000000000000000000000000000000000611c0a565b90505f61213b611928610f66565b90505f80831161214b575f612168565b8261215e670de0b6b3a76400008461428b565b61216891906142a2565b9050612172613109565b811115612183576001935050505090565b5f61218c611bdf565b9050811580159061219a57505f5b156121ae5760025448111594505050505090565b80821080156121cd575067016345785d8a00006121cb8383614278565b115b15612256575f670de0b6b3a76400006121e6838761428b565b6121f091906142a2565b90508381035f61222e612223837f0000000000000000000000000000000000000000000000000000000000000000611ca1565b6110b06110a8611dd7565b9050805f03612244575f97505050505050505090565b60025448111597505050505050505090565b5f94505050505090565b5f61226961125f565b612271610dde565b61227b9190614231565b90505f612286610f66565b9050808211156114df575f61229b8284614278565b90506122a681613124565b610dd96122b5826110b061125f565b612616565b604051630a28a47760e01b8152600481018290525f906123cd906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630a28a47790602401602060405180830381865afa158015612323573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123479190614206565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156123a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b09190614206565b604051635d043b2960e11b815260048101829052306024820181905260448201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ba087652906064016020604051808303815f875af1158015612441573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd99190614206565b805f0361246f5750565b604051638720316d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638720316d906124c2906008908590309081906004016143bd565b5f604051808303815f87803b1580156124d9575f80fd5b505af11580156124eb573d5f803e3d5ffd5b5050505050565b5f81116125305760405162461bcd60e51b815260206004820152600c60248201526b085e995c9bc81c1c9bd99a5d60a21b60448201526064016108ff565b61ffff81111561256e5760405162461bcd60e51b8152602060048201526009602482015268042e8dede40d0d2ced60bb1b60448201526064016108ff565b5f805461ffff9092166101000262ffff0019909216919091179055565b610a8c611edf826110b030610fea565b6114286125b16125a961125f565b6110b0610f66565b61316c565b61271081106125f55760405162461bcd60e51b815260206004820152600b60248201526a085b1bdcdcc81b1a5b5a5d60aa1b60448201526064016108ff565b5f805461ffff90921663010000000264ffff00000019909216919091179055565b805f036126205750565b6114df7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000836126b1857f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006132e6565b613329565b6126c2816110b0611b89565b905080156126d3576126d3816122ba565b6126db61259b565b610a8c6126e661354d565b612465565b5f61273f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135da9092919063ffffffff16565b905080515f148061275f57508080602001905181019061275f91906143f1565b610dd95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108ff565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361280057611c9b6135e8565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031603612936575f6128426135e8565b90505f60086002015f9054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612898573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128bc9190614206565b90506128f76ec097ce7bc90715b34b9f10000000007f000000000000000000000000000000000000000000000000000000000000000061428b565b7f0000000000000000000000000000000000000000000000000000000000000000612922848461428b565b61292c919061428b565b610b8791906142a2565b5f80fd5b919050565b5f8061295361294e8585613693565b613706565b604051637784c68560e01b81529091506001600160a01b03861690637784c6859061298290849060040161440c565b5f60405180830381865afa15801561299c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526129c3919081019061444f565b5f815181106129d4576129d46144f0565b60200260200101515f1c6001600160801b03169150509392505050565b5f805f805f612a018660a0902090565b604051632e3071cd60e11b8152600481018290529091505f906001600160a01b03891690635c60e39a9060240160c060405180830381865afa158015612a49573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a6d9190614504565b90505f81608001516001600160801b031642612a899190614278565b90508015801590612aa6575060408201516001600160801b031615155b8015612abe575060608801516001600160a01b031615155b15612cb3576060888101805160408051638c00bf6b60e01b81528c516001600160a01b0390811660048301526020808f015182166024840152838f0151821660448401529451811660648301526080808f0151608484015288516001600160801b0390811660a485015295890151861660c484015292880151851660e483015294870151841661010482015290860151831661012482015260a08601519092166101448301525f921690638c00bf6b9061016401602060405180830381865afa158015612b8d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bb19190614206565b90505f612bd5612bc1838561374f565b60408601516001600160801b0316906137ad565b9050612be0816137c1565b84604001818151612bf191906145a1565b6001600160801b0316905250612c06816137c1565b84518590612c159083906145a1565b6001600160801b0390811690915260a086015116159050612cb0575f612c518560a001516001600160801b0316836137ad90919063ffffffff16565b90505f612c8582875f01516001600160801b0316612c6f9190614278565b60208801518491906001600160801b0316613816565b9050612c90816137c1565b86602001818151612ca191906145a1565b6001600160801b031690525050505b50505b508051602082015160408301516060909301516001600160801b039283169b9183169a509282169850911695509350505050565b5f610b87612cf6600185614231565b612d03620f424085614231565b86919061383a565b5f612d146114ec565b9050612d27612d228361385b565b613124565b612d2f61259b565b612d3e6126e6836110b061354d565b80612d476114ec565b612d519190614278565b82118015612d6557505f612d63610f66565b115b8015612d765750612d74610dde565b155b8015612d8d57505f5465010000000000900460ff16155b156114df57612d9a613916565b612da261259b565b6114df6126e661354d565b612db6816139df565b5f612dc26121076112ea565b90505f612dd0611928610f66565b90505f808311612de0575f612dfd565b82612df3670de0b6b3a76400008461428b565b612dfd91906142a2565b90505f612e08611bdf565b905081811115612e80575f670de0b6b3a7640000612e26838761428b565b612e3091906142a2565b90508381035f612e63612223837f0000000000000000000000000000000000000000000000000000000000000000611ca1565b9050600354811115612e7857612e7881613a3a565b505050612eef565b612e88613109565b821115612eef575f670de0b6b3a7640000612ea3868461428b565b612ead91906142a2565b9050612ee5612d22612ebf8387614278565b7f0000000000000000000000000000000000000000000000000000000000000000611ca1565b612eed61259b565b505b5f612ef861125f565b90508015612f0957612f0981613ad8565b505050505050565b5f80612f1b610a8f565b90508015612f5357612f50610b3c827f0000000000000000000000000000000000000000000000000000000000000000611c0a565b91505b5090565b6040516349e2903160e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906393c5206290604401606060405180830381865afa158015612fe3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613007919061431c565b905080602001516001600160801b03165f03613024575f91505090565b5f6ec097ce7bc90715b34b9f100000000060086002015f9054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613088573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130ac9190614206565b83604001516001600160801b03166130c4919061428b565b6130ce91906142a2565b90505f670de0b6b3a7640000600860040154836130eb919061428b565b6130f591906142a2565b905080613100610f66565b11935050505090565b5f8054600c5461271091600160401b900461ffff1690611bf6565b5f61312d61125f565b905081811015613146576131418183614278565b613148565b5f5b9150613156826110b0611b89565b9150815f03613163575050565b6114df826122ba565b805f036131765750565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b549091166060820152600c5460808201525f9081906131e8907f0000000000000000000000000000000000000000000000000000000000000000906129f1565b9350935050505f6132516131fd858585613816565b6110b06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000003061293f565b6040516320b76e8160e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906320b76e81906132a7906008905f90869030906004016145c8565b60408051808303815f875af11580156132c2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f09919061460e565b5f835f036132f557505f611d7b565b5f546127109061331690600160501b900467ffffffffffffffff1682614278565b6129226133238787611c0a565b85611ca1565b5f821580159061333b57506004548310155b15610b8757600654613357906001600160a01b03168685613b66565b6005546001600160a01b038681169116148061338057506005546001600160a01b038581169116145b156134535760408051610100810182526001600160a01b0380881680835287821660208085018290525f92835260078152858320918352528381205462ffffff168385015230606084015242608084015260a0830187905260c0830186905260e0830152600654925163414bf38960e01b81529192169063414bf3899061340b908490600401614630565b6020604051808303815f875af1158015613427573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061344b9190614206565b915050610b87565b6001600160a01b038086165f908152600760208181526040808420600554861680865290835281852054938352818520958a16855294825280842054905193946134ad948b9462ffffff90811694929316918b910161469a565b60408051601f1981840301815260065460a08401835281845230602085015242848401526060840188905260808401879052915163c04b8d5960e01b81529093506001600160a01b039091169163c04b8d599161350d919060040161473c565b6020604051808303815f875af1158015613529573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061110d9190614206565b5f806135576112ea565b90505f613562610f66565b9050805f036135715750919050565b5f61359c827f0000000000000000000000000000000000000000000000000000000000000000611c0a565b90505f6135bc6135aa611bdf565b611103670de0b6b3a76400008561428b565b9050838111156135d0575f94505050505090565b9092039392505050565b6060610b8784845f85613c05565b5f80600d5f9054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561363a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061365e9190614206565b90505f811361293a5760405162461bcd60e51b81526020600482015260016024820152600360fc1b60448201526064016108ff565b5f6001828460026040516020016136b4929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b0390941690830152810191909152606001604051602081830303815290604052805190602001205f1c611d7b9190614231565b6040805160018082528183019092526060915f91906020808301908036833701905050905082815f8151811061373e5761373e6144f0565b602090810291909101015292915050565b5f8061375b838561428b565b90505f61377b8280613776670de0b6b3a7640000600261428b565b613cd1565b90505f6137968284613776670de0b6b3a7640000600361428b565b9050806137a38385614231565b61110d9190614231565b5f611d7b8383670de0b6b3a7640000613cd1565b6040805180820190915260148152731b585e081d5a5b9d0c4c8e08195e18d95959195960621b60208201525f906001600160801b03831115611dd05760405162461bcd60e51b81526004016108ff919061474e565b5f610b87613827620f424084614231565b613832600186614231565b869190613cd1565b5f81613847600182614278565b613851858761428b565b61292c9190614231565b5f815f0361386a57505f919050565b5f6138736112ea565b905080831061388457611d7b610f66565b5f6138926121078584614278565b90505f670de0b6b3a76400006138a6611bdf565b6138b0908461428b565b6138ba91906142a2565b90505f6138e7827f0000000000000000000000000000000000000000000000000000000000000000611ca1565b90505f6138f2610f66565b9050808210613901575f61390b565b61390b8282614278565b979650505050505050565b5f61391f610a8f565b5f8054919250906127109061394590600160501b900467ffffffffffffffff1682614231565b613972610b3c857f0000000000000000000000000000000000000000000000000000000000000000611c0a565b61397c919061428b565b61398691906142a2565b9050805f03613993575050565b610dd97f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008484613cdd565b805f036139e95750565b60405163238d657960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063238d6579906124c29060089085903090600401614760565b805f03613a445750565b6040516350d8cd4b60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906350d8cd4b90613a999060089085905f90309081906004016147a0565b60408051808303815f875af1158015613ab4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd9919061460e565b604051636e553f6560e01b8152600481018290523060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636e553f65906044016020604051808303815f875af1158015613b42573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114df9190614206565b604051636eb1769f60e11b81523060048201526001600160a01b03848116602483015282919084169063dd62ed3e90604401602060405180830381865afa158015613bb3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bd79190614206565b1015610dd957613bf16001600160a01b038316845f613e7d565b610dd96001600160a01b0383168483613e7d565b606082471015613c665760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016108ff565b5f80866001600160a01b03168587604051613c8191906147dc565b5f6040518083038185875af1925050503d805f8114613cbb576040519150601f19603f3d011682016040523d82523d5f602084013e613cc0565b606091505b509150915061390b87838387613f11565b5f8161292c848661428b565b5f8115801590613cef57506004548210155b15610b8757600654613d0b906001600160a01b03168684613b66565b6005546001600160a01b0386811691161480613d3457506005546001600160a01b038581169116145b15613dbf5760408051610100810182526001600160a01b0380881680835287821660208085018290525f92835260078152858320918352528381205462ffffff168385015230606084015242608084015260a0830187905260c0830186905260e08301526006549251631b67c43360e31b81529192169063db3e21989061340b908490600401614630565b6005546001600160a01b039081165f818152600760208181526040808420868b168552825280842054958b16845291815281832084845281528183205491519294613e1d948a9462ffffff92831694919391909216918c910161469a565b60408051601f1981840301815260065460a084018352818452306020850152428484015260608401889052608084018790529151631e51809360e31b81529093506001600160a01b039091169163f28c04989161350d919060040161473c565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613ece8482613f89565b613f0b576040516001600160a01b03841660248201525f6044820152613f0190859063095ea7b360e01b90606401611b52565b613f0b84826126eb565b50505050565b60608315613f7f5782515f03613f78576001600160a01b0385163b613f785760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108ff565b5081610b87565b610b87838361402a565b5f805f846001600160a01b031684604051613fa491906147dc565b5f604051808303815f865af19150503d805f8114613fdd576040519150601f19603f3d011682016040523d82523d5f602084013e613fe2565b606091505b509150915081801561400c57508051158061400c57508080602001905181019061400c91906143f1565b801561402157506001600160a01b0385163b15155b95945050505050565b81511561403a5781518083602001fd5b8060405162461bcd60e51b81526004016108ff919061474e565b80356001600160a01b038116811461293a575f80fd5b5f6020828403121561407a575f80fd5b611d7b82614054565b5f60208284031215614093575f80fd5b5035919050565b5f805f606084860312156140ac575f80fd5b6140b584614054565b92506140c360208501614054565b9150604084013562ffffff811681146140da575f80fd5b809150509250925092565b8015158114610a8c575f80fd5b5f60208284031215614102575f80fd5b8135611d7b816140e5565b803561ffff8116811461293a575f80fd5b5f806040838503121561412f575f80fd5b6141388361410d565b91506141466020840161410d565b90509250929050565b5f5b83811015614169578181015183820152602001614151565b50505f910152565b5f815180845261418881602086016020860161414f565b601f01601f19169290920160200192915050565b8215158152604060208201525f610b876040830184614171565b5f80604083850312156141c7575f80fd5b6141d083614054565b946020939093013593505050565b5f80604083850312156141ef575f80fd5b6141f883614054565b915061414660208401614054565b5f60208284031215614216575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115611c9b57611c9b61421d565b634e487b7160e01b5f52601260045260245ffd5b5f60208284031215614268575f80fd5b815160ff81168114611d7b575f80fd5b81810381811115611c9b57611c9b61421d565b8082028115828204841417611c9b57611c9b61421d565b5f826142bc57634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156142fe576142fe6142c1565b604052919050565b80516001600160801b038116811461293a575f80fd5b5f6060828403121561432c575f80fd5b6040516060810181811067ffffffffffffffff8211171561434f5761434f6142c1565b6040528251815261436260208401614306565b602082015261437360408401614306565b60408201529392505050565b80546001600160a01b039081168352600182015481166020840152600282015481166040840152600382015416606083015260040154608090910152565b61010081016143cc828761437f565b60a08201949094526001600160a01b0392831660c0820152911660e090910152919050565b5f60208284031215614401575f80fd5b8151611d7b816140e5565b602080825282518282018190525f9190848201906040850190845b8181101561444357835183529284019291840191600101614427565b50909695505050505050565b5f6020808385031215614460575f80fd5b825167ffffffffffffffff80821115614477575f80fd5b818501915085601f83011261448a575f80fd5b81518181111561449c5761449c6142c1565b8060051b91506144ad8483016142d5565b81815291830184019184810190888411156144c6575f80fd5b938501935b838510156144e4578451825293850193908501906144cb565b98975050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60c08284031215614514575f80fd5b60405160c0810181811067ffffffffffffffff82111715614537576145376142c1565b60405261454383614306565b815261455160208401614306565b602082015261456260408401614306565b604082015261457360608401614306565b606082015261458460808401614306565b608082015261459560a08401614306565b60a08201529392505050565b6001600160801b038181168382160190808211156145c1576145c161421d565b5092915050565b5f6101206145d6838861437f565b60a08301959095525060c08101929092526001600160a01b031660e082015261010081018290525f9181019190915261014001919050565b5f806040838503121561461f575f80fd5b505080516020909101519092909150565b6101008101611c9b828480516001600160a01b03908116835260208083015182169084015260408083015162ffffff16908401526060808301518216908401526080808301519084015260a0828101519084015260c0808301519084015260e09182015116910152565b6bffffffffffffffffffffffff19606096871b811682526001600160e81b031960e896871b8116601484015294871b811660178301529290941b909216602b840152921b909116602e82015260420190565b5f815160a0845261470060a0850182614171565b6020848101516001600160a01b031690860152604080850151908601526060808501519086015260809384015193909401929092525090919050565b602081525f611d7b60208301846146ec565b602081525f611d7b6020830184614171565b5f61010061476e838761437f565b60a0830194909452506001600160a01b039190911660c082015260e081018290525f9181019190915261012001919050565b61012081016147af828861437f565b60a082019590955260c08101939093526001600160a01b0391821660e08401521661010090910152919050565b5f82516147ed81846020870161414f565b919091019291505056fea264697066735822122036e1c05f22693a25fcf2164d78ddf6b5a361cfbf84dfeb52c15642d4ae9aafa964736f6c63430008170033a2646970667358221220124bf9188917a632c38071a8dc8af8a8210488057bd927a01407d971a1ae31a564736f6c634300081700330000000000000000000000001b5f15dcb82d25f91c65b53cee151e8b9fbdd2710000000000000000000000005a74cb32d36f2f517db6f7b0a0591e09b22cde69000000000000000000000000604e586f17ce106b64185a7a0d2c1da5bace711e00000000000000000000000016388463d60ffe0661cf7f1f31a7d658ac790ff7000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff52000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564

Deployed Bytecode

0x608060405234801562000010575f80fd5b5060043610620000a8575f3560e01c806388a8d602116200006b57806388a8d602146200015f578063aced16611462000172578063d8fbc8331462000186578063ed27f7c914620001ae578063f887ea4014620001c2575f80fd5b80630b18f8a514620000ac578063180cb47f14620000df5780633740401714620001075780634a945f8d146200013257806370905dce146200014b575b5f80fd5b620000c3620000bd36600462000552565b620001ea565b6040516001600160a01b03909116815260200160405180910390f35b620000c37f000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff5281565b620000c36200011836600462000615565b60046020525f90815260409020546001600160a01b031681565b62000149620001433660046200062d565b6200048d565b005b600354620000c3906001600160a01b031681565b5f54620000c3906001600160a01b031681565b600254620000c3906001600160a01b031681565b620000c37f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb81565b600154620000c3906001600160a01b031681565b620000c37f000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156481565b5f828152600460205260408120546001600160a01b0316156200020b575f80fd5b5f88888888887f000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff527f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb8a8a7f000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564604051620002849062000528565b620002999a9998979695949392919062000687565b604051809103905ff080158015620002b3573d5f803e3d5ffd5b5060015460405163352f8d5160e11b81526001600160a01b039182166004820152919250821690636a5f1aa2906024015f604051808303815f87803b158015620002fb575f80fd5b505af11580156200030e573d5f803e3d5ffd5b5050600254604051633a43a3f360e11b81526001600160a01b039182166004820152908416925063748747e691506024015f604051808303815f87803b15801562000357575f80fd5b505af11580156200036a573d5f803e3d5ffd5b50505f54604051630f629b7960e41b81526001600160a01b039182166004820152908416925063f629b79091506024015f604051808303815f87803b158015620003b2575f80fd5b505af1158015620003c5573d5f803e3d5ffd5b5050600354604051630d768ce560e21b81526001600160a01b03918216600482015290841692506335da339491506024015f604051808303815f87803b1580156200040e575f80fd5b505af115801562000421573d5f803e3d5ffd5b50506040518692506001600160a01b03841691507f0df9ebba034d173a99a9f19984c4f6b69c45f86fdf65be5234f4040127fb1b17905f90a35f9384526004602052604090932080546001600160a01b0319166001600160a01b03851617905550909695505050505050565b5f546001600160a01b03163314620004d95760405162461bcd60e51b815260206004820152600b60248201526a085b585b9859d95b595b9d60aa1b604482015260640160405180910390fd5b5f80546001600160a01b039586166001600160a01b0319918216179091556001805494861694821694909417909355600280549285169284169290921790915560038054919093169116179055565b6158a7806200070883390190565b80356001600160a01b03811681146200054d575f80fd5b919050565b5f805f805f805f60c0888a03121562000569575f80fd5b620005748862000536565b9650602088013567ffffffffffffffff8082111562000591575f80fd5b818a0191508a601f830112620005a5575f80fd5b813581811115620005b4575f80fd5b8b6020828501011115620005c6575f80fd5b602083019850809750505050620005e06040890162000536565b9350620005f06060890162000536565b9250608088013591506200060760a0890162000536565b905092959891949750929550565b5f6020828403121562000626575f80fd5b5035919050565b5f805f806080858703121562000641575f80fd5b6200064c8562000536565b93506200065c6020860162000536565b92506200066c6040860162000536565b91506200067c6060860162000536565b905092959194509250565b6001600160a01b038b811682526101206020830181905282018a90525f90610140908b8d838601375f848d01830152998a16604084015297891660608301525094871660808601529290951660a084015260c08301526001600160a01b0393841660e0830152909216610100830152601f909201601f191601019291505056fe6101a06040525f80546227100162ffffff19909116179055600580546001600160a01b031990811673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2179091556006805490911673e592427a0aece92de3edee1f18e0157c058615641790553480156200006b575f80fd5b50604051620058a7380380620058a78339810160408190526200008e9162000a73565b6001600160a01b0389166080523060a052604051899089908990899084908490829082906200010390620000cf908490849033908190819060240162000bc4565b60408051601f198184030181529190526020810180516001600160e01b03908116634b839d7360e11b179091526200056716565b505073d377919fa87120584b21279a491f82d5265a139c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc555050506001600160a01b0382811660c0525f196001555f8054642e90edd000600255600160281b600160901b0319166b01f41f401b580000000000001790558116156200026e57806001600160a01b031660e0816001600160a01b031681525050816001600160a01b031660e0516001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001e6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200020c919062000c06565b6001600160a01b031614620002575760405162461bcd60e51b815260206004820152600c60248201526b085b195b99195c95985d5b1d60a21b60448201526064015b60405180910390fd5b6200026e6001600160a01b038316825f19620005f6565b505050506001600160a01b03858116610140528416610100819052610120849052604051632c3c915760e01b815260048101859052632c3c91579060240160a060405180830381865afa158015620002c8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620002ee919062000c22565b8051600880546001600160a01b03199081166001600160a01b0393841690811790925560208401516009805483169185169190911790556040840151600a805483169185169190911790556060840151600b8054909216908416179055608090920151600c5588161480156200037157506009546001600160a01b038a81169116145b6200037a575f80fd5b886001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003b7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620003dd919062000cb5565b620003ea90600a62000de4565b6101608181525050866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200042f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000455919062000cb5565b6200046290600a62000de4565b610180526200047d6001600160a01b038a16855f19620005f6565b620004946001600160a01b038816855f19620005f6565b620004a0612710600455565b8060065f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000503573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000529919062000cb5565b60ff1660081462000538575f80fd5b50600d80546001600160a01b0319166001600160a01b03929092169190911790555062000e4695505050505050565b60605f8073d377919fa87120584b21279a491f82d5265a139c6001600160a01b03168460405162000599919062000df4565b5f60405180830381855af49150503d805f8114620005d3576040519150601f19603f3d011682016040523d82523d5f602084013e620005d8565b606091505b509150915081620005ef576040513d805f833e8082fd5b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152620006509085908390620006cb16565b620006c5576040516001600160a01b03841660248201525f6044820152620006b990859063095ea7b360e01b9060640160408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b03938416179052906200077716565b620006c5848262000777565b50505050565b5f805f846001600160a01b031684604051620006e8919062000df4565b5f604051808303815f865af19150503d805f811462000723576040519150601f19603f3d011682016040523d82523d5f602084013e62000728565b606091505b5091509150818015620007565750805115806200075657508080602001905181019062000756919062000e11565b80156200076c57506001600160a01b0385163b15155b925050505b92915050565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201525f90620007c5906001600160a01b0385169084906200084e565b905080515f1480620007e8575080806020019051810190620007e8919062000e11565b620008495760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016200024e565b505050565b60606200085e84845f8562000866565b949350505050565b606082471015620008c95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016200024e565b5f80866001600160a01b03168587604051620008e6919062000df4565b5f6040518083038185875af1925050503d805f811462000922576040519150601f19603f3d011682016040523d82523d5f602084013e62000927565b606091505b5090925090506200093b8783838762000946565b979650505050505050565b60608315620009b95782515f03620009b1576001600160a01b0385163b620009b15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200024e565b50816200085e565b6200085e8383815115620009d05781518083602001fd5b8060405162461bcd60e51b81526004016200024e919062000e32565b80516001600160a01b038116811462000a03575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171562000a475762000a4762000a08565b604052919050565b5f5b8381101562000a6b57818101518382015260200162000a51565b50505f910152565b5f805f805f805f805f6101208a8c03121562000a8d575f80fd5b62000a988a620009ec565b60208b01519099506001600160401b038082111562000ab5575f80fd5b818c0191508c601f83011262000ac9575f80fd5b81518181111562000ade5762000ade62000a08565b62000af3601f8201601f191660200162000a1c565b91508082528d602082850101111562000b0a575f80fd5b62000b1d81602084016020860162000a4f565b50985062000b30905060408b01620009ec565b965062000b4060608b01620009ec565b955062000b5060808b01620009ec565b945062000b6060a08b01620009ec565b935060c08a0151925062000b7760e08b01620009ec565b915062000b886101008b01620009ec565b90509295985092959850929598565b5f815180845262000bb081602086016020860162000a4f565b601f01601f19169290920160200192915050565b5f60018060a01b03808816835260a0602084015262000be760a084018862000b97565b9581166040840152938416606083015250911660809091015292915050565b5f6020828403121562000c17575f80fd5b620005ef82620009ec565b5f60a0828403121562000c33575f80fd5b60405160a081016001600160401b038111828210171562000c585762000c5862000a08565b60405262000c6683620009ec565b815262000c7660208401620009ec565b602082015262000c8960408401620009ec565b604082015262000c9c60608401620009ec565b6060820152608083015160808201528091505092915050565b5f6020828403121562000cc6575f80fd5b815160ff81168114620005ef575f80fd5b634e487b7160e01b5f52601160045260245ffd5b600181815b8085111562000d2b57815f190482111562000d0f5762000d0f62000cd7565b8085161562000d1d57918102915b93841c939080029062000cf0565b509250929050565b5f8262000d435750600162000771565b8162000d5157505f62000771565b816001811462000d6a576002811462000d755762000d95565b600191505062000771565b60ff84111562000d895762000d8962000cd7565b50506001821b62000771565b5060208310610133831016604e8410600b841016171562000dba575081810a62000771565b62000dc6838362000ceb565b805f190482111562000ddc5762000ddc62000cd7565b029392505050565b5f620005ef60ff84168362000d33565b5f825162000e0781846020870162000a4f565b9190910192915050565b5f6020828403121562000e22575f80fd5b81518015158114620005ef575f80fd5b602081525f620005ef602083018462000b97565b60805160a05160c05160e051610100516101205161014051610160516101805161482d6200107a5f395f6128d301525f6128f901525f8181610444015281816108a90152610a0801525f8181610612015281816112f901528181612f66015261322c01525f818161079801528181610fb00152818161133101528181611e730152818161248601528181612f9e015281816131c30152818161320a0152818161326b01528181613a000152613a5b01525f81816103f10152818161098501528181610df501528181611ba001528181611dee015281816122de0152818161235c015281816123f30152613af401525f81816105160152818161094701528181610b18015281816110d5015281816112760152818161149f0152818161192a015281816121ff015281816126250152818161266c015281816127c101528181612e3f01528181612ec101528181612f2c01528181613578015281816138c30152818161394e01526139b901525f8181610ba401528181610ca701528181610d2301528181610ee601528181610fee0152818161115c015281816113c20152818161143f0152818161154f015281816115cb01528181611657015281816116dd01528181611758015281816117ee015281816118690152818161197801528181611a4e01528181611f2b015261207201525f818161090a01528181610b3e01528181611503015281816118f001528181612109015281816126460152818161268d015281816128020152613998015261482d5ff3fe608060405234801561000f575f80fd5b5060043610610376575f3560e01c80636ed71ede116101d1578063bdc8144b11610102578063ebf27802116100a0578063f0fa55a91161007a578063f0fa55a914610822578063f3ce280a14610835578063f887ea4014610878578063fde813a81461088b57610376565b8063ebf27802146107f3578063ecf7085814610806578063ee8bfa311461080f57610376565b8063d8fbc833116100dc578063d8fbc83314610793578063da769c47146107ba578063e4d746fb146107cf578063e8621149146107eb57610376565b8063bdc8144b14610752578063d19a3bb814610765578063d69686011461078057610376565b8063950b3d731161016f578063ac00ff2611610149578063ac00ff261461071b578063b1ea668d1461072e578063b2a1913714610736578063b6a165061461074957610376565b8063950b3d73146106e25780639b90fb16146106f55780639d7fb70c1461070857610376565b8063797bf343116101ab578063797bf3431461064f5780637b9e68f2146106575780637d969932146106bc5780638e72e31b146106ce57610376565b80636ed71ede1461060d578063702651db14610634578063735fd1891461063c57610376565b80633e032a3b116102ab57806355b90362116102495780636353f822116102235780636353f822146105ba5780636687500e146105e15780636718835f146105e95780636be36a1d1461060557610376565b806355b90362146105895780635bc34efe1461059c5780635d265d3f146105a457610376565b806349317f1d1161028557806349317f1d1461054b5780634a5d0943146105535780635001f3b514610563578063503160d91461057657610376565b80633e032a3b146104de578063456dc17a1461051157806346aa2f121461053857610376565b8063278b73121161031857806331b7f9b2116102f257806331b7f9b2146104a857806338dbd359146104b0578063392f7a70146104c35780633d6cb575146104cb57610376565b8063278b73121461046f5780632d49be91146104825780632e3ecf2c1461049557610376565b806306876d191161035457806306876d19146103ec578063085c884f1461042b578063180cb47f1461043f578063181443671461046657610376565b806301681a62146103ab57806302402ae0146103be57806304bd4629146103d9575b73d377919fa87120584b21279a491f82d5265a139c365f80375f80365f845af43d5f803e8080156103a5573d5ff35b3d5ffd5b005b6103a96103b936600461406a565b61089e565b6103c6610a8f565b6040519081526020015b60405180910390f35b6103c66103e736600461406a565b610acd565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103d0565b6103c6610439366004614083565b50600190565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6103c660045481565b6103a961047d36600461406a565b610b8f565b6103a9610490366004614083565b610c92565b6103a96104a336600461409a565b610d0e565b6103c6610dde565b6103a96104be3660046140f2565b610ed1565b6103c6610f66565b6103a96104d9366004614083565b610fd9565b5f546104f890600160501b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103d0565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6103c661054636600461406a565b610fea565b6103c6611117565b5f54610100900461ffff166103c6565b600554610413906001600160a01b031681565b6103a9610584366004614083565b611136565b6103a961059736600461411e565b611147565b6103c661125f565b6105ac6112ad565b6040516103d092919061419c565b5f546105ce90600160401b900461ffff1681565b60405161ffff90911681526020016103d0565b6103c66112ea565b5f546105f59060ff1681565b60405190151581526020016103d0565b600c546103c6565b6103c67f000000000000000000000000000000000000000000000000000000000000000081565b6103a96113ad565b6103a961064a3660046141b6565b61142a565b6103c66114ec565b600854600954600a54600b54600c54610681946001600160a01b0390811694811693811692169085565b604080516001600160a01b0396871681529486166020860152928516928401929092529092166060820152608081019190915260a0016103d0565b5f546301000000900461ffff166103c6565b5f546105ce90600160301b900461ffff1681565b6103a96106f0366004614083565b61153a565b6103a9610703366004614083565b6115b6565b6103a9610716366004614083565b611631565b6103a96107293660046140f2565b611642565b6103a96116c8565b6103a961074436600461406a565b611743565b6103c660025481565b6103a9610760366004614083565b6117d9565b61041373d377919fa87120584b21279a491f82d5265a139c81565b6103a961078e366004614083565b611854565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b5f546105f59065010000000000900460ff1681565b6103c66107dd366004614083565b5068056bc75e2d6310000090565b6103c66118d0565b6103a9610801366004614083565b611963565b6103c660015481565b600d54610413906001600160a01b031681565b6103a9610830366004614083565b611a39565b6108646108433660046141de565b600760209081525f928352604080842090915290825290205462ffffff1681565b60405162ffffff90911681526020016103d0565b600654610413906001600160a01b031681565b6103a9610899366004614083565b611b15565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109085760405162461bcd60e51b81526004016108ff9060208082526004908201526310b3b7bb60e11b604082015260600190565b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b03161415801561097c57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b80156109ba57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b6109ef5760405162461bcd60e51b815260206004820152600660248201526502173776565760d41b60448201526064016108ff565b6040516370a0823160e01b8152306004820152610a8c907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b038416906370a0823190602401602060405180830381865afa158015610a57573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a7b9190614206565b6001600160a01b0384169190611b26565b50565b5f80610a9961125f565b610aa1610dde565b610aab9190614231565b90505f610ab6610f66565b9050808210610ac7575f9250505090565b03919050565b5f80610ad76112ea565b610ae2906001614231565b90505f610aed611b89565b9050610af7610dde565b811015610b7457610b06611bdf565b670de0b6b3a7640000610b62610b3c847f0000000000000000000000000000000000000000000000000000000000000000611c0a565b7f0000000000000000000000000000000000000000000000000000000000000000611ca1565b0281610b7057610b70614244565b0491505b81610b7d6114ec565b610b879190614231565b949350505050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610bec575f80fd5b505afa158015610bfe573d5f803e3d5ffd5b50505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c3e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c629190614258565b60ff16600814610c70575f80fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610cef575f80fd5b505afa158015610d01573d5f803e3d5ffd5b50505050610a8c81600455565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610d6b575f80fd5b505afa158015610d7d573d5f803e3d5ffd5b5050506001600160a01b038085165f8181526007602081815260408084209589168452948152848320805462ffffff1990811662ffffff8a16908117909255928252858420948452939052929020805490921617905550505050565b505050565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a9082906370a08231906024015b602060405180830381865afa158015610e4b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e6f9190614206565b6040518263ffffffff1660e01b8152600401610e8d91815260200190565b602060405180830381865afa158015610ea8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ecc9190614206565b905090565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610f2e575f80fd5b505afa158015610f40573d5f803e3d5ffd5b50505f8054931515650100000000000265ff000000000019909416939093179092555050565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b5482166060820152600c5460808201525f91610ecc917f00000000000000000000000000000000000000000000000000000000000000009091169030611d2e565b610fe1611d82565b610a8c81611db9565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611048573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061106c9190614206565b90505f816001541161107e575f61108c565b8160015461108c9190614278565b90505f61109a5f1983611dc2565b90505f6110b56110a8611dd7565b6110b0611e25565b611dc2565b905061110d826110b06110c6611bdf565b670de0b6b3a76400006110f9867f0000000000000000000000000000000000000000000000000000000000000000611c0a565b611103919061428b565b610b3c91906142a2565b9695505050505050565b5f611120611d82565b611128611ebf565b905061113381611f10565b90565b61113e611d82565b610a8c81612053565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156111a4575f80fd5b505afa1580156111b6573d5f803e3d5ffd5b505050506123288161ffff16111580156111d757508061ffff168261ffff16105b80156111e6575061ffff821615155b6112205760405162461bcd60e51b815260206004820152600b60248201526a34b73b30b634b210262a2b60a91b60448201526064016108ff565b5f805469ffffffff0000000000001916600160301b61ffff9485160269ffff0000000000000000191617600160401b9290931691909102919091179055565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401610e8d565b5f60606112b861205c565b6040805160048152602481019091526020810180516001600160e01b031663440368a360e01b17905290939092509050565b6040516349e2903160e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906393c5206290604401606060405180830381865afa158015611376573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061139a919061431c565b604001516001600160801b031692915050565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b15801561140a575f80fd5b505afa15801561141c573d5f803e3d5ffd5b50505050611428612260565b565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b158015611487575f80fd5b505afa158015611499573d5f803e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036114e3576114df816122ba565b5050565b6114df81612465565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401610e8d565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611597575f80fd5b505afa1580156115a9573d5f803e3d5ffd5b50505050610a8c816124f2565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611613575f80fd5b505afa158015611625573d5f803e3d5ffd5b50505060029190915550565b611639611d82565b610a8c8161258b565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b15801561169f575f80fd5b505afa1580156116b1573d5f803e3d5ffd5b50505f805460ff1916931515939093179092555050565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b158015611725575f80fd5b505afa158015611737573d5f803e3d5ffd5b5050505061142861259b565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156117a0575f80fd5b505afa1580156117b2573d5f803e3d5ffd5b5050600580546001600160a01b0319166001600160a01b0394909416939093179092555050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611836575f80fd5b505afa158015611848573d5f803e3d5ffd5b50505060019190915550565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156118b1575f80fd5b505afa1580156118c3573d5f803e3d5ffd5b50505050610a8c816125b6565b5f806118da6112ea565b9050805f036118ea575f91505090565b611914817f0000000000000000000000000000000000000000000000000000000000000000611c0a565b670de0b6b3a764000061194e611928610f66565b7f0000000000000000000000000000000000000000000000000000000000000000611c0a565b028161195c5761195c614244565b0491505090565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b1580156119c0575f80fd5b505afa1580156119d2573d5f803e3d5ffd5b505050505f198103611a30575f6119e761125f565b90505f816119f3610dde565b6119fd9190614231565b90505f611a08610f66565b9050808211611a17575f611a2a565b611a2a611a248284614278565b84611dc2565b93505050505b610a8c81612616565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611a96575f80fd5b505afa158015611aa8573d5f803e3d5ffd5b505050506127108110611ae85760405162461bcd60e51b8152602060048201526008602482015267736c69707061676560c01b60448201526064016108ff565b5f805467ffffffffffffffff909216600160501b0267ffffffffffffffff60501b19909216919091179055565b611b1d611d82565b610a8c816126b6565b6040516001600160a01b038316602482015260448101829052610dd990849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526126eb565b604051636c82bbbf60e11b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a90829063d905777e90602401610e30565b5f8054600c5461271091600160301b900461ffff16905b0281611c0457611c04614244565b04905090565b5f825f03611c1957505f611c9b565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c55573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c799190614258565b60ff16600a0a611c88836127be565b840281611c9757611c97614244565b0490505b92915050565b5f825f03611cb057505f611c9b565b611cb9826127be565b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d199190614258565b60ff16600a0a840281611c9757611c97614244565b5f80611d3b8460a0902090565b90505f611d526001600160a01b038716838661293f565b90505f80611d6088886129f1565b9094509250611d7491508490508383612ce7565b9450505050505b9392505050565b3330146114285760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b60448201526064016108ff565b610a8c81612d0b565b5f818310611dd05781611d7b565b5090919050565b60405163402d267d60e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063402d267d90602401610e8d565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b5482166060820152600c5460808201525f9182918291611e9b917f0000000000000000000000000000000000000000000000000000000000000000909116906129f1565b509250509150808211611eae575f611eb8565b611eb88183614278565b9250505090565b5f611ec8612260565b611ee4611edf611ed66114ec565b6110b030610fea565b612dad565b611eec612f11565b611ef46112ea565b611efc6114ec565b611f069190614231565b610ecc9190614278565b5f5460ff16611f2857505f805460ff19166001179055565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fa99190614206565b90508082111561201c575f5461271090611fcc90610100900461ffff168361428b565b611fd691906142a2565b611fe08284614278565b11156114df5760405162461bcd60e51b815260206004820152600b60248201526a6865616c7468436865636b60a81b60448201526064016108ff565b818111156114df575f546127109061203f906301000000900461ffff168361428b565b61204991906142a2565b611fe08383614278565b610a8c81612dad565b5f612065612f57565b156120705750600190565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120cc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120f09190614206565b5f036120fb57505f90565b5f61212d6121076112ea565b7f0000000000000000000000000000000000000000000000000000000000000000611c0a565b90505f61213b611928610f66565b90505f80831161214b575f612168565b8261215e670de0b6b3a76400008461428b565b61216891906142a2565b9050612172613109565b811115612183576001935050505090565b5f61218c611bdf565b9050811580159061219a57505f5b156121ae5760025448111594505050505090565b80821080156121cd575067016345785d8a00006121cb8383614278565b115b15612256575f670de0b6b3a76400006121e6838761428b565b6121f091906142a2565b90508381035f61222e612223837f0000000000000000000000000000000000000000000000000000000000000000611ca1565b6110b06110a8611dd7565b9050805f03612244575f97505050505050505090565b60025448111597505050505050505090565b5f94505050505090565b5f61226961125f565b612271610dde565b61227b9190614231565b90505f612286610f66565b9050808211156114df575f61229b8284614278565b90506122a681613124565b610dd96122b5826110b061125f565b612616565b604051630a28a47760e01b8152600481018290525f906123cd906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630a28a47790602401602060405180830381865afa158015612323573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123479190614206565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156123a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b09190614206565b604051635d043b2960e11b815260048101829052306024820181905260448201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ba087652906064016020604051808303815f875af1158015612441573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd99190614206565b805f0361246f5750565b604051638720316d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638720316d906124c2906008908590309081906004016143bd565b5f604051808303815f87803b1580156124d9575f80fd5b505af11580156124eb573d5f803e3d5ffd5b5050505050565b5f81116125305760405162461bcd60e51b815260206004820152600c60248201526b085e995c9bc81c1c9bd99a5d60a21b60448201526064016108ff565b61ffff81111561256e5760405162461bcd60e51b8152602060048201526009602482015268042e8dede40d0d2ced60bb1b60448201526064016108ff565b5f805461ffff9092166101000262ffff0019909216919091179055565b610a8c611edf826110b030610fea565b6114286125b16125a961125f565b6110b0610f66565b61316c565b61271081106125f55760405162461bcd60e51b815260206004820152600b60248201526a085b1bdcdcc81b1a5b5a5d60aa1b60448201526064016108ff565b5f805461ffff90921663010000000264ffff00000019909216919091179055565b805f036126205750565b6114df7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000836126b1857f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006132e6565b613329565b6126c2816110b0611b89565b905080156126d3576126d3816122ba565b6126db61259b565b610a8c6126e661354d565b612465565b5f61273f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135da9092919063ffffffff16565b905080515f148061275f57508080602001905181019061275f91906143f1565b610dd95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108ff565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361280057611c9b6135e8565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031603612936575f6128426135e8565b90505f60086002015f9054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612898573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128bc9190614206565b90506128f76ec097ce7bc90715b34b9f10000000007f000000000000000000000000000000000000000000000000000000000000000061428b565b7f0000000000000000000000000000000000000000000000000000000000000000612922848461428b565b61292c919061428b565b610b8791906142a2565b5f80fd5b919050565b5f8061295361294e8585613693565b613706565b604051637784c68560e01b81529091506001600160a01b03861690637784c6859061298290849060040161440c565b5f60405180830381865afa15801561299c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526129c3919081019061444f565b5f815181106129d4576129d46144f0565b60200260200101515f1c6001600160801b03169150509392505050565b5f805f805f612a018660a0902090565b604051632e3071cd60e11b8152600481018290529091505f906001600160a01b03891690635c60e39a9060240160c060405180830381865afa158015612a49573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a6d9190614504565b90505f81608001516001600160801b031642612a899190614278565b90508015801590612aa6575060408201516001600160801b031615155b8015612abe575060608801516001600160a01b031615155b15612cb3576060888101805160408051638c00bf6b60e01b81528c516001600160a01b0390811660048301526020808f015182166024840152838f0151821660448401529451811660648301526080808f0151608484015288516001600160801b0390811660a485015295890151861660c484015292880151851660e483015294870151841661010482015290860151831661012482015260a08601519092166101448301525f921690638c00bf6b9061016401602060405180830381865afa158015612b8d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bb19190614206565b90505f612bd5612bc1838561374f565b60408601516001600160801b0316906137ad565b9050612be0816137c1565b84604001818151612bf191906145a1565b6001600160801b0316905250612c06816137c1565b84518590612c159083906145a1565b6001600160801b0390811690915260a086015116159050612cb0575f612c518560a001516001600160801b0316836137ad90919063ffffffff16565b90505f612c8582875f01516001600160801b0316612c6f9190614278565b60208801518491906001600160801b0316613816565b9050612c90816137c1565b86602001818151612ca191906145a1565b6001600160801b031690525050505b50505b508051602082015160408301516060909301516001600160801b039283169b9183169a509282169850911695509350505050565b5f610b87612cf6600185614231565b612d03620f424085614231565b86919061383a565b5f612d146114ec565b9050612d27612d228361385b565b613124565b612d2f61259b565b612d3e6126e6836110b061354d565b80612d476114ec565b612d519190614278565b82118015612d6557505f612d63610f66565b115b8015612d765750612d74610dde565b155b8015612d8d57505f5465010000000000900460ff16155b156114df57612d9a613916565b612da261259b565b6114df6126e661354d565b612db6816139df565b5f612dc26121076112ea565b90505f612dd0611928610f66565b90505f808311612de0575f612dfd565b82612df3670de0b6b3a76400008461428b565b612dfd91906142a2565b90505f612e08611bdf565b905081811115612e80575f670de0b6b3a7640000612e26838761428b565b612e3091906142a2565b90508381035f612e63612223837f0000000000000000000000000000000000000000000000000000000000000000611ca1565b9050600354811115612e7857612e7881613a3a565b505050612eef565b612e88613109565b821115612eef575f670de0b6b3a7640000612ea3868461428b565b612ead91906142a2565b9050612ee5612d22612ebf8387614278565b7f0000000000000000000000000000000000000000000000000000000000000000611ca1565b612eed61259b565b505b5f612ef861125f565b90508015612f0957612f0981613ad8565b505050505050565b5f80612f1b610a8f565b90508015612f5357612f50610b3c827f0000000000000000000000000000000000000000000000000000000000000000611c0a565b91505b5090565b6040516349e2903160e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906393c5206290604401606060405180830381865afa158015612fe3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613007919061431c565b905080602001516001600160801b03165f03613024575f91505090565b5f6ec097ce7bc90715b34b9f100000000060086002015f9054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613088573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130ac9190614206565b83604001516001600160801b03166130c4919061428b565b6130ce91906142a2565b90505f670de0b6b3a7640000600860040154836130eb919061428b565b6130f591906142a2565b905080613100610f66565b11935050505090565b5f8054600c5461271091600160401b900461ffff1690611bf6565b5f61312d61125f565b905081811015613146576131418183614278565b613148565b5f5b9150613156826110b0611b89565b9150815f03613163575050565b6114df826122ba565b805f036131765750565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b549091166060820152600c5460808201525f9081906131e8907f0000000000000000000000000000000000000000000000000000000000000000906129f1565b9350935050505f6132516131fd858585613816565b6110b06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000003061293f565b6040516320b76e8160e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906320b76e81906132a7906008905f90869030906004016145c8565b60408051808303815f875af11580156132c2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f09919061460e565b5f835f036132f557505f611d7b565b5f546127109061331690600160501b900467ffffffffffffffff1682614278565b6129226133238787611c0a565b85611ca1565b5f821580159061333b57506004548310155b15610b8757600654613357906001600160a01b03168685613b66565b6005546001600160a01b038681169116148061338057506005546001600160a01b038581169116145b156134535760408051610100810182526001600160a01b0380881680835287821660208085018290525f92835260078152858320918352528381205462ffffff168385015230606084015242608084015260a0830187905260c0830186905260e0830152600654925163414bf38960e01b81529192169063414bf3899061340b908490600401614630565b6020604051808303815f875af1158015613427573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061344b9190614206565b915050610b87565b6001600160a01b038086165f908152600760208181526040808420600554861680865290835281852054938352818520958a16855294825280842054905193946134ad948b9462ffffff90811694929316918b910161469a565b60408051601f1981840301815260065460a08401835281845230602085015242848401526060840188905260808401879052915163c04b8d5960e01b81529093506001600160a01b039091169163c04b8d599161350d919060040161473c565b6020604051808303815f875af1158015613529573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061110d9190614206565b5f806135576112ea565b90505f613562610f66565b9050805f036135715750919050565b5f61359c827f0000000000000000000000000000000000000000000000000000000000000000611c0a565b90505f6135bc6135aa611bdf565b611103670de0b6b3a76400008561428b565b9050838111156135d0575f94505050505090565b9092039392505050565b6060610b8784845f85613c05565b5f80600d5f9054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561363a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061365e9190614206565b90505f811361293a5760405162461bcd60e51b81526020600482015260016024820152600360fc1b60448201526064016108ff565b5f6001828460026040516020016136b4929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b0390941690830152810191909152606001604051602081830303815290604052805190602001205f1c611d7b9190614231565b6040805160018082528183019092526060915f91906020808301908036833701905050905082815f8151811061373e5761373e6144f0565b602090810291909101015292915050565b5f8061375b838561428b565b90505f61377b8280613776670de0b6b3a7640000600261428b565b613cd1565b90505f6137968284613776670de0b6b3a7640000600361428b565b9050806137a38385614231565b61110d9190614231565b5f611d7b8383670de0b6b3a7640000613cd1565b6040805180820190915260148152731b585e081d5a5b9d0c4c8e08195e18d95959195960621b60208201525f906001600160801b03831115611dd05760405162461bcd60e51b81526004016108ff919061474e565b5f610b87613827620f424084614231565b613832600186614231565b869190613cd1565b5f81613847600182614278565b613851858761428b565b61292c9190614231565b5f815f0361386a57505f919050565b5f6138736112ea565b905080831061388457611d7b610f66565b5f6138926121078584614278565b90505f670de0b6b3a76400006138a6611bdf565b6138b0908461428b565b6138ba91906142a2565b90505f6138e7827f0000000000000000000000000000000000000000000000000000000000000000611ca1565b90505f6138f2610f66565b9050808210613901575f61390b565b61390b8282614278565b979650505050505050565b5f61391f610a8f565b5f8054919250906127109061394590600160501b900467ffffffffffffffff1682614231565b613972610b3c857f0000000000000000000000000000000000000000000000000000000000000000611c0a565b61397c919061428b565b61398691906142a2565b9050805f03613993575050565b610dd97f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008484613cdd565b805f036139e95750565b60405163238d657960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063238d6579906124c29060089085903090600401614760565b805f03613a445750565b6040516350d8cd4b60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906350d8cd4b90613a999060089085905f90309081906004016147a0565b60408051808303815f875af1158015613ab4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd9919061460e565b604051636e553f6560e01b8152600481018290523060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636e553f65906044016020604051808303815f875af1158015613b42573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114df9190614206565b604051636eb1769f60e11b81523060048201526001600160a01b03848116602483015282919084169063dd62ed3e90604401602060405180830381865afa158015613bb3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bd79190614206565b1015610dd957613bf16001600160a01b038316845f613e7d565b610dd96001600160a01b0383168483613e7d565b606082471015613c665760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016108ff565b5f80866001600160a01b03168587604051613c8191906147dc565b5f6040518083038185875af1925050503d805f8114613cbb576040519150601f19603f3d011682016040523d82523d5f602084013e613cc0565b606091505b509150915061390b87838387613f11565b5f8161292c848661428b565b5f8115801590613cef57506004548210155b15610b8757600654613d0b906001600160a01b03168684613b66565b6005546001600160a01b0386811691161480613d3457506005546001600160a01b038581169116145b15613dbf5760408051610100810182526001600160a01b0380881680835287821660208085018290525f92835260078152858320918352528381205462ffffff168385015230606084015242608084015260a0830187905260c0830186905260e08301526006549251631b67c43360e31b81529192169063db3e21989061340b908490600401614630565b6005546001600160a01b039081165f818152600760208181526040808420868b168552825280842054958b16845291815281832084845281528183205491519294613e1d948a9462ffffff92831694919391909216918c910161469a565b60408051601f1981840301815260065460a084018352818452306020850152428484015260608401889052608084018790529151631e51809360e31b81529093506001600160a01b039091169163f28c04989161350d919060040161473c565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613ece8482613f89565b613f0b576040516001600160a01b03841660248201525f6044820152613f0190859063095ea7b360e01b90606401611b52565b613f0b84826126eb565b50505050565b60608315613f7f5782515f03613f78576001600160a01b0385163b613f785760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108ff565b5081610b87565b610b87838361402a565b5f805f846001600160a01b031684604051613fa491906147dc565b5f604051808303815f865af19150503d805f8114613fdd576040519150601f19603f3d011682016040523d82523d5f602084013e613fe2565b606091505b509150915081801561400c57508051158061400c57508080602001905181019061400c91906143f1565b801561402157506001600160a01b0385163b15155b95945050505050565b81511561403a5781518083602001fd5b8060405162461bcd60e51b81526004016108ff919061474e565b80356001600160a01b038116811461293a575f80fd5b5f6020828403121561407a575f80fd5b611d7b82614054565b5f60208284031215614093575f80fd5b5035919050565b5f805f606084860312156140ac575f80fd5b6140b584614054565b92506140c360208501614054565b9150604084013562ffffff811681146140da575f80fd5b809150509250925092565b8015158114610a8c575f80fd5b5f60208284031215614102575f80fd5b8135611d7b816140e5565b803561ffff8116811461293a575f80fd5b5f806040838503121561412f575f80fd5b6141388361410d565b91506141466020840161410d565b90509250929050565b5f5b83811015614169578181015183820152602001614151565b50505f910152565b5f815180845261418881602086016020860161414f565b601f01601f19169290920160200192915050565b8215158152604060208201525f610b876040830184614171565b5f80604083850312156141c7575f80fd5b6141d083614054565b946020939093013593505050565b5f80604083850312156141ef575f80fd5b6141f883614054565b915061414660208401614054565b5f60208284031215614216575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115611c9b57611c9b61421d565b634e487b7160e01b5f52601260045260245ffd5b5f60208284031215614268575f80fd5b815160ff81168114611d7b575f80fd5b81810381811115611c9b57611c9b61421d565b8082028115828204841417611c9b57611c9b61421d565b5f826142bc57634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156142fe576142fe6142c1565b604052919050565b80516001600160801b038116811461293a575f80fd5b5f6060828403121561432c575f80fd5b6040516060810181811067ffffffffffffffff8211171561434f5761434f6142c1565b6040528251815261436260208401614306565b602082015261437360408401614306565b60408201529392505050565b80546001600160a01b039081168352600182015481166020840152600282015481166040840152600382015416606083015260040154608090910152565b61010081016143cc828761437f565b60a08201949094526001600160a01b0392831660c0820152911660e090910152919050565b5f60208284031215614401575f80fd5b8151611d7b816140e5565b602080825282518282018190525f9190848201906040850190845b8181101561444357835183529284019291840191600101614427565b50909695505050505050565b5f6020808385031215614460575f80fd5b825167ffffffffffffffff80821115614477575f80fd5b818501915085601f83011261448a575f80fd5b81518181111561449c5761449c6142c1565b8060051b91506144ad8483016142d5565b81815291830184019184810190888411156144c6575f80fd5b938501935b838510156144e4578451825293850193908501906144cb565b98975050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60c08284031215614514575f80fd5b60405160c0810181811067ffffffffffffffff82111715614537576145376142c1565b60405261454383614306565b815261455160208401614306565b602082015261456260408401614306565b604082015261457360608401614306565b606082015261458460808401614306565b608082015261459560a08401614306565b60a08201529392505050565b6001600160801b038181168382160190808211156145c1576145c161421d565b5092915050565b5f6101206145d6838861437f565b60a08301959095525060c08101929092526001600160a01b031660e082015261010081018290525f9181019190915261014001919050565b5f806040838503121561461f575f80fd5b505080516020909101519092909150565b6101008101611c9b828480516001600160a01b03908116835260208083015182169084015260408083015162ffffff16908401526060808301518216908401526080808301519084015260a0828101519084015260c0808301519084015260e09182015116910152565b6bffffffffffffffffffffffff19606096871b811682526001600160e81b031960e896871b8116601484015294871b811660178301529290941b909216602b840152921b909116602e82015260420190565b5f815160a0845261470060a0850182614171565b6020848101516001600160a01b031690860152604080850151908601526060808501519086015260809384015193909401929092525090919050565b602081525f611d7b60208301846146ec565b602081525f611d7b6020830184614171565b5f61010061476e838761437f565b60a0830194909452506001600160a01b039190911660c082015260e081018290525f9181019190915261012001919050565b61012081016147af828861437f565b60a082019590955260c08101939093526001600160a01b0391821660e08401521661010090910152919050565b5f82516147ed81846020870161414f565b919091019291505056fea264697066735822122036e1c05f22693a25fcf2164d78ddf6b5a361cfbf84dfeb52c15642d4ae9aafa964736f6c63430008170033a2646970667358221220124bf9188917a632c38071a8dc8af8a8210488057bd927a01407d971a1ae31a564736f6c63430008170033

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

0000000000000000000000001b5f15dcb82d25f91c65b53cee151e8b9fbdd2710000000000000000000000005a74cb32d36f2f517db6f7b0a0591e09b22cde69000000000000000000000000604e586f17ce106b64185a7a0d2c1da5bace711e00000000000000000000000016388463d60ffe0661cf7f1f31a7d658ac790ff7000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff52000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564

-----Decoded View---------------
Arg [0] : _management (address): 0x1b5f15DCb82d25f91c65b53CEe151E8b9fBdD271
Arg [1] : _performanceFeeRecipient (address): 0x5A74Cb32D36f2f517DB6f7b0A0591e09b22cDE69
Arg [2] : _keeper (address): 0x604e586F17cE106B64185A7a0d2c1Da5bAce711E
Arg [3] : _emergencyAdmin (address): 0x16388463d60FFE0661Cf7F1f31a7D658aC790ff7
Arg [4] : _gov (address): 0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52
Arg [5] : _morpho (address): 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb
Arg [6] : _router (address): 0xE592427A0AEce92De3Edee1F18E0157C05861564

-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000001b5f15dcb82d25f91c65b53cee151e8b9fbdd271
Arg [1] : 0000000000000000000000005a74cb32d36f2f517db6f7b0a0591e09b22cde69
Arg [2] : 000000000000000000000000604e586f17ce106b64185a7a0d2c1da5bace711e
Arg [3] : 00000000000000000000000016388463d60ffe0661cf7f1f31a7d658ac790ff7
Arg [4] : 000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff52
Arg [5] : 000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
Arg [6] : 000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.