Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
OrigamiMorphoBorrowAndLend
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 10000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
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(); } _; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import {MarketParams, Market} from "./IMorpho.sol"; /// @title IIrm /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement. interface IIrm { /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`. /// @dev Assumes that `market` corresponds to `marketParams`. function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256); /// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any /// storage. /// @dev Assumes that `market` corresponds to `marketParams`. function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; type Id is bytes32; struct MarketParams { address loanToken; address collateralToken; address oracle; address irm; uint256 lltv; } /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest /// accrual. struct Position { uint256 supplyShares; uint128 borrowShares; uint128 collateral; } /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last /// interest accrual. struct Market { uint128 totalSupplyAssets; uint128 totalSupplyShares; uint128 totalBorrowAssets; uint128 totalBorrowShares; uint128 lastUpdate; uint128 fee; } struct Authorization { address authorizer; address authorized; bool isAuthorized; uint256 nonce; uint256 deadline; } struct Signature { uint8 v; bytes32 r; bytes32 s; } /// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho. /// @dev Consider using the IMorpho interface instead of this one. interface IMorphoBase { /// @notice The EIP-712 domain separator. /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing /// the same chain id because the domain separator would be the same. function DOMAIN_SEPARATOR() external view returns (bytes32); /// @notice The owner of the contract. /// @dev It has the power to change the owner. /// @dev It has the power to set fees on markets and set the fee recipient. /// @dev It has the power to enable but not disable IRMs and LLTVs. function owner() external view returns (address); /// @notice The fee recipient of all markets. /// @dev The recipient receives the fees of a given market through a supply position on that market. function feeRecipient() external view returns (address); /// @notice Whether the `irm` is enabled. function isIrmEnabled(address irm) external view returns (bool); /// @notice Whether the `lltv` is enabled. function isLltvEnabled(uint256 lltv) external view returns (bool); /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets. /// @dev Anyone is authorized to modify their own positions, regardless of this variable. function isAuthorized(address authorizer, address authorized) external view returns (bool); /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures. function nonce(address authorizer) external view returns (uint256); /// @notice Sets `newOwner` as `owner` of the contract. /// @dev Warning: No two-step transfer ownership. /// @dev Warning: The owner can be set to the zero address. function setOwner(address newOwner) external; /// @notice Enables `irm` as a possible IRM for market creation. /// @dev Warning: It is not possible to disable an IRM. function enableIrm(address irm) external; /// @notice Enables `lltv` as a possible LLTV for market creation. /// @dev Warning: It is not possible to disable a LLTV. function enableLltv(uint256 lltv) external; /// @notice Sets the `newFee` for the given market `marketParams`. /// @param newFee The new fee, scaled by WAD. /// @dev Warning: The recipient can be the zero address. function setFee(MarketParams memory marketParams, uint256 newFee) external; /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee. /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost. /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes. function setFeeRecipient(address newFeeRecipient) external; /// @notice Creates the market `marketParams`. /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees /// Morpho behaves as expected: /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`. /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with /// burn functions are not supported. /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`. /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported. /// - The IRM should not re-enter Morpho. /// - The oracle should return a price with the correct scaling. /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties /// (funds could get stuck): /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue. /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and /// `toSharesDown` overflow. /// - The IRM can revert on `borrowRate`. /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest` /// overflow. /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and /// `liquidate` from being used under certain market conditions. /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or /// the computation of `assetsRepaid` in `liquidate` overflow. /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to /// the point where `totalBorrowShares` is very large and borrowing overflows. function createMarket(MarketParams memory marketParams) external; /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's /// `onMorphoSupply` function with the given `data`. /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific /// amount of shares is given for full compatibility and precision. /// @dev Supplying a large amount can revert for overflow. /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage. /// Consider using the `assets` parameter to avoid this. /// @param marketParams The market to supply assets to. /// @param assets The amount of assets to supply. /// @param shares The amount of shares to mint. /// @param onBehalf The address that will own the increased supply position. /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed. /// @return assetsSupplied The amount of assets supplied. /// @return sharesSupplied The amount of shares minted. function supply( MarketParams memory marketParams, uint256 assets, uint256 shares, address onBehalf, bytes memory data ) external returns (uint256 assetsSupplied, uint256 sharesSupplied); /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`. /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`. /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow. /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to /// conversion roundings between shares and assets. /// @param marketParams The market to withdraw assets from. /// @param assets The amount of assets to withdraw. /// @param shares The amount of shares to burn. /// @param onBehalf The address of the owner of the supply position. /// @param receiver The address that will receive the withdrawn assets. /// @return assetsWithdrawn The amount of assets withdrawn. /// @return sharesWithdrawn The amount of shares burned. function withdraw( MarketParams memory marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn); /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`. /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is /// given for full compatibility and precision. /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Borrowing a large amount can revert for overflow. /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage. /// Consider using the `assets` parameter to avoid this. /// @param marketParams The market to borrow assets from. /// @param assets The amount of assets to borrow. /// @param shares The amount of shares to mint. /// @param onBehalf The address that will own the increased borrow position. /// @param receiver The address that will receive the borrowed assets. /// @return assetsBorrowed The amount of assets borrowed. /// @return sharesBorrowed The amount of shares minted. function borrow( MarketParams memory marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed); /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's /// `onMorphoReplay` function with the given `data`. /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`. /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow. /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion /// roundings between shares and assets. /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow. /// @param marketParams The market to repay assets to. /// @param assets The amount of assets to repay. /// @param shares The amount of shares to burn. /// @param onBehalf The address of the owner of the debt position. /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed. /// @return assetsRepaid The amount of assets repaid. /// @return sharesRepaid The amount of shares burned. function repay( MarketParams memory marketParams, uint256 assets, uint256 shares, address onBehalf, bytes memory data ) external returns (uint256 assetsRepaid, uint256 sharesRepaid); /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's /// `onMorphoSupplyCollateral` function with the given `data`. /// @dev Interest are not accrued since it's not required and it saves gas. /// @dev Supplying a large amount can revert for overflow. /// @param marketParams The market to supply collateral to. /// @param assets The amount of collateral to supply. /// @param onBehalf The address that will own the increased collateral position. /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed. function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data) external; /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`. /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions. /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow. /// @param marketParams The market to withdraw collateral from. /// @param assets The amount of collateral to withdraw. /// @param onBehalf The address of the owner of the collateral position. /// @param receiver The address that will receive the collateral assets. function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver) external; /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's /// `onMorphoLiquidate` function with the given `data`. /// @dev Either `seizedAssets` or `repaidShares` should be zero. /// @dev Seizing more than the collateral balance will underflow and revert without any error message. /// @dev Repaying more than the borrow balance will underflow and revert without any error message. /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow. /// @param marketParams The market of the position. /// @param borrower The owner of the position. /// @param seizedAssets The amount of collateral to seize. /// @param repaidShares The amount of shares to repay. /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed. /// @return The amount of assets seized. /// @return The amount of assets repaid. function liquidate( MarketParams memory marketParams, address borrower, uint256 seizedAssets, uint256 repaidShares, bytes memory data ) external returns (uint256, uint256); /// @notice Executes a flash loan. /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all /// markets combined, plus donations). /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached: /// - `flashFee` is zero. /// - `maxFlashLoan` is the token's balance of this contract. /// - The receiver of `assets` is the caller. /// @param token The token to flash loan. /// @param assets The amount of assets to flash loan. /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback. function flashLoan(address token, uint256 assets, bytes calldata data) external; /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions. /// @param authorized The authorized address. /// @param newIsAuthorized The new authorization status. function setAuthorization(address authorized, bool newIsAuthorized) external; /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions. /// @dev Warning: Reverts if the signature has already been submitted. /// @dev The signature is malleable, but it has no impact on the security here. /// @dev The nonce is passed as argument to be able to revert with a different error message. /// @param authorization The `Authorization` struct. /// @param signature The signature. function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external; /// @notice Accrues interest for the given market `marketParams`. function accrueInterest(MarketParams memory marketParams) external; /// @notice Returns the data stored on the different `slots`. function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory); } /// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler. /// @dev Consider using the IMorpho interface instead of this one. interface IMorphoStaticTyping is IMorphoBase { /// @notice The state of the position of `user` on the market corresponding to `id`. /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest /// accrual. function position(Id id, address user) external view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral); /// @notice The state of the market corresponding to `id`. /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest /// accrual. function market(Id id) external view returns ( uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee ); /// @notice The market params corresponding to `id`. /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`. function idToMarketParams(Id id) external view returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv); } /// @title IMorpho /// @author Morpho Labs /// @custom:contact [email protected] /// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures. interface IMorpho is IMorphoBase { /// @notice The state of the position of `user` on the market corresponding to `id`. /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest /// accrual. function position(Id id, address user) external view returns (Position memory p); /// @notice The state of the market corresponding to `id`. /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual. /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last /// interest accrual. function market(Id id) external view returns (Market memory m); /// @notice The market params corresponding to `id`. /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`. function idToMarketParams(Id id) external view returns (MarketParams memory); }
// 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; }
// 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); }
// 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)");
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @title ErrorsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library exposing error messages. library ErrorsLib { /// @notice Thrown when the caller is not the owner. string internal constant NOT_OWNER = "not owner"; /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV. string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded"; /// @notice Thrown when the fee to set exceeds the maximum fee. string internal constant MAX_FEE_EXCEEDED = "max fee exceeded"; /// @notice Thrown when the value is already set. string internal constant ALREADY_SET = "already set"; /// @notice Thrown when the IRM is not enabled at market creation. string internal constant IRM_NOT_ENABLED = "IRM not enabled"; /// @notice Thrown when the LLTV is not enabled at market creation. string internal constant LLTV_NOT_ENABLED = "LLTV not enabled"; /// @notice Thrown when the market is already created. string internal constant MARKET_ALREADY_CREATED = "market already created"; /// @notice Thrown when a token to transfer doesn't have code. string internal constant NO_CODE = "no code"; /// @notice Thrown when the market is not created. string internal constant MARKET_NOT_CREATED = "market not created"; /// @notice Thrown when not exactly one of the input amount is zero. string internal constant INCONSISTENT_INPUT = "inconsistent input"; /// @notice Thrown when zero assets is passed as input. string internal constant ZERO_ASSETS = "zero assets"; /// @notice Thrown when a zero address is passed as input. string internal constant ZERO_ADDRESS = "zero address"; /// @notice Thrown when the caller is not authorized to conduct an action. string internal constant UNAUTHORIZED = "unauthorized"; /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`. string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral"; /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`. string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity"; /// @notice Thrown when the position to liquidate is healthy. string internal constant HEALTHY_POSITION = "position is healthy"; /// @notice Thrown when the authorization signature is invalid. string internal constant INVALID_SIGNATURE = "invalid signature"; /// @notice Thrown when the authorization signature is expired. string internal constant SIGNATURE_EXPIRED = "signature expired"; /// @notice Thrown when the nonce is invalid. string internal constant INVALID_NONCE = "invalid nonce"; /// @notice Thrown when a token transfer reverted. string internal constant TRANSFER_REVERTED = "transfer reverted"; /// @notice Thrown when a token transfer returned false. string internal constant TRANSFER_RETURNED_FALSE = "transfer returned false"; /// @notice Thrown when a token transferFrom reverted. string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted"; /// @notice Thrown when a token transferFrom returned false string internal constant TRANSFER_FROM_RETURNED_FALSE = "transferFrom returned false"; /// @notice Thrown when the maximum uint128 is exceeded. string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded"; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {Id, MarketParams} from "../interfaces/IMorpho.sol"; /// @title MarketParamsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library to convert a market to its id. library MarketParamsLib { /// @notice The length of the data used to compute the id of a market. /// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each. uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32; /// @notice Returns the id of the market `marketParams`. function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) { assembly ("memory-safe") { marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH) } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; uint256 constant WAD = 1e18; /// @title MathLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library to manage fixed-point arithmetic. library MathLib { /// @dev Returns (`x` * `y`) / `WAD` rounded down. function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); } /// @dev Returns (`x` * `WAD`) / `y` rounded down. function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); } /// @dev Returns (`x` * `WAD`) / `y` rounded up. function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); } /// @dev Returns (`x` * `y`) / `d` rounded down. function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) { return (x * y) / d; } /// @dev Returns (`x` * `y`) / `d` rounded up. function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) { return (x * y + (d - 1)) / d; } /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a /// continuous compound interest rate. function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) { uint256 firstTerm = x * n; uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD); uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD); return firstTerm + secondTerm + thirdTerm; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {Id, MarketParams, Market, IMorpho} from "../../interfaces/IMorpho.sol"; import {IIrm} from "../../interfaces/IIrm.sol"; import {MathLib} from "../MathLib.sol"; import {UtilsLib} from "../UtilsLib.sol"; import {MorphoLib} from "./MorphoLib.sol"; import {SharesMathLib} from "../SharesMathLib.sol"; import {MarketParamsLib} from "../MarketParamsLib.sol"; /// @title MorphoBalancesLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Helper library exposing getters with the expected value after interest accrual. /// @dev This library is not used in Morpho itself and is intended to be used by integrators. /// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply /// to it. The value can be queried directly on Morpho using `totalBorrowShares`. library MorphoBalancesLib { using MathLib for uint256; using MathLib for uint128; using UtilsLib for uint256; using MorphoLib for IMorpho; using SharesMathLib for uint256; using MarketParamsLib for MarketParams; /// @notice Returns the expected market balances of a market after having accrued interest. /// @return The expected total supply assets. /// @return The expected total supply shares. /// @return The expected total borrow assets. /// @return The expected total borrow shares. function expectedMarketBalances(IMorpho morpho, MarketParams memory marketParams) internal view returns (uint256, uint256, uint256, uint256) { Id id = marketParams.id(); Market memory market = morpho.market(id); uint256 elapsed = block.timestamp - market.lastUpdate; // Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0). if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) { uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market); uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed)); market.totalBorrowAssets += interest.toUint128(); market.totalSupplyAssets += interest.toUint128(); if (market.fee != 0) { uint256 feeAmount = interest.wMulDown(market.fee); // The fee amount is subtracted from the total supply in this calculation to compensate for the fact // that total supply is already updated. uint256 feeShares = feeAmount.toSharesDown(market.totalSupplyAssets - feeAmount, market.totalSupplyShares); market.totalSupplyShares += feeShares.toUint128(); } } return (market.totalSupplyAssets, market.totalSupplyShares, market.totalBorrowAssets, market.totalBorrowShares); } /// @notice Returns the expected total supply assets of a market after having accrued interest. function expectedTotalSupplyAssets(IMorpho morpho, MarketParams memory marketParams) internal view returns (uint256 totalSupplyAssets) { (totalSupplyAssets,,,) = expectedMarketBalances(morpho, marketParams); } /// @notice Returns the expected total borrow assets of a market after having accrued interest. function expectedTotalBorrowAssets(IMorpho morpho, MarketParams memory marketParams) internal view returns (uint256 totalBorrowAssets) { (,, totalBorrowAssets,) = expectedMarketBalances(morpho, marketParams); } /// @notice Returns the expected total supply shares of a market after having accrued interest. function expectedTotalSupplyShares(IMorpho morpho, MarketParams memory marketParams) internal view returns (uint256 totalSupplyShares) { (, totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams); } /// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest. /// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account. /// @dev Warning: Withdrawing using the expected supply assets can lead to a revert due to conversion roundings from /// assets to shares. function expectedSupplyAssets(IMorpho morpho, MarketParams memory marketParams, address user) internal view returns (uint256) { Id id = marketParams.id(); uint256 supplyShares = morpho.supplyShares(id, user); (uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams); return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares); } /// @notice Returns the expected borrow assets balance of `user` on a market after having accrued interest. /// @dev Warning: The expected balance is rounded up, so it may be greater than the market's expected total borrow /// assets. function expectedBorrowAssets(IMorpho morpho, MarketParams memory marketParams, address user) internal view returns (uint256) { Id id = marketParams.id(); uint256 borrowShares = morpho.borrowShares(id, user); (,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = expectedMarketBalances(morpho, marketParams); return borrowShares.toAssetsUp(totalBorrowAssets, totalBorrowShares); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {IMorpho, Id} from "../../interfaces/IMorpho.sol"; import {MorphoStorageLib} from "./MorphoStorageLib.sol"; /// @title MorphoLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Helper library to access Morpho storage variables. /// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest. library MorphoLib { function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.positionSupplySharesSlot(id, user)); return uint256(morpho.extSloads(slot)[0]); } function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user)); return uint128(uint256(morpho.extSloads(slot)[0])); } function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user)); return uint256(morpho.extSloads(slot)[0] >> 128); } function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id)); return uint128(uint256(morpho.extSloads(slot)[0])); } function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id)); return uint256(morpho.extSloads(slot)[0] >> 128); } function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id)); return uint128(uint256(morpho.extSloads(slot)[0])); } function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id)); return uint256(morpho.extSloads(slot)[0] >> 128); } function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id)); return uint128(uint256(morpho.extSloads(slot)[0])); } function fee(IMorpho morpho, Id id) internal view returns (uint256) { bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id)); return uint256(morpho.extSloads(slot)[0] >> 128); } function _array(bytes32 x) private pure returns (bytes32[] memory) { bytes32[] memory res = new bytes32[](1); res[0] = x; return res; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {Id} from "../../interfaces/IMorpho.sol"; /// @title MorphoStorageLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Helper library exposing getters to access Morpho storage variables' slot. /// @dev This library is not used in Morpho itself and is intended to be used by integrators. library MorphoStorageLib { /* SLOTS */ uint256 internal constant OWNER_SLOT = 0; uint256 internal constant FEE_RECIPIENT_SLOT = 1; uint256 internal constant POSITION_SLOT = 2; uint256 internal constant MARKET_SLOT = 3; uint256 internal constant IS_IRM_ENABLED_SLOT = 4; uint256 internal constant IS_LLTV_ENABLED_SLOT = 5; uint256 internal constant IS_AUTHORIZED_SLOT = 6; uint256 internal constant NONCE_SLOT = 7; uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 8; /* SLOT OFFSETS */ uint256 internal constant LOAN_TOKEN_OFFSET = 0; uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1; uint256 internal constant ORACLE_OFFSET = 2; uint256 internal constant IRM_OFFSET = 3; uint256 internal constant LLTV_OFFSET = 4; uint256 internal constant SUPPLY_SHARES_OFFSET = 0; uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1; uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0; uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1; uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2; /* GETTERS */ function ownerSlot() internal pure returns (bytes32) { return bytes32(OWNER_SLOT); } function feeRecipientSlot() internal pure returns (bytes32) { return bytes32(FEE_RECIPIENT_SLOT); } function positionSupplySharesSlot(Id id, address user) internal pure returns (bytes32) { return bytes32( uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + SUPPLY_SHARES_OFFSET ); } function positionBorrowSharesAndCollateralSlot(Id id, address user) internal pure returns (bytes32) { return bytes32( uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + BORROW_SHARES_AND_COLLATERAL_OFFSET ); } function marketTotalSupplyAssetsAndSharesSlot(Id id) internal pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET); } function marketTotalBorrowAssetsAndSharesSlot(Id id) internal pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET); } function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + LAST_UPDATE_AND_FEE_OFFSET); } function isIrmEnabledSlot(address irm) internal pure returns (bytes32) { return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT)); } function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) { return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT)); } function isAuthorizedSlot(address authorizer, address authorizee) internal pure returns (bytes32) { return keccak256(abi.encode(authorizee, keccak256(abi.encode(authorizer, IS_AUTHORIZED_SLOT)))); } function nonceSlot(address authorizer) internal pure returns (bytes32) { return keccak256(abi.encode(authorizer, NONCE_SLOT)); } function idToLoanTokenSlot(Id id) internal pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LOAN_TOKEN_OFFSET); } function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + COLLATERAL_TOKEN_OFFSET); } function idToOracleSlot(Id id) internal pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + ORACLE_OFFSET); } function idToIrmSlot(Id id) internal pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + IRM_OFFSET); } function idToLltvSlot(Id id) internal pure returns (bytes32) { return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LLTV_OFFSET); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {MathLib} from "./MathLib.sol"; /// @title SharesMathLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Shares management library. /// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares: /// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack. library SharesMathLib { using MathLib for uint256; /// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure /// high precision computations. /// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price /// stays low enough not to inflate these assets to a significant value. /// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt. uint256 internal constant VIRTUAL_SHARES = 1e6; /// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is /// empty. uint256 internal constant VIRTUAL_ASSETS = 1; /// @dev Calculates the value of `assets` quoted in shares, rounding down. function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) { return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS); } /// @dev Calculates the value of `shares` quoted in assets, rounding down. function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) { return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES); } /// @dev Calculates the value of `assets` quoted in shares, rounding up. function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) { return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS); } /// @dev Calculates the value of `shares` quoted in assets, rounding up. function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) { return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {ErrorsLib} from "../libraries/ErrorsLib.sol"; /// @title UtilsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library exposing helpers. /// @dev Inspired by https://github.com/morpho-org/morpho-utils. library UtilsLib { /// @dev Returns true if there is exactly one zero among `x` and `y`. function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) { assembly { z := xor(iszero(x), iszero(y)) } } /// @dev Returns the min of `x` and `y`. function min(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly { z := xor(x, mul(xor(x, y), lt(y, x))) } } /// @dev Returns `x` safely cast to uint128. function toUint128(uint256 x) internal pure returns (uint128) { require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED); return uint128(x); } /// @dev Returns max(0, x - y). function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { assembly { z := mul(gt(x, y), sub(x, y)) } } }
// SPDX-License-Identifier: 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); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (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)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT 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; } } }
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); } }
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(); _; } }
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; }
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); }
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 ); }
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); }
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); }
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 ); } }
{ "optimizer": { "enabled": true, "runs": 10000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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
Loading...
Loading
Loading...
Loading
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.