ETH Price: $2,916.46 (-7.93%)
Gas: 8 Gwei

Contract

0xF919e7a09d6c9dC2db9c3DdD9c667ed5949C322c
 

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Value
Propose New Owne...197149752024-04-23 2:01:1173 days ago1713837671IN
0xF919e7a0...5949C322c
0 ETH0.000338617
Set Swapper197149452024-04-23 1:55:1173 days ago1713837311IN
0xF919e7a0...5949C322c
0 ETH0.000763517
Set Position Own...197149442024-04-23 1:54:5973 days ago1713837299IN
0xF919e7a0...5949C322c
0 ETH0.000331357
0x61016060197149282024-04-23 1:51:4773 days ago1713837107IN
 Create: OrigamiMorphoBorrowAndLend
0 ETH0.029620757

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
OrigamiMorphoBorrowAndLend

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 10000 runs

Other Settings:
default evmVersion
File 1 of 27 : OrigamiMorphoBorrowAndLend.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/borrowAndLend/OrigamiMorphoBorrowAndLend.sol)

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

import { 
    IMorpho,
    Id as MorphoMarketId,
    MarketParams as MorphoMarketParams
} from "@morpho-org/morpho-blue/src/interfaces/IMorpho.sol";
import { IOracle as IMorphoOracle } from "@morpho-org/morpho-blue/src/interfaces/IOracle.sol";
import { ORACLE_PRICE_SCALE as MORPHO_ORACLE_PRICE_SCALE } from "@morpho-org/morpho-blue/src/libraries/ConstantsLib.sol";
import { IMorphoSupplyCollateralCallback } from "@morpho-org/morpho-blue/src/interfaces/IMorphoCallbacks.sol";
import { MorphoBalancesLib } from "@morpho-org/morpho-blue/src/libraries/periphery/MorphoBalancesLib.sol";
import { MorphoLib } from "@morpho-org/morpho-blue/src/libraries/periphery/MorphoLib.sol";
import { MarketParamsLib } from "@morpho-org/morpho-blue/src/libraries/MarketParamsLib.sol";

import { IOrigamiSwapper } from "contracts/interfaces/common/swappers/IOrigamiSwapper.sol";
import { IOrigamiMorphoBorrowAndLend } from "contracts/interfaces/common/borrowAndLend/IOrigamiMorphoBorrowAndLend.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";
import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol";

/**
 * @notice An Origami abstraction over a borrow/lend money market for
 * a single `supplyToken` and a single `borrowToken`.
 * This is a Morpho specific interface
 */
contract OrigamiMorphoBorrowAndLend is IOrigamiMorphoBorrowAndLend, IMorphoSupplyCollateralCallback, OrigamiElevatedAccess {
    using SafeERC20 for IERC20;
    using MorphoBalancesLib for IMorpho;
    using MorphoLib for IMorpho;
    using MarketParamsLib for MorphoMarketParams;
    using OrigamiMath for uint256;

    /**
     * @notice The morpho singleton contract
     */
    IMorpho public immutable override morpho;

    /**
     * @notice The token supplied as collateral
     */
    IERC20 private immutable _supplyToken;
    
    /**
     * @notice The token which is borrowed
     */
    IERC20 private immutable _borrowToken;

    /**
     * @notice The Morpho oracle used for the target market
     */
    address public override immutable morphoMarketOracle;

    /**
     * @notice The Morpho Interest Rate Model used for the target market
     */
    address public override immutable morphoMarketIrm;

    /**
     * @notice The Morpho Liquidation LTV for the target market
     */
    uint96 public override immutable morphoMarketLltv;

    /**
     * @notice The derived Morpho market ID given the market parameters
     */
    MorphoMarketId public override immutable marketId;

    /**
     * @notice The approved owner of the borrow/lend position
     */
    address public override positionOwner;

    /**
     * @notice The max LTV we will allow when borrowing or withdrawing collateral.
     * @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
     */
    uint256 public override maxSafeLtv;
    
    /**
     * @notice The swapper for `borrowToken` <--> `supplyToken`
     */
    IOrigamiSwapper public override swapper;

    /**
     * @dev Factor when converting the Morpho LTV (1e18) to an Origami Assets/Liabilities (1e18)
     */
    uint256 private constant LTV_TO_AL_FACTOR = 1e36;

    /// @dev internal serialization of callback data for increasing leverage
    struct IncreaseLeverageData {
        /// @dev The amount of new `borrowToken` to borrow
        uint256 borrowAmount;

        /// @dev The encoded swap data for `borrowToken` to `supplyToken`
        bytes swapData;
    }

    /// @dev internal serialization of callback data for decreasing leverage
    struct DecreaseLeverageData {
        /// @dev The amount of `supplyToken` to withdraw from collateral
        uint256 withdrawCollateralAmount;

        /// @dev The encoded swap data for `supplyToken` to `borrowToken`
        bytes swapData;
    }
    
    constructor(
        address _initialOwner,
        address __supplyToken,
        address __borrowToken,
        address _morphoAddress,
        address _morphoMarketOracle, 
        address _morphoMarketIrm,
        uint96 _morphoMarketLltv,
        uint256 _maxSafeLtv
    ) OrigamiElevatedAccess(_initialOwner) {
        _supplyToken = IERC20(__supplyToken);
        _borrowToken = IERC20(__borrowToken);
        
        morpho = IMorpho(_morphoAddress);

        morphoMarketOracle = _morphoMarketOracle;
        morphoMarketIrm = _morphoMarketIrm;
        morphoMarketLltv = _morphoMarketLltv;

        if (_maxSafeLtv >= morphoMarketLltv) revert CommonEventsAndErrors.InvalidParam();
        maxSafeLtv = _maxSafeLtv;

        marketId = getMarketParams().id();

        // Verify that the market is valid
        if (morpho.lastUpdate(marketId) == 0) revert CommonEventsAndErrors.InvalidParam();

        // Approve the supply and borrow to the Morpho singleton upfront
        _supplyToken.forceApprove(_morphoAddress, type(uint256).max);
        _borrowToken.forceApprove(_morphoAddress, type(uint256).max);
    }

    /**
     * @notice Set the position owner who can borrow/lend via this contract
     */
    function setPositionOwner(address account) external override onlyElevatedAccess {
        positionOwner = account;
        emit PositionOwnerSet(account);
    }

    /**
     * @notice Set the max LTV we will allow when borrowing or withdrawing collateral.
     * @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
     * so we set a more restrictive 'safe' LTV'
     */
    function setMaxSafeLtv(uint256 _maxSafeLtv) external override onlyElevatedAccess {
        if (_maxSafeLtv >= morphoMarketLltv) revert CommonEventsAndErrors.InvalidParam();
        maxSafeLtv = _maxSafeLtv;
        emit MaxSafeLtvSet(_maxSafeLtv);
    }
    
    /**
     * @notice Set the swapper responsible for `borrowToken` <--> `supplyToken` swaps
     */
    function setSwapper(address _swapper) external override onlyElevatedAccess {
        if (_swapper == address(0)) revert CommonEventsAndErrors.InvalidAddress(_swapper);

        // Update the approval's for both `supplyToken` and `borrowToken`
        address _oldSwapper = address(swapper);
        if (_oldSwapper != address(0)) {
            _supplyToken.forceApprove(_oldSwapper, 0);
            _borrowToken.forceApprove(_oldSwapper, 0);
        }
        _supplyToken.forceApprove(_swapper, type(uint256).max);
        _borrowToken.forceApprove(_swapper, type(uint256).max);

        emit SwapperSet(_swapper);
        swapper = IOrigamiSwapper(_swapper);
    }

    /**
     * @notice Supply tokens as collateral
     */
    function supply(
        uint256 supplyAmount
    ) external override onlyPositionOwnerOrElevated {
        _supply(supplyAmount, getMarketParams(), "");
    }

    /**
     * @notice Withdraw collateral tokens to recipient
     * @dev Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
     */
    function withdraw(
        uint256 withdrawAmount, 
        address recipient
    ) external override onlyPositionOwnerOrElevated returns (uint256 amountWithdrawn) {
        amountWithdrawn = _withdraw(withdrawAmount, recipient, getMarketParams());
    }

    /**
     * @notice Borrow tokens and send to recipient
     */
    function borrow(
        uint256 borrowAmount, 
        address recipient
    ) external override onlyPositionOwnerOrElevated {
        _borrow(borrowAmount, recipient, getMarketParams());
    }

    /**
     * @notice Repay debt. 
     * @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
     * to that outstanding debt balance
     * `debtRepaidAmount` return parameter will be capped to the outstanding debt balance.
     * Any surplus debtTokens (if debt fully repaid) will remain in this contract
     */
    function repay(
        uint256 repayAmount
    ) external override onlyPositionOwnerOrElevated returns (uint256 debtRepaidAmount) {
        debtRepaidAmount = _repay(repayAmount, getMarketParams(), "");
    }

    /**
     * @notice Repay debt and withdraw collateral in one step
     * @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
     * to that outstanding debt balance
     * Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
     * `debtRepaidAmount` return parameter will be capped to the outstanding debt amount.
     * Any surplus debtTokens (if debt fully repaid) will remain in this contract
     */
    function repayAndWithdraw(
        uint256 repayAmount, 
        uint256 withdrawAmount, 
        address recipient
    ) external override onlyPositionOwnerOrElevated returns (uint256 debtRepaidAmount, uint256 withdrawnAmount) {
        MorphoMarketParams memory marketParams = getMarketParams();
        debtRepaidAmount = _repay(repayAmount, marketParams, "");
        withdrawnAmount = _withdraw(withdrawAmount, recipient, marketParams);
    }

    /**
     * @notice Supply collateral and borrow in one step
     */
    function supplyAndBorrow(
        uint256 supplyAmount, 
        uint256 borrowAmount, 
        address recipient
    ) external override onlyPositionOwnerOrElevated {
        MorphoMarketParams memory marketParams = getMarketParams();
        _supply(supplyAmount, marketParams, "");
        _borrow(borrowAmount, recipient, marketParams);
    }

    /**
     * @notice Increase the leverage of the existing position, by supplying `supplyToken` as collateral
     * and borrowing `borrowToken` and swapping that back to `supplyToken`
     * @dev The totalCollateralSupplied may include any surplus after swapping from the debt to collateral
     */
    function increaseLeverage(
        uint256 supplyAmount,
        uint256 borrowAmount,
        bytes memory swapData,
        uint256 supplyCollateralSurplusThreshold
    ) external override onlyPositionOwnerOrElevated returns (uint256 totalCollateralSupplied) {
        MorphoMarketParams memory marketParams = getMarketParams();
        _supply(
            supplyAmount,
            marketParams,
            abi.encode(IncreaseLeverageData(
                borrowAmount,
                swapData
            ))
        );
        totalCollateralSupplied = supplyAmount;

        // There may be a suplus of `supplyToken` in this contract after the leverage increase
        // If over the threshold, supply any surplus back in as collateral to morpho
        uint256 surplusAfterLeverage = _supplyToken.balanceOf(address(this));
        if (surplusAfterLeverage > supplyCollateralSurplusThreshold) {
            _supply(surplusAfterLeverage, marketParams, "");
            totalCollateralSupplied = totalCollateralSupplied + surplusAfterLeverage;
        }
    }

    /**
     * @notice Callback called when a supply of collateral occurs in Morpho.
     * @dev The callback is called only if data is not empty.
     * @param supplyAmount The amount of supplied collateral.
     * @param data Arbitrary data passed to the `supplyCollateral` function.
     */
    function onMorphoSupplyCollateral(uint256 supplyAmount, bytes calldata data) external override {
        if (msg.sender != address(morpho)) revert CommonEventsAndErrors.InvalidAccess();
        IncreaseLeverageData memory decoded = abi.decode(data, (IncreaseLeverageData));

        MorphoMarketParams memory marketParams = getMarketParams();

        // Perform the borrow
        _borrow(decoded.borrowAmount, address(this), marketParams);

        // Swap from [borrowToken] to [supplyToken]
        // The expected amount of [supplyToken] received after swapping from [borrowToken]
        // needs to at least cover the supplyAmount
        uint256 collateralReceived = swapper.execute(_borrowToken, decoded.borrowAmount, _supplyToken, decoded.swapData);
        if (collateralReceived < supplyAmount) {
            revert CommonEventsAndErrors.Slippage(supplyAmount, collateralReceived);
        }
    }

    /**
     * @notice Decrease the leverage of the existing position, by repaying `borrowToken`
     * and withdrawing `supplyToken` collateral then swapping that back to `borrowToken`
     */
    function decreaseLeverage(
        uint256 repayAmount,
        uint256 withdrawCollateralAmount,
        bytes memory swapData,
        uint256 repaySurplusThreshold
    ) external override onlyPositionOwnerOrElevated returns (
        uint256 debtRepaidAmount, 
        uint256 surplusDebtRepaid
    ) {
        MorphoMarketParams memory marketParams = getMarketParams();
        debtRepaidAmount = _repay(
            repayAmount, 
            marketParams,
            abi.encode(DecreaseLeverageData(
                withdrawCollateralAmount,
                swapData
            ))
        );

        // There may be a suplus of `borrowToken` in this contract after the delverage
        // If over the threshold, repay any surplus back to morpho
        uint256 surplusAfterDeleverage = _borrowToken.balanceOf(address(this));
        if (surplusAfterDeleverage > repaySurplusThreshold) {
            surplusDebtRepaid = _repay(surplusAfterDeleverage, marketParams, "");
        }
    }

    /**
     * @notice Callback called when a repayment occurs.
     * @dev The callback is called only if data is not empty.
     * @param repayAmount The amount of repaid assets.
     * @param data Arbitrary data passed to the `repay` function.
     */
    function onMorphoRepay(uint256 repayAmount, bytes calldata data) external {
        if (msg.sender != address(morpho)) revert CommonEventsAndErrors.InvalidAccess();
        DecreaseLeverageData memory decoded = abi.decode(data, (DecreaseLeverageData));

        MorphoMarketParams memory marketParams = getMarketParams();

        // Withdraw collateral
        uint256 _amountWithdrawn = _withdraw(decoded.withdrawCollateralAmount, address(this), marketParams);
        if (_amountWithdrawn != decoded.withdrawCollateralAmount) {
            revert CommonEventsAndErrors.InvalidAmount(address(_supplyToken), decoded.withdrawCollateralAmount);
        }
        
        // Swap from [supplyToken] to [borrowToken]
        // The expected amount of [borrowToken] received after swapping from [supplyToken]
        // needs to at least cover the repayAmount
        uint256 borrowTokenReceived = swapper.execute(_supplyToken, decoded.withdrawCollateralAmount, _borrowToken, decoded.swapData);
        if (borrowTokenReceived < repayAmount) {
            revert CommonEventsAndErrors.Slippage(repayAmount, borrowTokenReceived);
        }
    }

    /**
     * @notice Recover accidental donations.
     * @dev Does not allow for recovery of supplyToken or borrowToken if there is an outstanding
     * morpho debt on this pool
     * @param token Token to recover
     * @param to Recipient address
     * @param amount Amount to recover
     */
    function recoverToken(address token, address to, uint256 amount) external onlyElevatedAccess {
        if (debtBalance() != 0) {
            if (token == address(_supplyToken) || token == address(_borrowToken)) {
                revert CommonEventsAndErrors.InvalidToken(token);
            }
        }
        emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
        IERC20(token).safeTransfer(to, amount);
    }
        
    /**
     * @notice The Morpho market parameters
     */
    function getMarketParams() public override view returns (MorphoMarketParams memory) {
        return MorphoMarketParams({
            loanToken: address(_borrowToken),
            collateralToken: address(_supplyToken),
            oracle: morphoMarketOracle,
            irm: morphoMarketIrm,
            lltv: morphoMarketLltv
        });
    }

    /**
     * @notice The token supplied as collateral
     */
    function supplyToken() public override view returns (address) {
        return address(_supplyToken);
    }
    
    /**
     * @notice The token which is borrowed
     */
    function borrowToken() public override view returns (address) {
        return address(_borrowToken);
    }

    /**
     * @notice The current (manually tracked) balance of tokens supplied
     */
    function suppliedBalance() public override view returns (uint256) {
        return morpho.collateral(marketId, address(this));
    }

    /**
     * @notice The current debt balance of tokens borrowed
     */
    function debtBalance() public override view returns (uint256) {
        return morpho.expectedBorrowAssets(getMarketParams(), address(this));
    }

    /**
     * @notice Whether a given Assets/Liabilities Ratio is safe, given the upstream
     * money market parameters
     */
    function isSafeAlRatio(uint256 alRatio) external override view returns (bool) {
        return alRatio >= LTV_TO_AL_FACTOR / maxSafeLtv;
    }

    /**
     * @notice How many `supplyToken` are available to withdraw from collateral
     * from the entire protocol, assuming this contract has fully paid down its debt
     */
    function availableToWithdraw() external override view returns (uint256) {
        // The collateral (for borrows) never gets used as they are siloed markets,
        // this contracts collateral is always available to be withdrawn.
        // There's no morpho metric for the entire collateral supplied, instead
        // this just returns our collateral - so the same as `suppliedBalance()`
        return suppliedBalance();
    }

    /**
     * @notice How many `borrowToken` are available to borrow
     * from the entire protocol
     */
    function availableToBorrow() external override view returns (uint256) {
        uint256 totalSupplyAssets = morpho.totalSupplyAssets(marketId);
        uint256 totalBorrowAssets = morpho.totalBorrowAssets(marketId);
        return totalSupplyAssets > totalBorrowAssets ? totalSupplyAssets - totalBorrowAssets : 0;
    }

    /**
     * @notice How much more capacity is available to supply
     */
    function availableToSupply() external override pure returns (
        uint256 supplyCap,
        uint256 available
    ) {
        return (
            type(uint256).max,
            type(uint256).max
        );
    }

    /**
     * @notice Returns the curent Morpho position data
     */
    function debtAccountData() external override view returns (
        uint256 collateral,
        uint256 collateralPrice,
        uint256 borrowed,
        uint256 maxBorrow,
        uint256 currentLtv,
        uint256 healthFactor
    ) {
        // supplyToken decimals
        collateral = suppliedBalance();
        // `36 + borrowToken decimals - supplyToken decimals` decimals of precision.
        collateralPrice = IMorphoOracle(morphoMarketOracle).price();
        // borrowToken decimals
        borrowed = debtBalance();
        
        uint256 _collateralInBorrowTerms = collateral.mulDiv(
            collateralPrice, 
            MORPHO_ORACLE_PRICE_SCALE, 
            OrigamiMath.Rounding.ROUND_DOWN
        );

        maxBorrow = _collateralInBorrowTerms.mulDiv(
            morphoMarketLltv,
            1e18,
            OrigamiMath.Rounding.ROUND_DOWN
        );
        
        if (borrowed == 0) {
            healthFactor = type(uint256).max;
        } else {
            currentLtv = borrowed.mulDiv(
                1e18,
                _collateralInBorrowTerms,
                OrigamiMath.Rounding.ROUND_UP
            );
            healthFactor = maxBorrow.mulDiv(1e18, borrowed, OrigamiMath.Rounding.ROUND_DOWN);
        }
    }

    function _supply(uint256 supplyAmount, MorphoMarketParams memory marketParams, bytes memory data) internal {
        morpho.supplyCollateral(marketParams, supplyAmount, address(this), data);
    }

    function _withdraw(uint256 withdrawAmount, address recipient, MorphoMarketParams memory marketParams) internal returns (uint256 amountWithdrawn) {
        // If `withdrawAmount` == uint256.max, then set the the current supplied collateral balance
        amountWithdrawn = withdrawAmount == type(uint256).max ? suppliedBalance() : withdrawAmount;
        morpho.withdrawCollateral(marketParams, amountWithdrawn, address(this), recipient);
    }

    function _borrow(uint256 borrowAmount, address recipient, MorphoMarketParams memory marketParams) internal {
        (uint256 assetsBorrowed,) = morpho.borrow(
            marketParams, borrowAmount, 0, address(this), recipient
        );
        if (assetsBorrowed != borrowAmount) revert CommonEventsAndErrors.InvalidAmount(address(_borrowToken), assetsBorrowed);
    }
    
    function _repay(uint256 repayAmount, MorphoMarketParams memory marketParams, bytes memory data) internal returns (uint256 debtRepaidAmount) {
        uint256 _debtBalance = debtBalance();

        if (_debtBalance != 0) {
            // If the repayment amount is more than the current balance, then repay 100% of the debt.
            if (repayAmount > _debtBalance) {
                // Calculate the current morpho shares owed, and repay via the shares (not amount)
                uint256 _repayShares = morpho.position(marketId, address(this)).borrowShares;
                (debtRepaidAmount, ) = morpho.repay(marketParams, 0, _repayShares, address(this), data);
            } else {
                // Repay via the amount (not shares)
                (debtRepaidAmount, ) = morpho.repay(marketParams, repayAmount, 0, address(this), data);
            }                          
        }
    }

    /**
     * @dev Only the positionOwner or Elevated Access is allowed to call.
     */
    modifier onlyPositionOwnerOrElevated() {
        if (msg.sender != address(positionOwner)) {
            if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
        }
        _;
    }
}

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

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

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

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

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

type Id is bytes32;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// @title IMorphoLiquidateCallback
/// @notice Interface that liquidators willing to use `liquidate`'s callback must implement.
interface IMorphoLiquidateCallback {
    /// @notice Callback called when a liquidation occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param repaidAssets The amount of repaid assets.
    /// @param data Arbitrary data passed to the `liquidate` function.
    function onMorphoLiquidate(uint256 repaidAssets, bytes calldata data) external;
}

/// @title IMorphoRepayCallback
/// @notice Interface that users willing to use `repay`'s callback must implement.
interface IMorphoRepayCallback {
    /// @notice Callback called when a repayment occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of repaid assets.
    /// @param data Arbitrary data passed to the `repay` function.
    function onMorphoRepay(uint256 assets, bytes calldata data) external;
}

/// @title IMorphoSupplyCallback
/// @notice Interface that users willing to use `supply`'s callback must implement.
interface IMorphoSupplyCallback {
    /// @notice Callback called when a supply occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of supplied assets.
    /// @param data Arbitrary data passed to the `supply` function.
    function onMorphoSupply(uint256 assets, bytes calldata data) external;
}

/// @title IMorphoSupplyCollateralCallback
/// @notice Interface that users willing to use `supplyCollateral`'s callback must implement.
interface IMorphoSupplyCollateralCallback {
    /// @notice Callback called when a supply of collateral occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of supplied collateral.
    /// @param data Arbitrary data passed to the `supplyCollateral` function.
    function onMorphoSupplyCollateral(uint256 assets, bytes calldata data) external;
}

/// @title IMorphoFlashLoanCallback
/// @notice Interface that users willing to use `flashLoan`'s callback must implement.
interface IMorphoFlashLoanCallback {
    /// @notice Callback called when a flash loan occurs.
    /// @dev The callback is called only if data is not empty.
    /// @param assets The amount of assets that was flash loaned.
    /// @param data Arbitrary data passed to the `flashLoan` function.
    function onMorphoFlashLoan(uint256 assets, bytes calldata data) external;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

uint256 constant WAD = 1e18;

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

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

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

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

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

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

        return firstTerm + secondTerm + thirdTerm;
    }
}

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

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

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

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

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

        uint256 elapsed = block.timestamp - market.lastUpdate;

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

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

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

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

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

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

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

        return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /* SLOT OFFSETS */

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

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

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

    /* GETTERS */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 15 of 27 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (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.
 */
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].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

File 16 of 27 : IERC20.sol
// 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);
}

File 17 of 27 : SafeERC20.sol
// 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 18 of 27 : Address.sol
// 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);
        }
    }
}

File 19 of 27 : Common.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;

// Common.sol
//
// Common mathematical functions needed by both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.

/*//////////////////////////////////////////////////////////////////////////
                                CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);

/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);

/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();

/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);

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

/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;

/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;

/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;

/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;

/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;

/*//////////////////////////////////////////////////////////////////////////
                                    FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
    unchecked {
        // Start from 0.5 in the 192.64-bit fixed-point format.
        result = 0x800000000000000000000000000000000000000000000000;

        // The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
        //
        // 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
        // 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
        // a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
        // we know that `x & 0xFF` is also 1.
        if (x & 0xFF00000000000000 > 0) {
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
        }

        if (x & 0xFF000000000000 > 0) {
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
        }

        if (x & 0xFF0000000000 > 0) {
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
        }

        if (x & 0xFF00000000 > 0) {
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
        }

        if (x & 0xFF000000 > 0) {
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
        }

        if (x & 0xFF0000 > 0) {
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
        }

        if (x & 0xFF00 > 0) {
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
        }

        if (x & 0xFF > 0) {
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
        }

        // In the code snippet below, two operations are executed simultaneously:
        //
        // 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
        // accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
        // 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
        //
        // The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
        // integer part, $2^n$.
        result *= UNIT;
        result >>= (191 - (x >> 64));
    }
}

/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
///     x >>= 128;
///     result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
    // 2^128
    assembly ("memory-safe") {
        let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^64
    assembly ("memory-safe") {
        let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^32
    assembly ("memory-safe") {
        let factor := shl(5, gt(x, 0xFFFFFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^16
    assembly ("memory-safe") {
        let factor := shl(4, gt(x, 0xFFFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^8
    assembly ("memory-safe") {
        let factor := shl(3, gt(x, 0xFF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^4
    assembly ("memory-safe") {
        let factor := shl(2, gt(x, 0xF))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^2
    assembly ("memory-safe") {
        let factor := shl(1, gt(x, 0x3))
        x := shr(factor, x)
        result := or(result, factor)
    }
    // 2^1
    // No need to shift x any more.
    assembly ("memory-safe") {
        let factor := gt(x, 0x1)
        result := or(result, factor)
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
    // 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 ("memory-safe") {
        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) {
        unchecked {
            return prod0 / denominator;
        }
    }

    // Make sure the result is less than 2^256. Also prevents denominator == 0.
    if (prod1 >= denominator) {
        revert PRBMath_MulDiv_Overflow(x, y, denominator);
    }

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

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

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

    unchecked {
        // Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
        // because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
        // For more detail, see https://cs.stackexchange.com/q/138556/92363.
        uint256 lpotdod = denominator & (~denominator + 1);
        uint256 flippedLpotdod;

        assembly ("memory-safe") {
            // Factor powers of two out of denominator.
            denominator := div(denominator, lpotdod)

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

            // Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
            // `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
            // However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
            flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
        }

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

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

/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
///     x * y = MAX\_UINT256 * UNIT \\
///     (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
    uint256 prod0;
    uint256 prod1;
    assembly ("memory-safe") {
        let mm := mulmod(x, y, not(0))
        prod0 := mul(x, y)
        prod1 := sub(sub(mm, prod0), lt(mm, prod0))
    }

    if (prod1 == 0) {
        unchecked {
            return prod0 / UNIT;
        }
    }

    if (prod1 >= UNIT) {
        revert PRBMath_MulDiv18_Overflow(x, y);
    }

    uint256 remainder;
    assembly ("memory-safe") {
        remainder := mulmod(x, y, UNIT)
        result :=
            mul(
                or(
                    div(sub(prod0, remainder), UNIT_LPOTD),
                    mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
                ),
                UNIT_INVERSE
            )
    }
}

/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
    if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
        revert PRBMath_MulDivSigned_InputTooSmall();
    }

    // Get hold of the absolute values of x, y and the denominator.
    uint256 xAbs;
    uint256 yAbs;
    uint256 dAbs;
    unchecked {
        xAbs = x < 0 ? uint256(-x) : uint256(x);
        yAbs = y < 0 ? uint256(-y) : uint256(y);
        dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
    }

    // Compute the absolute value of x*y÷denominator. The result must fit in int256.
    uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
    if (resultAbs > uint256(type(int256).max)) {
        revert PRBMath_MulDivSigned_Overflow(x, y);
    }

    // Get the signs of x, y and the denominator.
    uint256 sx;
    uint256 sy;
    uint256 sd;
    assembly ("memory-safe") {
        // "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
        sx := sgt(x, sub(0, 1))
        sy := sgt(y, sub(0, 1))
        sd := sgt(denominator, sub(0, 1))
    }

    // XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
    // If there are, the result should be negative. Otherwise, it should be positive.
    unchecked {
        result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
    }
}

/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
    if (x == 0) {
        return 0;
    }

    // For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
    //
    // We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
    //
    // $$
    // msb(x) <= x <= 2*msb(x)$
    // $$
    //
    // We write $msb(x)$ as $2^k$, and we get:
    //
    // $$
    // k = log_2(x)
    // $$
    //
    // Thus, we can write the initial inequality as:
    //
    // $$
    // 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
    // sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
    // 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
    // $$
    //
    // Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
    uint256 xAux = uint256(x);
    result = 1;
    if (xAux >= 2 ** 128) {
        xAux >>= 128;
        result <<= 64;
    }
    if (xAux >= 2 ** 64) {
        xAux >>= 64;
        result <<= 32;
    }
    if (xAux >= 2 ** 32) {
        xAux >>= 32;
        result <<= 16;
    }
    if (xAux >= 2 ** 16) {
        xAux >>= 16;
        result <<= 8;
    }
    if (xAux >= 2 ** 8) {
        xAux >>= 8;
        result <<= 4;
    }
    if (xAux >= 2 ** 4) {
        xAux >>= 4;
        result <<= 2;
    }
    if (xAux >= 2 ** 2) {
        result <<= 1;
    }

    // At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
    // most 128 bits, 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 + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;
        result = (result + x / result) >> 1;

        // If x is not a perfect square, round the result toward zero.
        uint256 roundedResult = x / result;
        if (result >= roundedResult) {
            result = roundedResult;
        }
    }
}

File 20 of 27 : OrigamiElevatedAccess.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)

import { OrigamiElevatedAccessBase } from "contracts/common/access/OrigamiElevatedAccessBase.sol";

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
abstract contract OrigamiElevatedAccess is OrigamiElevatedAccessBase {
    constructor(address initialOwner) {
        _init(initialOwner);
    }
}

File 21 of 27 : OrigamiElevatedAccessBase.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)

import { IOrigamiElevatedAccess } from "contracts/interfaces/common/access/IOrigamiElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
abstract contract OrigamiElevatedAccessBase is IOrigamiElevatedAccess {
    /**
     * @notice The address of the current owner.
     */ 
    address public override owner;

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    mapping(address => mapping(bytes4 => bool)) public override explicitFunctionAccess;

    /// @dev Track proposed owner
    address private _proposedNewOwner;

    function _init(address initialOwner) internal {
        if (owner != address(0)) revert CommonEventsAndErrors.InvalidAccess();
        if (initialOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
        owner = initialOwner;
    }

    /**
     * @notice Proposes a new Owner.
     * Can only be called by the current owner
     */
    function proposeNewOwner(address account) external override onlyElevatedAccess {
        if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress(account);
        emit NewOwnerProposed(owner, _proposedNewOwner, account);
        _proposedNewOwner = account;
    }

    /**
     * @notice Caller accepts the role as new Owner.
     * Can only be called by the proposed owner
     */
    function acceptOwner() external override {
        if (msg.sender != _proposedNewOwner) revert CommonEventsAndErrors.InvalidAccess();

        emit NewOwnerAccepted(owner, msg.sender);
        owner = msg.sender;
        delete _proposedNewOwner;
    }

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external override onlyElevatedAccess {
        if (allowedCaller == address(0)) revert CommonEventsAndErrors.InvalidAddress(allowedCaller);
        ExplicitAccess memory _access;
        for (uint256 i; i < access.length; ++i) {
            _access = access[i];
            emit ExplicitAccessSet(allowedCaller, _access.fnSelector, _access.allowed);
            explicitFunctionAccess[allowedCaller][_access.fnSelector] = _access.allowed;
        }
    }

    function isElevatedAccess(address caller, bytes4 fnSelector) internal view returns (bool) {
        return (
            caller == owner || 
            explicitFunctionAccess[caller][fnSelector]
        );
    }

    /**
     * @notice The owner is allowed to call, or if explicit access has been given to the caller.
     * @dev Important: Only for use when called from an *external* contract. 
     * If a function with this modifier is called internally then the `msg.sig` 
     * will still refer to the top level externally called function.
     */
    modifier onlyElevatedAccess() {
        if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
        _;
    }
}

File 22 of 27 : IOrigamiElevatedAccess.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/access/IOrigamiElevatedAccess.sol)

/**
 * @notice Inherit to add Owner roles for DAO elevated access.
 */ 
interface IOrigamiElevatedAccess {
    event ExplicitAccessSet(address indexed account, bytes4 indexed fnSelector, bool indexed value);

    event NewOwnerProposed(address indexed oldOwner, address indexed oldProposedOwner, address indexed newProposedOwner);
    event NewOwnerAccepted(address indexed oldOwner, address indexed newOwner);

    struct ExplicitAccess {
        bytes4 fnSelector;
        bool allowed;
    }

    /**
     * @notice The address of the current owner.
     */ 
    function owner() external returns (address);

    /**
     * @notice Explicit approval for an address to execute a function.
     * allowedCaller => function selector => true/false
     */
    function explicitFunctionAccess(address contractAddr, bytes4 functionSelector) external returns (bool);

    /**
     * @notice Proposes a new Owner.
     * Can only be called by the current owner
     */
    function proposeNewOwner(address account) external;

    /**
     * @notice Caller accepts the role as new Owner.
     * Can only be called by the proposed owner
     */
    function acceptOwner() external;

    /**
     * @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
     * @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
     */
    function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external;
}

File 23 of 27 : IOrigamiBorrowAndLend.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/borrowAndLend/IOrigamiBorrowAndLend.sol)

/**
 * @notice An Origami abstraction over a borrow/lend money market for
 * a single `supplyToken` and a single `borrowToken`, for a given `positionOwner`
 */
interface IOrigamiBorrowAndLend {
    event PositionOwnerSet(address indexed account);
    event SurplusDebtReclaimed(uint256 amount, address indexed recipient);

    /**
     * @notice Set the position owner who can borrow/lend via this contract
     */
    function setPositionOwner(address account) external;

    /**
     * @notice Supply tokens as collateral
     */
    function supply(
        uint256 supplyAmount
    ) external;

    /**
     * @notice Withdraw collateral tokens to recipient
     * @dev Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
     */
    function withdraw(
        uint256 withdrawAmount, 
        address recipient
    ) external returns (uint256 amountWithdrawn);

    /**
     * @notice Borrow tokens and send to recipient
     */
    function borrow(
        uint256 borrowAmount,
        address recipient
    ) external;

    /**
     * @notice Repay debt. 
     * @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
     * to that outstanding debt balance
     * `debtRepaidAmount` return parameter will be capped to the outstanding debt balance.
     * Any surplus debtTokens (if debt fully repaid) will remain in this contract
     */
    function repay(
        uint256 repayAmount
    ) external returns (uint256 debtRepaidAmount);

    /**
     * @notice Repay debt and withdraw collateral in one step
     * @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
     * to that outstanding debt balance
     * Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
     * `debtRepaidAmount` return parameter will be capped to the outstanding debt amount.
     * Any surplus debtTokens (if debt fully repaid) will remain in this contract
     */
    function repayAndWithdraw(
        uint256 repayAmount, 
        uint256 withdrawAmount, 
        address recipient
    ) external returns (
        uint256 debtRepaidAmount,
        uint256 withdrawnAmount
    );

    /**
     * @notice Supply collateral and borrow in one step
     */
    function supplyAndBorrow(
        uint256 supplyAmount, 
        uint256 borrowAmount, 
        address recipient
    ) external;

    /**
     * @notice The approved owner of the borrow/lend position
     */
    function positionOwner() external view returns (address);

    /**
     * @notice The token supplied as collateral
     */
    function supplyToken() external view returns (address);

    /**
     * @notice The token which is borrowed
     */
    function borrowToken() external view returns (address);

    /**
     * @notice The current (manually tracked) balance of tokens supplied
     */
    function suppliedBalance() external view returns (uint256);

    /**
     * @notice The current debt balance of tokens borrowed
     */
    function debtBalance() external view returns (uint256);

    /**
     * @notice Whether a given Assets/Liabilities Ratio is safe, given the upstream
     * money market parameters
     */
    function isSafeAlRatio(uint256 alRatio) external view returns (bool);

    /**
     * @notice How many `supplyToken` are available to withdraw from collateral
     * from the entire protocol, assuming this contract has fully paid down its debt
     */
    function availableToWithdraw() external view returns (uint256);

    /**
     * @notice How much more capacity is available to supply
     */
    function availableToSupply() external view returns (
        uint256 supplyCap,
        uint256 available
    );

    /**
     * @notice How many `borrowToken` are available to borrow
     * from the entire protocol
     */
    function availableToBorrow() external view returns (uint256);
}

File 24 of 27 : IOrigamiMorphoBorrowAndLend.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/borrowAndLend/IOrigamiMorphoBorrowAndLend.sol)

import { IOrigamiBorrowAndLend } from "contracts/interfaces/common/borrowAndLend/IOrigamiBorrowAndLend.sol";
import { IMorpho, Id as MorphoMarketId, MarketParams as MorphoMarketParams } from "@morpho-org/morpho-blue/src/interfaces/IMorpho.sol";
import { IOrigamiSwapper } from "contracts/interfaces/common/swappers/IOrigamiSwapper.sol";

/**
 * @notice An Origami abstraction over a borrow/lend money market for
 * a single `supplyToken` and a single `borrowToken`.
 * This is a Morpho specific interface
 */
interface IOrigamiMorphoBorrowAndLend is IOrigamiBorrowAndLend {
    event MaxSafeLtvSet(uint256 _maxSafeLtv);
    event SwapperSet(address indexed swapper);

    /**
     * @notice Set the max LTV we will allow when borrowing or withdrawing collateral.
     * @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
     */
    function setMaxSafeLtv(uint256 _maxSafeLtv) external;

    /**
     * @notice Set the swapper responsible for `borrowToken` <--> `supplyToken` swaps
     */
    function setSwapper(address _swapper) external;

    /**
     * @notice Increase the leverage of the existing position, by supplying `supplyToken` as collateral
     * and borrowing `borrowToken` and swapping that back to `supplyToken`
     * @dev The totalCollateralSupplied may include any surplus after swapping from the debt to collateral
     */
    function increaseLeverage(
        uint256 supplyCollateralAmount,
        uint256 borrowAmount,
        bytes memory swapData,
        uint256 supplyCollateralSurplusThreshold
    ) external returns (uint256 totalCollateralSupplied);

    /**
     * @notice Decrease the leverage of the existing position, by repaying `borrowToken`
     * and withdrawing `supplyToken` collateral then swapping that back to `borrowToken`
     */
    function decreaseLeverage(
        uint256 repayAmount,
        uint256 withdrawCollateralAmount,
        bytes memory swapData,
        uint256 repaySurplusThreshold
    ) external returns  (
        uint256 debtRepaidAmount, 
        uint256 surplusDebtRepaid
    );

    /**
     * @notice The morpho singleton contract
     */
    function morpho() external view returns (IMorpho);

    /**
     * @notice The Morpho oracle used for the target market
     */
    function morphoMarketOracle() external view returns (address);

    /**
     * @notice The Morpho Interest Rate Model used for the target market
     */
    function morphoMarketIrm() external view returns (address);

    /**
     * @notice The Morpho Liquidation LTV for the target market
     */
    function morphoMarketLltv() external view returns (uint96);

    /**
     * @notice The Morpho market parameters
     */
    function getMarketParams() external view returns (MorphoMarketParams memory);

    /**
     * @notice The derived Morpho market ID given the market parameters
     */
    function marketId() external view returns (MorphoMarketId);

    /**
     * @notice The max LTV we will allow when borrowing or withdrawing collateral.
     * @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
     */
    function maxSafeLtv() external view returns (uint256);
    
    /**
     * @notice The swapper for `borrowToken` <--> `supplyToken`
     */
    function swapper() external view returns (IOrigamiSwapper);

    /**
     * @notice Returns the curent Morpho position data
     */
    function debtAccountData() external view returns (
        uint256 collateral,
        uint256 collateralPrice,
        uint256 borrowed,
        uint256 maxBorrow,
        uint256 currentLtv,
        uint256 healthFactor
    );
}

File 25 of 27 : IOrigamiSwapper.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/swappers/IOrigamiSwapper.sol)

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

/**
 * @notice An on chain swapper contract to integrate with the 1Inch router | 0x proxy, 
 * possibly others which obtain quote calldata offchain and then execute via a low level call
 * to perform the swap onchain
 */
interface IOrigamiSwapper {
    error UnknownSwapError(bytes result);
    error InvalidSwap();

    event Swap(address indexed sellToken, uint256 sellTokenAmount, address indexed buyToken, uint256 buyTokenAmount);

    /**
     * @notice Pull tokens from sender then execute the swap
     */
    function execute(
        IERC20 sellToken, 
        uint256 sellTokenAmount, 
        IERC20 buyToken,
        bytes memory swapData
    ) external returns (uint256 buyTokenAmount);
}

File 26 of 27 : CommonEventsAndErrors.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/CommonEventsAndErrors.sol)

/// @notice A collection of common events and errors thrown within the Origami contracts
library CommonEventsAndErrors {
    error InsufficientBalance(address token, uint256 required, uint256 balance);
    error InvalidToken(address token);
    error InvalidParam();
    error InvalidAddress(address addr);
    error InvalidAmount(address token, uint256 amount);
    error ExpectedNonZero();
    error Slippage(uint256 minAmountExpected, uint256 actualAmount);
    error IsPaused();
    error UnknownExecuteError(bytes returndata);
    error InvalidAccess();
    error BreachedMaxTotalSupply(uint256 totalSupply, uint256 maxTotalSupply);

    event TokenRecovered(address indexed to, address indexed token, uint256 amount);
}

File 27 of 27 : OrigamiMath.sol
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/OrigamiMath.sol)

import { mulDiv as prbMulDiv, PRBMath_MulDiv_Overflow } from "@prb/math/src/Common.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";

/**
 * @notice Utilities to operate on fixed point math multipliation and division
 * taking rounding into consideration
 */
library OrigamiMath {
    enum Rounding {
        ROUND_DOWN,
        ROUND_UP
    }

    uint256 public constant BASIS_POINTS_DIVISOR = 10_000;

    function scaleUp(uint256 amount, uint256 scalar) internal pure returns (uint256) {
        // Special case for scalar == 1, as it's common for token amounts to not need
        // scaling if decimal places are the same
        return scalar == 1 ? amount : amount * scalar;
    }

    function scaleDown(
        uint256 amount, 
        uint256 scalar, 
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        // Special case for scalar == 1, as it's common for token amounts to not need
        // scaling if decimal places are the same
        unchecked {
            if (scalar == 1) {
                result = amount;
            } else if (roundingMode == Rounding.ROUND_DOWN) {
                result = amount / scalar;
            } else {
                // ROUND_UP uses the same logic as OZ Math.ceilDiv()
                result = amount == 0 ? 0 : (amount - 1) / scalar + 1;
            }
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision,
     * rounding up
     */
    function mulDiv(
        uint256 x, 
        uint256 y, 
        uint256 denominator,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        result = prbMulDiv(x, y, denominator);
        if (roundingMode == Rounding.ROUND_UP) {
            if (mulmod(x, y, denominator) != 0) {
                if (result < type(uint256).max) {
                    unchecked {
                        result = result + 1;
                    }
                } else {
                    revert PRBMath_MulDiv_Overflow(x, y, denominator);
                }
            }
        }
    }

    function subtractBps(
        uint256 inputAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        uint256 numeratorBps;
        unchecked {
            numeratorBps = BASIS_POINTS_DIVISOR - basisPoints;
        }

        result = basisPoints < BASIS_POINTS_DIVISOR
            ? mulDiv(
                inputAmount,
                numeratorBps, 
                BASIS_POINTS_DIVISOR, 
                roundingMode
            ) : 0;
    }

    function addBps(
        uint256 inputAmount,
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        uint256 numeratorBps;
        unchecked {
            numeratorBps = BASIS_POINTS_DIVISOR + basisPoints;
        }

        // Round up for max amounts out expected
        result = mulDiv(
            inputAmount,
            numeratorBps, 
            BASIS_POINTS_DIVISOR, 
            roundingMode
        );
    }

    /**
     * @notice Split the `inputAmount` into two parts based on the `basisPoints` fraction.
     * eg: 3333 BPS (33.3%) can be used to split an input amount of 600 into: (result=400, removed=200).
     * @dev The rounding mode is applied to the `result`
     */
    function splitSubtractBps(
        uint256 inputAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result, uint256 removed) {
        result = subtractBps(inputAmount, basisPoints, roundingMode);
        unchecked {
            removed = inputAmount - result;
        }
    }

    /**
     * @notice Reverse the fractional amount of an input.
     * eg: For 3333 BPS (33.3%) and the remainder=400, the result is 600
     */
    function inverseSubtractBps(
        uint256 remainderAmount, 
        uint256 basisPoints,
        Rounding roundingMode
    ) internal pure returns (uint256 result) {
        if (basisPoints == 0) return remainderAmount; // gas shortcut for 0
        if (basisPoints >= BASIS_POINTS_DIVISOR) revert CommonEventsAndErrors.InvalidParam();

        uint256 denominatorBps;
        unchecked {
            denominatorBps = BASIS_POINTS_DIVISOR - basisPoints;
        }
        result = mulDiv(
            remainderAmount,
            BASIS_POINTS_DIVISOR, 
            denominatorBps, 
            roundingMode
        );
    }

    /**
     * @notice Calculate the relative difference of a value to a reference
     * @dev `value` and `referenceValue` must have the same precision
     * The denominator is always the referenceValue
     */
    function relativeDifferenceBps(
        uint256 value,
        uint256 referenceValue,
        Rounding roundingMode
    ) internal pure returns (uint256) {
        if (referenceValue == 0) revert CommonEventsAndErrors.InvalidParam();

        uint256 absDelta;
        unchecked {
            absDelta = value < referenceValue
                ? referenceValue - value
                : value - referenceValue;
        }

        return mulDiv(
            absDelta,
            BASIS_POINTS_DIVISOR,
            referenceValue,
            roundingMode
        );
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"address","name":"__supplyToken","type":"address"},{"internalType":"address","name":"__borrowToken","type":"address"},{"internalType":"address","name":"_morphoAddress","type":"address"},{"internalType":"address","name":"_morphoMarketOracle","type":"address"},{"internalType":"address","name":"_morphoMarketIrm","type":"address"},{"internalType":"uint96","name":"_morphoMarketLltv","type":"uint96"},{"internalType":"uint256","name":"_maxSafeLtv","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAccess","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"InvalidAddress","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidParam","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"InvalidToken","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath_MulDiv_Overflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmountExpected","type":"uint256"},{"internalType":"uint256","name":"actualAmount","type":"uint256"}],"name":"Slippage","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"ExplicitAccessSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_maxSafeLtv","type":"uint256"}],"name":"MaxSafeLtvSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"NewOwnerAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"oldProposedOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newProposedOwner","type":"address"}],"name":"NewOwnerProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"PositionOwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"SurplusDebtReclaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"swapper","type":"address"}],"name":"SwapperSet","type":"event"},{"inputs":[],"name":"acceptOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"availableToBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableToSupply","outputs":[{"internalType":"uint256","name":"supplyCap","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"availableToWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtAccountData","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"collateralPrice","type":"uint256"},{"internalType":"uint256","name":"borrowed","type":"uint256"},{"internalType":"uint256","name":"maxBorrow","type":"uint256"},{"internalType":"uint256","name":"currentLtv","type":"uint256"},{"internalType":"uint256","name":"healthFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawCollateralAmount","type":"uint256"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"uint256","name":"repaySurplusThreshold","type":"uint256"}],"name":"decreaseLeverage","outputs":[{"internalType":"uint256","name":"debtRepaidAmount","type":"uint256"},{"internalType":"uint256","name":"surplusDebtRepaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"explicitFunctionAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketParams","outputs":[{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"uint256","name":"supplyCollateralSurplusThreshold","type":"uint256"}],"name":"increaseLeverage","outputs":[{"internalType":"uint256","name":"totalCollateralSupplied","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"alRatio","type":"uint256"}],"name":"isSafeAlRatio","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketId","outputs":[{"internalType":"Id","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSafeLtv","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morpho","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morphoMarketIrm","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morphoMarketLltv","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morphoMarketOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onMorphoRepay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onMorphoSupplyCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"proposeNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"debtRepaidAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"repayAndWithdraw","outputs":[{"internalType":"uint256","name":"debtRepaidAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawnAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"allowedCaller","type":"address"},{"components":[{"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"internalType":"bool","name":"allowed","type":"bool"}],"internalType":"struct IOrigamiElevatedAccess.ExplicitAccess[]","name":"access","type":"tuple[]"}],"name":"setExplicitAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSafeLtv","type":"uint256"}],"name":"setMaxSafeLtv","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setPositionOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"suppliedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"}],"name":"supply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"supplyAndBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapper","outputs":[{"internalType":"contract IOrigamiSwapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"amountWithdrawn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

6101606040523480156200001257600080fd5b50604051620056cf380380620056cf8339810160408190526200003591620007b7565b876200004181620001c1565b506001600160a01b0380881660a05286811660c05285811660805284811660e0528316610100526001600160601b03821661012081905281106200009857604051633494a40d60e21b815260040160405180910390fd5b60048190556200013c620001356040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a0016040528060c0516001600160a01b0316815260200160a0516001600160a01b0316815260200160e0516001600160a01b03168152602001610100516001600160a01b03168152602001610120516001600160601b0316815250905090565b60a0902090565b6101408190526080516200015c916001600160a01b03909116906200023e565b6000036200017d57604051633494a40d60e21b815260040160405180910390fd5b60a05162000198906001600160a01b03168660001962000302565b60c051620001b3906001600160a01b03168660001962000302565b505050505050505062000a67565b6000546001600160a01b031615620001ec57604051633006171960e21b815260040160405180910390fd5b6001600160a01b0381166200021c57604051634726455360e11b8152600060048201526024015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600080620002566200025084620003d8565b6200041f565b604051637784c68560e01b81529091506001600160a01b03851690637784c685906200028790849060040162000866565b600060405180830381865afa158015620002a5573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052620002cf9190810190620008c2565b600081518110620002e457620002e46200098b565b602002602001015160001c6001600160801b03169150505b92915050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b179091526200035c90859083906200046d16565b620003d2576040516001600160a01b038416602482015260006044820152620003c690859063095ea7b360e01b9060640160408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b03938416179052906200051d16565b620003d284826200051d565b50505050565b60006002826003604051602001620003fa929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c620002fc9190620009a1565b604080516001808252818301909252606091600091906020808301908036833701905050905082816000815181106200045c576200045c6200098b565b602090810291909101015292915050565b6000806000846001600160a01b0316846040516200048c9190620009e9565b6000604051808303816000865af19150503d8060008114620004cb576040519150601f19603f3d011682016040523d82523d6000602084013e620004d0565b606091505b5091509150818015620004fe575080511580620004fe575080806020019051810190620004fe919062000a07565b80156200051457506001600160a01b0385163b15155b95945050505050565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201526000906200056c906001600160a01b038516908490620005f6565b90508051600014806200059057508080602001905181019062000590919062000a07565b620005f15760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b606482015260840162000213565b505050565b60606200060784846000856200060f565b949350505050565b606082471015620006725760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b606482015260840162000213565b600080866001600160a01b03168587604051620006909190620009e9565b60006040518083038185875af1925050503d8060008114620006cf576040519150601f19603f3d011682016040523d82523d6000602084013e620006d4565b606091505b509092509050620006e887838387620006f3565b979650505050505050565b60608315620007675782516000036200075f576001600160a01b0385163b6200075f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162000213565b508162000607565b6200060783838151156200077e5781518083602001fd5b8060405162461bcd60e51b815260040162000213919062000a32565b80516001600160a01b0381168114620007b257600080fd5b919050565b600080600080600080600080610100898b031215620007d557600080fd5b620007e0896200079a565b9750620007f060208a016200079a565b96506200080060408a016200079a565b95506200081060608a016200079a565b94506200082060808a016200079a565b93506200083060a08a016200079a565b60c08a01519093506001600160601b03811681146200084e57600080fd5b8092505060e089015190509295985092959890939650565b6020808252825182820181905260009190848201906040850190845b81811015620008a05783518352928401929184019160010162000882565b50909695505050505050565b634e487b7160e01b600052604160045260246000fd5b60006020808385031215620008d657600080fd5b82516001600160401b0380821115620008ee57600080fd5b818501915085601f8301126200090357600080fd5b815181811115620009185762000918620008ac565b8060051b604051601f19603f83011681018181108582111715620009405762000940620008ac565b6040529182528482019250838101850191888311156200095f57600080fd5b938501935b828510156200097f5784518452938501939285019262000964565b98975050505050505050565b634e487b7160e01b600052603260045260246000fd5b80820180821115620002fc57634e487b7160e01b600052601160045260246000fd5b60005b83811015620009e0578181015183820152602001620009c6565b50506000910152565b60008251620009fd818460208701620009c3565b9190910192915050565b60006020828403121562000a1a57600080fd5b8151801515811462000a2b57600080fd5b9392505050565b602081526000825180602084015262000a53816040850160208701620009c3565b601f01601f19169190910160400192915050565b60805160a05160c05160e05161010051610120516101405161498262000d4d600039600081816104470152818161206201528181612871015281816128c80152612b110152600081816105730152818161065a015281816108a301528181610a3601528181610d7301528181610f1b015281816110660152818161123b015281816113cf0152818161166b01528181611a7b01528181611df1015281816121e2015281816123a401526126f10152600081816102fa015281816105440152818161087401528181610a0701528181610d4401528181610eec0152818161120c015281816113a001528181611a4c01528181611dc2015281816121b30152818161237501526126c20152600081816102bb0152818161051501528181610845015281816109d801528181610d1501528181610ebd01528181610fa8015281816111dd0152818161137101528181611a1d01528181611d93015281816121840152818161234601526126930152600081816103b8015281816104b7015281816107e70152818161097a01528181610b6101528181610cb701528181610e5f0152818161117f015281816113130152818161151b015281816115860152818161184d015281816119bf01528181611b0a01528181611d3501528181612126015281816122e801528181612635015281816127a20152612eaf01526000818161040b015281816104e601528181610816015281816109a901528181610abb01528181610b3e01528181610ce601528181610e8e015281816111ae01528181611342015281816114e60152818161155001528181611812015281816119ee01528181611b2d01528181611d6401528181612155015281816123170152818161244d015261266401526000818161069e015281816108ef0152818161193401528181611e30015281816120400152818161284f015281816128a6015281816129c101528181612a6501528181612b4001528181612bcd01528181612c9c0152612df601526149826000f3fe608060405234801561001057600080fd5b50600436106102765760003560e01c80639c82f2a411610160578063cbda3f69116100d8578063e5331e911161008c578063ebbc496511610071578063ebbc496514610724578063eedc07131461072c578063f8fd31001461073f57600080fd5b8063e5331e91146106fe578063e9e11f101461071157600080fd5b8063daeccc79116100bd578063daeccc79146106c0578063e322ad2b146106ee578063e4e88954146106f657600080fd5b8063cbda3f6914610655578063d8fbc8331461069957600080fd5b8063b1022fdf1161012f578063bf1eb64a11610114578063bf1eb64a14610627578063bfccf0ec1461062f578063cb2af14b1461064257600080fd5b8063b1022fdf14610601578063b1f8100d1461061457600080fd5b80639c82f2a4146105b55780639e784426146105c8578063a4fb4120146105db578063a7229fd9146105ee57600080fd5b806342e4a4ac116101f357806352059756116101c25780636ed71ede116101a75780636ed71ede146104425780638da5cb5b1461046957806390c9427c1461047c57600080fd5b8063520597561461040957806362625c3f1461042f57600080fd5b806342e4a4ac146103ad578063456dc17a146103b65780634b3fd148146103dc5780634eb75f40146103ef57600080fd5b80632b3297f91161024a578063371fd8e61161022f578063371fd8e6146103425780633d33809d1461035557806340a647e21461037857600080fd5b80632b3297f91461031c578063354030231461032f57600080fd5b8062f714ce1461027b57806305b4591c146102a1578063070881d1146102b65780631e99f224146102f5575b600080fd5b61028e610289366004613d7c565b610747565b6040519081526020015b60405180910390f35b6102b46102af366004613da8565b6108e4565b005b6102dd7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b039091168152602001610298565b6102dd7f000000000000000000000000000000000000000000000000000000000000000081565b6005546102dd906001600160a01b031681565b6102b461033d366004613e24565b610c1b565b61028e610350366004613e24565b610dc0565b610368610363366004613e24565b610f6b565b6040519015158152602001610298565b610380610f93565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610298565b61028e60045481565b7f00000000000000000000000000000000000000000000000000000000000000006102dd565b6102b46103ea366004613d7c565b6110e2565b600019805b60408051928352602083019190915201610298565b7f00000000000000000000000000000000000000000000000000000000000000006102dd565b6102b461043d366004613e3d565b611279565b61028e7f000000000000000000000000000000000000000000000000000000000000000081565b6000546102dd906001600160a01b031681565b6105a86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b6040516102989190613e72565b6102b46105c3366004613ebc565b611431565b6102b46105d6366004613e24565b61161e565b6102b46105e9366004613ebc565b61170b565b6102b46105fc366004613ed7565b6117b8565b6102b461060f366004613da8565b611929565b6102b4610622366004613ebc565b611be6565b61028e611cf5565b6102b461063d366004613f13565b611e5c565b6003546102dd906001600160a01b031681565b61067c7f000000000000000000000000000000000000000000000000000000000000000081565b6040516bffffffffffffffffffffffff9091168152602001610298565b6102dd7f000000000000000000000000000000000000000000000000000000000000000081565b6103686106ce366004613fb6565b600160209081526000928352604080842090915290825290205460ff1681565b61028e61202b565b61028e612031565b6103f461070c366004613e3d565b612087565b61028e61071f3660046140d9565b61224b565b6102b46124fb565b6103f461073a3660046140d9565b612596565b61028e61283f565b6003546000906001600160a01b031633146107a75761078a336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6107a757604051633006171960e21b815260040160405180910390fd5b6108dd83836108d86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b612975565b9392505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461092d57604051633006171960e21b815260040160405180910390fd5b600061093b82840184614183565b90506000610a6b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b90506000610a7e83600001513084612975565b83519091508114610af55782516040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016600482015260248101919091526044015b60405180910390fd5b600554835160208501516040517fee534a3f0000000000000000000000000000000000000000000000000000000081526000936001600160a01b03169263ee534a3f92610b89927f000000000000000000000000000000000000000000000000000000000000000092917f000000000000000000000000000000000000000000000000000000000000000091600401614208565b6020604051808303816000875af1158015610ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcc919061423a565b905086811015610c12576040517f2746152a0000000000000000000000000000000000000000000000000000000081526004810188905260248101829052604401610aec565b50505050505050565b6003546001600160a01b03163314610c7857610c5b336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b610c7857604051633006171960e21b815260040160405180910390fd5b610dbd81610da86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b60405180602001604052806000815250612a35565b50565b6003546000906001600160a01b03163314610e2057610e03336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b610e2057604051633006171960e21b815260040160405180910390fd5b610f6582610f506040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b60405180602001604052806000815250612ace565b92915050565b60006004546ec097ce7bc90715b34b9f1000000000610f8a91906142b1565b90911015919050565b600080600080600080610fa4612031565b95507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611004573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611028919061423a565b9450611032611cf5565b9350600061105187876ec097ce7bc90715b34b9f100000000084612d28565b9050611096816bffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016670de0b6b3a76400006000612d28565b9350846000036110aa5760001991506110d9565b6110bf85670de0b6b3a7640000836001612d28565b92506110d684670de0b6b3a7640000876000612d28565b91505b50909192939495565b6003546001600160a01b0316331461113f57611122336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61113f57604051633006171960e21b815260040160405180910390fd5b61127582826112706040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b612dc3565b5050565b6003546001600160a01b031633146112d6576112b9336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6112d657604051633006171960e21b815260040160405180910390fd5b60006114046040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b9050611420848260405180602001604052806000815250612a35565b61142b838383612dc3565b50505050565b61145f336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61147c57604051633006171960e21b815260040160405180910390fd5b6001600160a01b0381166114c7576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610aec565b6005546001600160a01b031680156115435761150e6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016826000612ee3565b6115436001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016826000612ee3565b6115796001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001683600019612ee3565b6115af6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001683600019612ee3565b6040516001600160a01b038316907f673779832598d6a388768ee342f8de96fdd5c39a468a6955377cf1405b9652b990600090a250600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b61164c336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61166957604051633006171960e21b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff1681106116d0576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048190556040518181527f994e009790ba0b9928d4b193d3ba06a943b02485bae67e9c3af2e1955e065cf29060200160405180910390a150565b611739336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61175657604051633006171960e21b815260040160405180910390fd5b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f014e5e53fac070a4a01cf0bc7ae3d30d5ee46aed56c4029a5b6f8e0ad21f8a5890600090a250565b6117e6336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61180357604051633006171960e21b815260040160405180910390fd5b61180b611cf5565b156118c3577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316148061188157507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316145b156118c3576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610aec565b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f90968360405161190891815260200190565b60405180910390a36119246001600160a01b0384168383613019565b505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461197257604051633006171960e21b815260040160405180910390fd5b600061198082840184614183565b90506000611ab06040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b9050611ac182600001513083612dc3565b600554825160208401516040517fee534a3f0000000000000000000000000000000000000000000000000000000081526000936001600160a01b03169263ee534a3f92611b55927f000000000000000000000000000000000000000000000000000000000000000092917f000000000000000000000000000000000000000000000000000000000000000091600401614208565b6020604051808303816000875af1158015611b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b98919061423a565b905085811015611bde576040517f2746152a0000000000000000000000000000000000000000000000000000000081526004810187905260248101829052604401610aec565b505050505050565b611c14336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b611c3157604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611c7c576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610aec565b600254600080546040516001600160a01b03808616948116939216917f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d891a4600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000611e57611e266040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169030613062565b905090565b611e8a336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b611ea757604051633006171960e21b815260040160405180910390fd5b6001600160a01b038316611ef2576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610aec565b604080518082019091526000808252602082015260005b8281101561202457838382818110611f2357611f236142ec565b905060400201803603810190611f399190614329565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b038716600090815260018352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561201d81614368565b9050611f09565b5050505050565b6000611e575b6000611e576001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f0000000000000000000000000000000000000000000000000000000000000000306130b7565b60035460009081906001600160a01b031633146120e9576120cc336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6120e957604051633006171960e21b815260040160405180910390fd5b60006122176040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b9050612233868260405180602001604052806000815250612ace565b9250612240858583612975565b915050935093915050565b6003546000906001600160a01b031633146122ab5761228e336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6122ab57604051633006171960e21b815260040160405180910390fd5b60006123d96040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b9050612419868260405180604001604052808981526020018881525060405160200161240591906143a1565b604051602081830303815290604052612a35565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528692506000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561249c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c0919061423a565b9050838111156124f1576124e4818360405180602001604052806000815250612a35565b6124ee81846143b4565b92505b5050949350505050565b6002546001600160a01b0316331461252657604051633006171960e21b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d55491a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600280549091169055565b60035460009081906001600160a01b031633146125f8576125db336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6125f857604051633006171960e21b815260040160405180910390fd5b60006127266040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006bffffffffffffffffffffffff16815250905090565b9050612766878260405180604001604052808a81526020018981525060405160200161275291906143a1565b604051602081830303815290604052612ace565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529093506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156127e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061280d919061423a565b90508481111561283457612831818360405180602001604052806000815250612ace565b92505b505094509492505050565b6000806128956001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000061316a565b905060006128ec6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000061323e565b90508082116128fc576000612906565b61290681836143c7565b9250505090565b600080546001600160a01b03848116911614806108dd5750506001600160a01b03821660009081526001602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008516845290915290205460ff1692915050565b60006000198414612986578361298e565b61298e612031565b6040517f8720316d0000000000000000000000000000000000000000000000000000000081529091506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638720316d906129fc9085908590309089906004016143da565b600060405180830381600087803b158015612a1657600080fd5b505af1158015612a2a573d6000803e3d6000fd5b505050509392505050565b6040517f238d65790000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063238d657990612aa090859087903090879060040161444a565b600060405180830381600087803b158015612aba57600080fd5b505af1158015610c12573d6000803e3d6000fd5b600080612ad9611cf5565b90508015612d205780851115612c6c576040517f93c520620000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906393c5206290604401606060405180830381865afa158015612b8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb391906144e7565b602001516fffffffffffffffffffffffffffffffff1690507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166320b76e818660008430896040518663ffffffff1660e01b8152600401612c2095949392919061454c565b60408051808303816000875af1158015612c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c6291906145c5565b509250612d209050565b6040517f20b76e810000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906320b76e8190612cda908790899060009030908a9060040161454c565b60408051808303816000875af1158015612cf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d1c91906145c5565b5091505b509392505050565b6000612d3585858561324d565b90506001826001811115612d4b57612d4b6145e9565b03612dbb578280612d5e57612d5e614253565b84860915612dbb57600019811015612d7857600101612dbb565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610aec565b949350505050565b6040517f50d8cd4b0000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906350d8cd4b90612e339085908890869030908a90600401614618565b60408051808303816000875af1158015612e51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e7591906145c5565b50905083811461142b576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016600482015260248101829052604401610aec565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052612f62848261333a565b61142b576040516001600160a01b03841660248201526000604482015261300f9085907f095ea7b300000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526133e1565b61142b84826133e1565b6040516001600160a01b0383166024820152604481018290526119249084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401612fab565b6000806130708460a0902090565b905060006130886001600160a01b03871683866134e3565b905060008061309788886135b9565b90945092506130ab915084905083836138ce565b98975050505050505050565b6000806130cc6130c785856138f3565b613968565b90506080856001600160a01b0316637784c685836040518263ffffffff1660e01b81526004016130fc9190614690565b600060405180830381865afa158015613119573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261314191908101906146d4565b600081518110613153576131536142ec565b6020026020010151901c60001c9150509392505050565b6000806131796130c7846139b3565b6040517f7784c6850000000000000000000000000000000000000000000000000000000081529091506001600160a01b03851690637784c685906131c1908490600401614690565b600060405180830381865afa1580156131de573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261320691908101906146d4565b600081518110613218576132186142ec565b602002602001015160001c6fffffffffffffffffffffffffffffffff1691505092915050565b6000806131796130c7846139f6565b60008080600019858709858702925082811083820303915050806000036132875783828161327d5761327d614253565b04925050506108dd565b8381106132d1576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610aec565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000806000846001600160a01b031684604051613357919061476e565b6000604051808303816000865af19150503d8060008114613394576040519150601f19603f3d011682016040523d82523d6000602084013e613399565b606091505b50915091508180156133c35750805115806133c35750808060200190518101906133c3919061478a565b80156133d857506001600160a01b0385163b15155b95945050505050565b6000613436826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a179092919063ffffffff16565b9050805160001480613457575080806020019051810190613457919061478a565b611924576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610aec565b6000806134f36130c785856138f3565b6040517f7784c6850000000000000000000000000000000000000000000000000000000081529091506001600160a01b03861690637784c6859061353b908490600401614690565b600060405180830381865afa158015613558573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261358091908101906146d4565b600081518110613592576135926142ec565b602002602001015160001c6fffffffffffffffffffffffffffffffff169150509392505050565b60008060008060006135cc8660a0902090565b6040517f5c60e39a000000000000000000000000000000000000000000000000000000008152600481018290529091506000906001600160a01b03891690635c60e39a9060240160c060405180830381865afa158015613630573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061365491906147a7565b9050600081608001516fffffffffffffffffffffffffffffffff164261367a91906143c7565b905080158015906136a0575060408201516fffffffffffffffffffffffffffffffff1615155b80156136b8575060608801516001600160a01b031615155b156138915760608801516040517f8c00bf6b0000000000000000000000000000000000000000000000000000000081526000916001600160a01b031690638c00bf6b9061370b908c908790600401614846565b602060405180830381865afa158015613728573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061374c919061423a565b9050600061377a61375d8385613a26565b60408601516fffffffffffffffffffffffffffffffff1690613a91565b905061378581613aa6565b8460400181815161379691906148f2565b6fffffffffffffffffffffffffffffffff169052506137b481613aa6565b845185906137c39083906148f2565b6fffffffffffffffffffffffffffffffff90811690915260a08601511615905061388e5760006138128560a001516fffffffffffffffffffffffffffffffff1683613a9190919063ffffffff16565b9050600061385a8287600001516fffffffffffffffffffffffffffffffff1661383b91906143c7565b60208801518491906fffffffffffffffffffffffffffffffff16613b2f565b905061386581613aa6565b8660200181815161387691906148f2565b6fffffffffffffffffffffffffffffffff1690525050505b50505b508051602082015160408301516060909301516fffffffffffffffffffffffffffffffff9283169b9183169a509282169850911695509350505050565b6000612dbb6138de6001856143b4565b6138eb620f4240856143b4565b869190613b54565b6000600182846002604051602001613915929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b03909416908301528101919091526060016040516020818303038152906040528051906020012060001c6108dd91906143b4565b604080516001808252818301909252606091600091906020808301908036833701905050905082816000815181106139a2576139a26142ec565b602090810291909101015292915050565b6000808260036040516020016139d3929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c610f6591906143b4565b600060018260036040516020016139d3929190918252602082015260400190565b6060612dbb8484600085613b80565b600080613a338385614922565b90506000613a548280613a4f670de0b6b3a76400006002614922565b613c81565b90506000613a708284613a4f670de0b6b3a76400006003614922565b905080613a7d83856143b4565b613a8791906143b4565b9695505050505050565b60006108dd8383670de0b6b3a7640000613c81565b60408051808201909152601481527f6d61782075696e7431323820657863656564656400000000000000000000000060208201526000906fffffffffffffffffffffffffffffffff831115613b28576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aec9190614939565b5090919050565b6000612dbb613b41620f4240846143b4565b613b4c6001866143b4565b869190613c81565b600081613b626001826143c7565b613b6c8587614922565b613b7691906143b4565b612dbb91906142b1565b606082471015613c12576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610aec565b600080866001600160a01b03168587604051613c2e919061476e565b60006040518083038185875af1925050503d8060008114613c6b576040519150601f19603f3d011682016040523d82523d6000602084013e613c70565b606091505b50915091506124ee87838387613c8e565b600081613b768486614922565b60608315613d17578251600003613d10576001600160a01b0385163b613d10576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610aec565b5081612dbb565b612dbb8383815115613d2c5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aec9190614939565b80356001600160a01b0381168114613d7757600080fd5b919050565b60008060408385031215613d8f57600080fd5b82359150613d9f60208401613d60565b90509250929050565b600080600060408486031215613dbd57600080fd5b83359250602084013567ffffffffffffffff80821115613ddc57600080fd5b818601915086601f830112613df057600080fd5b813581811115613dff57600080fd5b876020828501011115613e1157600080fd5b6020830194508093505050509250925092565b600060208284031215613e3657600080fd5b5035919050565b600080600060608486031215613e5257600080fd5b8335925060208401359150613e6960408501613d60565b90509250925092565b60a08101610f6582846001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b600060208284031215613ece57600080fd5b6108dd82613d60565b600080600060608486031215613eec57600080fd5b613ef584613d60565b9250613f0360208501613d60565b9150604084013590509250925092565b600080600060408486031215613f2857600080fd5b613f3184613d60565b9250602084013567ffffffffffffffff80821115613f4e57600080fd5b818601915086601f830112613f6257600080fd5b813581811115613f7157600080fd5b8760208260061b8501011115613e1157600080fd5b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114613d7757600080fd5b60008060408385031215613fc957600080fd5b613fd283613d60565b9150613d9f60208401613f86565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561403257614032613fe0565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561406157614061613fe0565b604052919050565b600082601f83011261407a57600080fd5b813567ffffffffffffffff81111561409457614094613fe0565b6140a76020601f19601f84011601614038565b8181528460208386010111156140bc57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156140ef57600080fd5b8435935060208501359250604085013567ffffffffffffffff81111561411457600080fd5b61412087828801614069565b949793965093946060013593505050565b60006040828403121561414357600080fd5b61414b61400f565b905081358152602082013567ffffffffffffffff81111561416b57600080fd5b61417784828501614069565b60208301525092915050565b60006020828403121561419557600080fd5b813567ffffffffffffffff8111156141ac57600080fd5b612dbb84828501614131565b60005b838110156141d35781810151838201526020016141bb565b50506000910152565b600081518084526141f48160208601602086016141b8565b601f01601f19169290920160200192915050565b60006001600160a01b03808716835285602084015280851660408401525060806060830152613a8760808301846141dc565b60006020828403121561424c57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000826142e7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8015158114610dbd57600080fd5b60006040828403121561433b57600080fd5b61434361400f565b61434c83613f86565b8152602083013561435c8161431b565b60208201529392505050565b6000600019820361437b5761437b614282565b5060010190565b805182526000602082015160406020850152612dbb60408501826141dc565b6020815260006108dd6020830184614382565b80820180821115610f6557610f65614282565b81810381811115610f6557610f65614282565b610100810161442582876001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b60a08201949094526001600160a01b0392831660c0820152911660e090910152919050565b600061010061449583886001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b8560a08401526001600160a01b03851660c08401528060e08401526144bc818401856141dc565b979650505050505050565b80516fffffffffffffffffffffffffffffffff81168114613d7757600080fd5b6000606082840312156144f957600080fd5b6040516060810181811067ffffffffffffffff8211171561451c5761451c613fe0565b6040528251815261452f602084016144c7565b6020820152614540604084016144c7565b60408201529392505050565b600061012061459783896001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b8660a08401528560c08401526001600160a01b03851660e0840152806101008401526130ab818401856141dc565b600080604083850312156145d857600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b610120810161466382886001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b60a082019590955260c08101939093526001600160a01b0391821660e08401521661010090910152919050565b6020808252825182820181905260009190848201906040850190845b818110156146c8578351835292840192918401916001016146ac565b50909695505050505050565b600060208083850312156146e757600080fd5b825167ffffffffffffffff808211156146ff57600080fd5b818501915085601f83011261471357600080fd5b81518181111561472557614725613fe0565b8060051b9150614736848301614038565b818152918301840191848101908884111561475057600080fd5b938501935b838510156130ab57845182529385019390850190614755565b600082516147808184602087016141b8565b9190910192915050565b60006020828403121561479c57600080fd5b81516108dd8161431b565b600060c082840312156147b957600080fd5b60405160c0810181811067ffffffffffffffff821117156147dc576147dc613fe0565b6040526147e8836144c7565b81526147f6602084016144c7565b6020820152614807604084016144c7565b6040820152614818606084016144c7565b6060820152614829608084016144c7565b608082015261483a60a084016144c7565b60a08201529392505050565b610160810161489182856001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b6fffffffffffffffffffffffffffffffff8084511660a08401528060208501511660c08401528060408501511660e084015280606085015116610100840152806080850151166101208401528060a085015116610140840152509392505050565b6fffffffffffffffffffffffffffffffff81811683821601908082111561491b5761491b614282565b5092915050565b8082028115828204841417610f6557610f65614282565b6020815260006108dd60208301846141dc56fea26469706673582212201ee75513a55f32d7e3cc7a4add203a5f3866e3d5a527a8bf2d3c08cb2b4cf26b64736f6c63430008130033000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d80000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc0000000000000000000000000000000000000000000000000bef55718ad600000000000000000000000000000000000000000000000000000b96841373738000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102765760003560e01c80639c82f2a411610160578063cbda3f69116100d8578063e5331e911161008c578063ebbc496511610071578063ebbc496514610724578063eedc07131461072c578063f8fd31001461073f57600080fd5b8063e5331e91146106fe578063e9e11f101461071157600080fd5b8063daeccc79116100bd578063daeccc79146106c0578063e322ad2b146106ee578063e4e88954146106f657600080fd5b8063cbda3f6914610655578063d8fbc8331461069957600080fd5b8063b1022fdf1161012f578063bf1eb64a11610114578063bf1eb64a14610627578063bfccf0ec1461062f578063cb2af14b1461064257600080fd5b8063b1022fdf14610601578063b1f8100d1461061457600080fd5b80639c82f2a4146105b55780639e784426146105c8578063a4fb4120146105db578063a7229fd9146105ee57600080fd5b806342e4a4ac116101f357806352059756116101c25780636ed71ede116101a75780636ed71ede146104425780638da5cb5b1461046957806390c9427c1461047c57600080fd5b8063520597561461040957806362625c3f1461042f57600080fd5b806342e4a4ac146103ad578063456dc17a146103b65780634b3fd148146103dc5780634eb75f40146103ef57600080fd5b80632b3297f91161024a578063371fd8e61161022f578063371fd8e6146103425780633d33809d1461035557806340a647e21461037857600080fd5b80632b3297f91461031c578063354030231461032f57600080fd5b8062f714ce1461027b57806305b4591c146102a1578063070881d1146102b65780631e99f224146102f5575b600080fd5b61028e610289366004613d7c565b610747565b6040519081526020015b60405180910390f35b6102b46102af366004613da8565b6108e4565b005b6102dd7f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a81565b6040516001600160a01b039091168152602001610298565b6102dd7f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc81565b6005546102dd906001600160a01b031681565b6102b461033d366004613e24565b610c1b565b61028e610350366004613e24565b610dc0565b610368610363366004613e24565b610f6b565b6040519015158152602001610298565b610380610f93565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610298565b61028e60045481565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26102dd565b6102b46103ea366004613d7c565b6110e2565b600019805b60408051928352602083019190915201610298565b7f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6102dd565b6102b461043d366004613e3d565b611279565b61028e7f698fe98247a40c5771537b5786b2f3f9d78eb487b4ce4d75533cd0e94d88a11581565b6000546102dd906001600160a01b031681565b6105a86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b6040516102989190613e72565b6102b46105c3366004613ebc565b611431565b6102b46105d6366004613e24565b61161e565b6102b46105e9366004613ebc565b61170b565b6102b46105fc366004613ed7565b6117b8565b6102b461060f366004613da8565b611929565b6102b4610622366004613ebc565b611be6565b61028e611cf5565b6102b461063d366004613f13565b611e5c565b6003546102dd906001600160a01b031681565b61067c7f0000000000000000000000000000000000000000000000000bef55718ad6000081565b6040516bffffffffffffffffffffffff9091168152602001610298565b6102dd7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb81565b6103686106ce366004613fb6565b600160209081526000928352604080842090915290825290205460ff1681565b61028e61202b565b61028e612031565b6103f461070c366004613e3d565b612087565b61028e61071f3660046140d9565b61224b565b6102b46124fb565b6103f461073a3660046140d9565b612596565b61028e61283f565b6003546000906001600160a01b031633146107a75761078a336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6107a757604051633006171960e21b815260040160405180910390fd5b6108dd83836108d86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b612975565b9392505050565b336001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb161461092d57604051633006171960e21b815260040160405180910390fd5b600061093b82840184614183565b90506000610a6b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b90506000610a7e83600001513084612975565b83519091508114610af55782516040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee16600482015260248101919091526044015b60405180910390fd5b600554835160208501516040517fee534a3f0000000000000000000000000000000000000000000000000000000081526000936001600160a01b03169263ee534a3f92610b89927f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee92917f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc291600401614208565b6020604051808303816000875af1158015610ba8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bcc919061423a565b905086811015610c12576040517f2746152a0000000000000000000000000000000000000000000000000000000081526004810188905260248101829052604401610aec565b50505050505050565b6003546001600160a01b03163314610c7857610c5b336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b610c7857604051633006171960e21b815260040160405180910390fd5b610dbd81610da86040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b60405180602001604052806000815250612a35565b50565b6003546000906001600160a01b03163314610e2057610e03336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b610e2057604051633006171960e21b815260040160405180910390fd5b610f6582610f506040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b60405180602001604052806000815250612ace565b92915050565b60006004546ec097ce7bc90715b34b9f1000000000610f8a91906142b1565b90911015919050565b600080600080600080610fa4612031565b95507f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611004573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611028919061423a565b9450611032611cf5565b9350600061105187876ec097ce7bc90715b34b9f100000000084612d28565b9050611096816bffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000bef55718ad6000016670de0b6b3a76400006000612d28565b9350846000036110aa5760001991506110d9565b6110bf85670de0b6b3a7640000836001612d28565b92506110d684670de0b6b3a7640000876000612d28565b91505b50909192939495565b6003546001600160a01b0316331461113f57611122336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61113f57604051633006171960e21b815260040160405180910390fd5b61127582826112706040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b612dc3565b5050565b6003546001600160a01b031633146112d6576112b9336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6112d657604051633006171960e21b815260040160405180910390fd5b60006114046040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b9050611420848260405180602001604052806000815250612a35565b61142b838383612dc3565b50505050565b61145f336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61147c57604051633006171960e21b815260040160405180910390fd5b6001600160a01b0381166114c7576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610aec565b6005546001600160a01b031680156115435761150e6001600160a01b037f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee16826000612ee3565b6115436001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216826000612ee3565b6115796001600160a01b037f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee1683600019612ee3565b6115af6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21683600019612ee3565b6040516001600160a01b038316907f673779832598d6a388768ee342f8de96fdd5c39a468a6955377cf1405b9652b990600090a250600580547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b61164c336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61166957604051633006171960e21b815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff1681106116d0576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048190556040518181527f994e009790ba0b9928d4b193d3ba06a943b02485bae67e9c3af2e1955e065cf29060200160405180910390a150565b611739336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61175657604051633006171960e21b815260040160405180910390fd5b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517f014e5e53fac070a4a01cf0bc7ae3d30d5ee46aed56c4029a5b6f8e0ad21f8a5890600090a250565b6117e6336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b61180357604051633006171960e21b815260040160405180910390fd5b61180b611cf5565b156118c3577f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b0316836001600160a01b0316148061188157507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316836001600160a01b0316145b156118c3576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610aec565b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f90968360405161190891815260200190565b60405180910390a36119246001600160a01b0384168383613019565b505050565b336001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb161461197257604051633006171960e21b815260040160405180910390fd5b600061198082840184614183565b90506000611ab06040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b9050611ac182600001513083612dc3565b600554825160208401516040517fee534a3f0000000000000000000000000000000000000000000000000000000081526000936001600160a01b03169263ee534a3f92611b55927f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc292917f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee91600401614208565b6020604051808303816000875af1158015611b74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611b98919061423a565b905085811015611bde576040517f2746152a0000000000000000000000000000000000000000000000000000000081526004810187905260248101829052604401610aec565b505050505050565b611c14336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b611c3157604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611c7c576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610aec565b600254600080546040516001600160a01b03808616948116939216917f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d891a4600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6000611e57611e266040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b6001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb169030613062565b905090565b611e8a336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b611ea757604051633006171960e21b815260040160405180910390fd5b6001600160a01b038316611ef2576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610aec565b604080518082019091526000808252602082015260005b8281101561202457838382818110611f2357611f236142ec565b905060400201803603810190611f399190614329565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b038716600090815260018352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001691151591909117905561201d81614368565b9050611f09565b5050505050565b6000611e575b6000611e576001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb167f698fe98247a40c5771537b5786b2f3f9d78eb487b4ce4d75533cd0e94d88a115306130b7565b60035460009081906001600160a01b031633146120e9576120cc336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6120e957604051633006171960e21b815260040160405180910390fd5b60006122176040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b9050612233868260405180602001604052806000815250612ace565b9250612240858583612975565b915050935093915050565b6003546000906001600160a01b031633146122ab5761228e336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6122ab57604051633006171960e21b815260040160405180910390fd5b60006123d96040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b9050612419868260405180604001604052808981526020018881525060405160200161240591906143a1565b604051602081830303815290604052612a35565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528692506000907f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b0316906370a0823190602401602060405180830381865afa15801561249c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124c0919061423a565b9050838111156124f1576124e4818360405180602001604052806000815250612a35565b6124ee81846143b4565b92505b5050949350505050565b6002546001600160a01b0316331461252657604051633006171960e21b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d55491a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600280549091169055565b60035460009081906001600160a01b031633146125f8576125db336000357fffffffff000000000000000000000000000000000000000000000000000000001661290d565b6125f857604051633006171960e21b815260040160405180910390fd5b60006127266040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526040518060a001604052807f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b031681526020017f000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee6001600160a01b031681526020017f0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a6001600160a01b031681526020017f000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc6001600160a01b031681526020017f0000000000000000000000000000000000000000000000000bef55718ad600006bffffffffffffffffffffffff16815250905090565b9050612766878260405180604001604052808a81526020018981525060405160200161275291906143a1565b604051602081830303815290604052612ace565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529093506000906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa1580156127e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061280d919061423a565b90508481111561283457612831818360405180602001604052806000815250612ace565b92505b505094509492505050565b6000806128956001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb167f698fe98247a40c5771537b5786b2f3f9d78eb487b4ce4d75533cd0e94d88a11561316a565b905060006128ec6001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb167f698fe98247a40c5771537b5786b2f3f9d78eb487b4ce4d75533cd0e94d88a11561323e565b90508082116128fc576000612906565b61290681836143c7565b9250505090565b600080546001600160a01b03848116911614806108dd5750506001600160a01b03821660009081526001602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000008516845290915290205460ff1692915050565b60006000198414612986578361298e565b61298e612031565b6040517f8720316d0000000000000000000000000000000000000000000000000000000081529091506001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb1690638720316d906129fc9085908590309089906004016143da565b600060405180830381600087803b158015612a1657600080fd5b505af1158015612a2a573d6000803e3d6000fd5b505050509392505050565b6040517f238d65790000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb169063238d657990612aa090859087903090879060040161444a565b600060405180830381600087803b158015612aba57600080fd5b505af1158015610c12573d6000803e3d6000fd5b600080612ad9611cf5565b90508015612d205780851115612c6c576040517f93c520620000000000000000000000000000000000000000000000000000000081527f698fe98247a40c5771537b5786b2f3f9d78eb487b4ce4d75533cd0e94d88a11560048201523060248201526000907f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb6001600160a01b0316906393c5206290604401606060405180830381865afa158015612b8f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bb391906144e7565b602001516fffffffffffffffffffffffffffffffff1690507f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb6001600160a01b03166320b76e818660008430896040518663ffffffff1660e01b8152600401612c2095949392919061454c565b60408051808303816000875af1158015612c3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c6291906145c5565b509250612d209050565b6040517f20b76e810000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb16906320b76e8190612cda908790899060009030908a9060040161454c565b60408051808303816000875af1158015612cf8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d1c91906145c5565b5091505b509392505050565b6000612d3585858561324d565b90506001826001811115612d4b57612d4b6145e9565b03612dbb578280612d5e57612d5e614253565b84860915612dbb57600019811015612d7857600101612dbb565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610aec565b949350505050565b6040517f50d8cd4b0000000000000000000000000000000000000000000000000000000081526000906001600160a01b037f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb16906350d8cd4b90612e339085908890869030908a90600401614618565b60408051808303816000875af1158015612e51573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e7591906145c5565b50905083811461142b576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216600482015260248101829052604401610aec565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b300000000000000000000000000000000000000000000000000000000179052612f62848261333a565b61142b576040516001600160a01b03841660248201526000604482015261300f9085907f095ea7b300000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526133e1565b61142b84826133e1565b6040516001600160a01b0383166024820152604481018290526119249084907fa9059cbb0000000000000000000000000000000000000000000000000000000090606401612fab565b6000806130708460a0902090565b905060006130886001600160a01b03871683866134e3565b905060008061309788886135b9565b90945092506130ab915084905083836138ce565b98975050505050505050565b6000806130cc6130c785856138f3565b613968565b90506080856001600160a01b0316637784c685836040518263ffffffff1660e01b81526004016130fc9190614690565b600060405180830381865afa158015613119573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261314191908101906146d4565b600081518110613153576131536142ec565b6020026020010151901c60001c9150509392505050565b6000806131796130c7846139b3565b6040517f7784c6850000000000000000000000000000000000000000000000000000000081529091506001600160a01b03851690637784c685906131c1908490600401614690565b600060405180830381865afa1580156131de573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261320691908101906146d4565b600081518110613218576132186142ec565b602002602001015160001c6fffffffffffffffffffffffffffffffff1691505092915050565b6000806131796130c7846139f6565b60008080600019858709858702925082811083820303915050806000036132875783828161327d5761327d614253565b04925050506108dd565b8381106132d1576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610aec565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6000806000846001600160a01b031684604051613357919061476e565b6000604051808303816000865af19150503d8060008114613394576040519150601f19603f3d011682016040523d82523d6000602084013e613399565b606091505b50915091508180156133c35750805115806133c35750808060200190518101906133c3919061478a565b80156133d857506001600160a01b0385163b15155b95945050505050565b6000613436826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613a179092919063ffffffff16565b9050805160001480613457575080806020019051810190613457919061478a565b611924576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610aec565b6000806134f36130c785856138f3565b6040517f7784c6850000000000000000000000000000000000000000000000000000000081529091506001600160a01b03861690637784c6859061353b908490600401614690565b600060405180830381865afa158015613558573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261358091908101906146d4565b600081518110613592576135926142ec565b602002602001015160001c6fffffffffffffffffffffffffffffffff169150509392505050565b60008060008060006135cc8660a0902090565b6040517f5c60e39a000000000000000000000000000000000000000000000000000000008152600481018290529091506000906001600160a01b03891690635c60e39a9060240160c060405180830381865afa158015613630573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061365491906147a7565b9050600081608001516fffffffffffffffffffffffffffffffff164261367a91906143c7565b905080158015906136a0575060408201516fffffffffffffffffffffffffffffffff1615155b80156136b8575060608801516001600160a01b031615155b156138915760608801516040517f8c00bf6b0000000000000000000000000000000000000000000000000000000081526000916001600160a01b031690638c00bf6b9061370b908c908790600401614846565b602060405180830381865afa158015613728573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061374c919061423a565b9050600061377a61375d8385613a26565b60408601516fffffffffffffffffffffffffffffffff1690613a91565b905061378581613aa6565b8460400181815161379691906148f2565b6fffffffffffffffffffffffffffffffff169052506137b481613aa6565b845185906137c39083906148f2565b6fffffffffffffffffffffffffffffffff90811690915260a08601511615905061388e5760006138128560a001516fffffffffffffffffffffffffffffffff1683613a9190919063ffffffff16565b9050600061385a8287600001516fffffffffffffffffffffffffffffffff1661383b91906143c7565b60208801518491906fffffffffffffffffffffffffffffffff16613b2f565b905061386581613aa6565b8660200181815161387691906148f2565b6fffffffffffffffffffffffffffffffff1690525050505b50505b508051602082015160408301516060909301516fffffffffffffffffffffffffffffffff9283169b9183169a509282169850911695509350505050565b6000612dbb6138de6001856143b4565b6138eb620f4240856143b4565b869190613b54565b6000600182846002604051602001613915929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b03909416908301528101919091526060016040516020818303038152906040528051906020012060001c6108dd91906143b4565b604080516001808252818301909252606091600091906020808301908036833701905050905082816000815181106139a2576139a26142ec565b602090810291909101015292915050565b6000808260036040516020016139d3929190918252602082015260400190565b6040516020818303038152906040528051906020012060001c610f6591906143b4565b600060018260036040516020016139d3929190918252602082015260400190565b6060612dbb8484600085613b80565b600080613a338385614922565b90506000613a548280613a4f670de0b6b3a76400006002614922565b613c81565b90506000613a708284613a4f670de0b6b3a76400006003614922565b905080613a7d83856143b4565b613a8791906143b4565b9695505050505050565b60006108dd8383670de0b6b3a7640000613c81565b60408051808201909152601481527f6d61782075696e7431323820657863656564656400000000000000000000000060208201526000906fffffffffffffffffffffffffffffffff831115613b28576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aec9190614939565b5090919050565b6000612dbb613b41620f4240846143b4565b613b4c6001866143b4565b869190613c81565b600081613b626001826143c7565b613b6c8587614922565b613b7691906143b4565b612dbb91906142b1565b606082471015613c12576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610aec565b600080866001600160a01b03168587604051613c2e919061476e565b60006040518083038185875af1925050503d8060008114613c6b576040519150601f19603f3d011682016040523d82523d6000602084013e613c70565b606091505b50915091506124ee87838387613c8e565b600081613b768486614922565b60608315613d17578251600003613d10576001600160a01b0385163b613d10576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610aec565b5081612dbb565b612dbb8383815115613d2c5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610aec9190614939565b80356001600160a01b0381168114613d7757600080fd5b919050565b60008060408385031215613d8f57600080fd5b82359150613d9f60208401613d60565b90509250929050565b600080600060408486031215613dbd57600080fd5b83359250602084013567ffffffffffffffff80821115613ddc57600080fd5b818601915086601f830112613df057600080fd5b813581811115613dff57600080fd5b876020828501011115613e1157600080fd5b6020830194508093505050509250925092565b600060208284031215613e3657600080fd5b5035919050565b600080600060608486031215613e5257600080fd5b8335925060208401359150613e6960408501613d60565b90509250925092565b60a08101610f6582846001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b600060208284031215613ece57600080fd5b6108dd82613d60565b600080600060608486031215613eec57600080fd5b613ef584613d60565b9250613f0360208501613d60565b9150604084013590509250925092565b600080600060408486031215613f2857600080fd5b613f3184613d60565b9250602084013567ffffffffffffffff80821115613f4e57600080fd5b818601915086601f830112613f6257600080fd5b813581811115613f7157600080fd5b8760208260061b8501011115613e1157600080fd5b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114613d7757600080fd5b60008060408385031215613fc957600080fd5b613fd283613d60565b9150613d9f60208401613f86565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff8111828210171561403257614032613fe0565b60405290565b604051601f8201601f1916810167ffffffffffffffff8111828210171561406157614061613fe0565b604052919050565b600082601f83011261407a57600080fd5b813567ffffffffffffffff81111561409457614094613fe0565b6140a76020601f19601f84011601614038565b8181528460208386010111156140bc57600080fd5b816020850160208301376000918101602001919091529392505050565b600080600080608085870312156140ef57600080fd5b8435935060208501359250604085013567ffffffffffffffff81111561411457600080fd5b61412087828801614069565b949793965093946060013593505050565b60006040828403121561414357600080fd5b61414b61400f565b905081358152602082013567ffffffffffffffff81111561416b57600080fd5b61417784828501614069565b60208301525092915050565b60006020828403121561419557600080fd5b813567ffffffffffffffff8111156141ac57600080fd5b612dbb84828501614131565b60005b838110156141d35781810151838201526020016141bb565b50506000910152565b600081518084526141f48160208601602086016141b8565b601f01601f19169290920160200192915050565b60006001600160a01b03808716835285602084015280851660408401525060806060830152613a8760808301846141dc565b60006020828403121561424c57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000826142e7577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8015158114610dbd57600080fd5b60006040828403121561433b57600080fd5b61434361400f565b61434c83613f86565b8152602083013561435c8161431b565b60208201529392505050565b6000600019820361437b5761437b614282565b5060010190565b805182526000602082015160406020850152612dbb60408501826141dc565b6020815260006108dd6020830184614382565b80820180821115610f6557610f65614282565b81810381811115610f6557610f65614282565b610100810161442582876001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b60a08201949094526001600160a01b0392831660c0820152911660e090910152919050565b600061010061449583886001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b8560a08401526001600160a01b03851660c08401528060e08401526144bc818401856141dc565b979650505050505050565b80516fffffffffffffffffffffffffffffffff81168114613d7757600080fd5b6000606082840312156144f957600080fd5b6040516060810181811067ffffffffffffffff8211171561451c5761451c613fe0565b6040528251815261452f602084016144c7565b6020820152614540604084016144c7565b60408201529392505050565b600061012061459783896001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b8660a08401528560c08401526001600160a01b03851660e0840152806101008401526130ab818401856141dc565b600080604083850312156145d857600080fd5b505080516020909101519092909150565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b610120810161466382886001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b60a082019590955260c08101939093526001600160a01b0391821660e08401521661010090910152919050565b6020808252825182820181905260009190848201906040850190845b818110156146c8578351835292840192918401916001016146ac565b50909695505050505050565b600060208083850312156146e757600080fd5b825167ffffffffffffffff808211156146ff57600080fd5b818501915085601f83011261471357600080fd5b81518181111561472557614725613fe0565b8060051b9150614736848301614038565b818152918301840191848101908884111561475057600080fd5b938501935b838510156130ab57845182529385019390850190614755565b600082516147808184602087016141b8565b9190910192915050565b60006020828403121561479c57600080fd5b81516108dd8161431b565b600060c082840312156147b957600080fd5b60405160c0810181811067ffffffffffffffff821117156147dc576147dc613fe0565b6040526147e8836144c7565b81526147f6602084016144c7565b6020820152614807604084016144c7565b6040820152614818606084016144c7565b6060820152614829608084016144c7565b608082015261483a60a084016144c7565b60a08201529392505050565b610160810161489182856001600160a01b0380825116835280602083015116602084015280604083015116604084015280606083015116606084015250608081015160808301525050565b6fffffffffffffffffffffffffffffffff8084511660a08401528060208501511660c08401528060408501511660e084015280606085015116610100840152806080850151166101208401528060a085015116610140840152509392505050565b6fffffffffffffffffffffffffffffffff81811683821601908082111561491b5761491b614282565b5092915050565b8082028115828204841417610f6557610f65614282565b6020815260006108dd60208301846141dc56fea26469706673582212201ee75513a55f32d7e3cc7a4add203a5f3866e3d5a527a8bf2d3c08cb2b4cf26b64736f6c63430008130033

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

000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d80000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc0000000000000000000000000000000000000000000000000bef55718ad600000000000000000000000000000000000000000000000000000b96841373738000

-----Decoded View---------------
Arg [0] : _initialOwner (address): 0xb20AaE0Fe007519b7cE6f090a2aB8353B3Da5d80
Arg [1] : __supplyToken (address): 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee
Arg [2] : __borrowToken (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : _morphoAddress (address): 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb
Arg [4] : _morphoMarketOracle (address): 0x3fa58b74e9a8eA8768eb33c8453e9C2Ed089A40a
Arg [5] : _morphoMarketIrm (address): 0x870aC11D48B15DB9a138Cf899d20F13F79Ba00BC
Arg [6] : _morphoMarketLltv (uint96): 860000000000000000
Arg [7] : _maxSafeLtv (uint256): 835000000000000000

-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d80
Arg [1] : 000000000000000000000000cd5fe23c85820f7b72d0926fc9b05b43e359b7ee
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
Arg [4] : 0000000000000000000000003fa58b74e9a8ea8768eb33c8453e9c2ed089a40a
Arg [5] : 000000000000000000000000870ac11d48b15db9a138cf899d20f13f79ba00bc
Arg [6] : 0000000000000000000000000000000000000000000000000bef55718ad60000
Arg [7] : 0000000000000000000000000000000000000000000000000b96841373738000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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