Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 58 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Rebalance Down | 21556451 | 2 days ago | IN | 0 ETH | 0.00723239 | ||||
Rebalance Down | 21556153 | 2 days ago | IN | 0 ETH | 0.00761822 | ||||
Rebalance Down | 21501714 | 10 days ago | IN | 0 ETH | 0.00547999 | ||||
Rebalance Up | 21443658 | 18 days ago | IN | 0 ETH | 0.04484305 | ||||
Rebalance Up | 21443360 | 18 days ago | IN | 0 ETH | 0.02576895 | ||||
Rebalance Up | 21428895 | 20 days ago | IN | 0 ETH | 0.02167439 | ||||
Rebalance Down | 21405766 | 23 days ago | IN | 0 ETH | 0.00778808 | ||||
Rebalance Down | 21390814 | 25 days ago | IN | 0 ETH | 0.01314886 | ||||
Rebalance Down | 21388666 | 26 days ago | IN | 0 ETH | 0.02770163 | ||||
Rebalance Down | 21385919 | 26 days ago | IN | 0 ETH | 0.01416368 | ||||
Rebalance Up | 21281572 | 41 days ago | IN | 0 ETH | 0.03049064 | ||||
Rebalance Up | 21262544 | 43 days ago | IN | 0 ETH | 0.00874294 | ||||
Rebalance Up | 21262036 | 43 days ago | IN | 0 ETH | 0.01716604 | ||||
Rebalance Up | 21261731 | 43 days ago | IN | 0 ETH | 0.00945951 | ||||
Rebalance Up | 21261432 | 44 days ago | IN | 0 ETH | 0.01028237 | ||||
Rebalance Up | 21260093 | 44 days ago | IN | 0 ETH | 0.00886575 | ||||
Rebalance Up | 21259794 | 44 days ago | IN | 0 ETH | 0.01073155 | ||||
Rebalance Down | 21252627 | 45 days ago | IN | 0 ETH | 0.01439613 | ||||
Rebalance Down | 21230121 | 48 days ago | IN | 0 ETH | 0.01797162 | ||||
Rebalance Down | 21209036 | 51 days ago | IN | 0 ETH | 0.01394997 | ||||
Rebalance Down | 21208579 | 51 days ago | IN | 0 ETH | 0.01315274 | ||||
Rebalance Up | 21208539 | 51 days ago | IN | 0 ETH | 0.0118218 | ||||
Rebalance Down | 21206283 | 51 days ago | IN | 0 ETH | 0.01282162 | ||||
Rebalance Down | 21202392 | 52 days ago | IN | 0 ETH | 0.01193207 | ||||
Rebalance Down | 21202313 | 52 days ago | IN | 0 ETH | 0.01194548 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
OrigamiLovTokenMorphoManager
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 9999 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 (investments/lovToken/managers/OrigamiLovTokenMorphoManager.sol) import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { IOrigamiLovTokenMorphoManager } from "contracts/interfaces/investments/lovToken/managers/IOrigamiLovTokenMorphoManager.sol"; import { IOrigamiOracle } from "contracts/interfaces/common/oracle/IOrigamiOracle.sol"; import { IOrigamiLovTokenManager } from "contracts/interfaces/investments/lovToken/managers/IOrigamiLovTokenManager.sol"; import { IOrigamiMorphoBorrowAndLend } from "contracts/interfaces/common/borrowAndLend/IOrigamiMorphoBorrowAndLend.sol"; import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol"; import { OrigamiAbstractLovTokenManager } from "contracts/investments/lovToken/managers/OrigamiAbstractLovTokenManager.sol"; import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol"; import { Range } from "contracts/libraries/Range.sol"; import { DynamicFees } from "contracts/libraries/DynamicFees.sol"; /** * @title Origami LovToken Manager, for use with Morpho markets * @notice The `reserveToken` is deposited by users and supplied into Morpho as collateral * Upon a rebalanceDown (to decrease the A/L), the position is levered up */ contract OrigamiLovTokenMorphoManager is IOrigamiLovTokenMorphoManager, OrigamiAbstractLovTokenManager { using SafeERC20 for IERC20; using OrigamiMath for uint256; /** * @notice reserveToken that this lovToken levers up on * This is also the asset which users deposit/exit with in this lovToken manager */ IERC20 private immutable _reserveToken; /** * @notice The asset which lovToken borrows from the money market to increase the A/L ratio */ IERC20 private immutable _debtToken; /** * @notice The base asset used when retrieving the prices for dynamic fee calculations. */ address public immutable override dynamicFeeOracleBaseToken; /** * @notice The contract responsible for borrow/lend via external markets */ IOrigamiMorphoBorrowAndLend public override borrowLend; /** * @notice The oracle to convert `debtToken` <--> `reserveToken` */ IOrigamiOracle public override debtTokenToReserveTokenOracle; /** * @notice The oracle to use when observing prices which are used for the dynamic fee calculations */ IOrigamiOracle public override dynamicFeePriceOracle; /** * @dev Internal struct used to abi.encode params through a flashloan request */ enum RebalanceCallbackType { REBALANCE_DOWN, REBALANCE_UP } constructor( address _initialOwner, address _reserveToken_, address _debtToken_, address _dynamicFeeOracleBaseToken, address _lovToken, address _borrowLend ) OrigamiAbstractLovTokenManager(_initialOwner, _lovToken) { _reserveToken = IERC20(_reserveToken_); _debtToken = IERC20(_debtToken_); dynamicFeeOracleBaseToken = _dynamicFeeOracleBaseToken; borrowLend = IOrigamiMorphoBorrowAndLend(_borrowLend); // Validate the decimals of the reserve token // A borrow token of non-18dp has been tested and is ok // A reserve token of non-18dp has not been tested as yet. { uint256 _decimals = IERC20Metadata(_lovToken).decimals(); if (IERC20Metadata(_reserveToken_).decimals() != _decimals) revert CommonEventsAndErrors.InvalidToken(_reserveToken_); } } /** * @notice Set the `debtToken` <--> `reserveToken` oracle configuration */ function setOracles(address _debtTokenToReserveTokenOracle, address _dynamicFeePriceOracle) external override onlyElevatedAccess { debtTokenToReserveTokenOracle = _validatedOracle(_debtTokenToReserveTokenOracle, address(_reserveToken), address(_debtToken)); dynamicFeePriceOracle = _validatedOracle(_dynamicFeePriceOracle, dynamicFeeOracleBaseToken, address(_debtToken)); emit OraclesSet(_debtTokenToReserveTokenOracle, _dynamicFeePriceOracle); } /** * @notice Set the Origami Borrow/Lend position holder */ function setBorrowLend(address _address) external override onlyElevatedAccess { if (_address == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0)); borrowLend = IOrigamiMorphoBorrowAndLend(_address); emit BorrowLendSet(_address); } /** * @notice Increase the A/L by reducing liabilities. Flash loan and repay debt, and withdraw collateral to repay the flash loan */ function rebalanceUp(RebalanceUpParams calldata params) external override onlyElevatedAccess { _rebalanceUp(params, false); } /** * @notice Force a rebalanceUp ignoring A/L ceiling/floor * @dev Separate function to above to have stricter control on who can force */ function forceRebalanceUp(RebalanceUpParams calldata params) external override onlyElevatedAccess { _rebalanceUp(params, true); } /** * @notice Decrease the A/L by increasing liabilities. Flash loan `debtToken` swap to `reserveToken` * and add as collateral into a money market. Then borrow `debtToken` to repay the flash loan. */ function rebalanceDown(RebalanceDownParams calldata params) external override onlyElevatedAccess { _rebalanceDown(params, false); } /** * @notice Force a rebalanceDown ignoring A/L ceiling/floor * @dev Separate function to above to have stricter control on who can force */ function forceRebalanceDown(RebalanceDownParams calldata params) external override onlyElevatedAccess { _rebalanceDown(params, true); } function _rebalanceDown(RebalanceDownParams calldata params, bool force) internal { // Get the current A/L to check for oracle prices, and so we can compare that the new A/L is lower after the rebalance Cache memory cache = populateCache(IOrigamiOracle.PriceType.SPOT_PRICE); uint128 alRatioBefore = _assetToLiabilityRatio(cache); uint256 _totalCollateralSupplied = borrowLend.increaseLeverage( params.supplyAmount, params.borrowAmount, params.swapData, params.supplyCollateralSurplusThreshold ); // Validate that the new A/L is still within the `rebalanceALRange` and expected slippage range uint128 alRatioAfter = _validateAfterRebalance( cache, alRatioBefore, params.minNewAL, params.maxNewAL, AlValidationMode.LOWER_THAN_BEFORE, force ); emit Rebalance( int256(_totalCollateralSupplied), int256(params.borrowAmount), alRatioBefore, alRatioAfter ); } function _rebalanceUp(RebalanceUpParams calldata params, bool force) internal { // Get the current A/L to check for oracle prices, and so we can compare that the new A/L is lower after the rebalance Cache memory cache = populateCache(IOrigamiOracle.PriceType.SPOT_PRICE); uint128 alRatioBefore = _assetToLiabilityRatio(cache); (uint256 _debtRepaidAmount, uint256 _surplusRepaidAmount) = borrowLend.decreaseLeverage( params.repayAmount, params.withdrawCollateralAmount, params.swapData, params.repaySurplusThreshold ); // Repaying less than what was asked is only allowed in force mode. // This will only happen when there is no more debt in the money market, ie we are fully delevered if (_debtRepaidAmount != params.repayAmount) { if (!force) revert CommonEventsAndErrors.InvalidAmount(address(_debtToken), params.repayAmount); } // Validate that the new A/L is still within the `rebalanceALRange` and expected slippage range uint128 alRatioAfter = _validateAfterRebalance( cache, alRatioBefore, params.minNewAL, params.maxNewAL, AlValidationMode.HIGHER_THAN_BEFORE, force ); emit Rebalance( -int256(params.withdrawCollateralAmount), -int256(_debtRepaidAmount + _surplusRepaidAmount), alRatioBefore, alRatioAfter ); } /** * @notice Recover accidental donations. * @param token Token to recover * @param to Recipient address * @param amount Amount to recover */ function recoverToken(address token, address to, uint256 amount) external override onlyElevatedAccess { emit CommonEventsAndErrors.TokenRecovered(to, token, amount); IERC20(token).safeTransfer(to, amount); } /** * @notice The total balance of reserve tokens this lovToken holds. */ function reservesBalance() public override(OrigamiAbstractLovTokenManager,IOrigamiLovTokenManager) view returns (uint256) { return borrowLend.suppliedBalance(); } /** * @notice The underlying token this investment wraps. In this case, it's the `reserveToken` */ function baseToken() external override view returns (address) { return address(_reserveToken); } /** * @notice The set of accepted tokens which can be used to invest. * Only the `reserveToken` in this instance */ function acceptedInvestTokens() external override view returns (address[] memory tokens) { tokens = new address[](1); tokens[0] = address(_reserveToken); } /** * @notice The set of accepted tokens which can be used to exit into. * Only the `reserveToken` in this instance */ function acceptedExitTokens() external override view returns (address[] memory tokens) { tokens = new address[](1); tokens[0] = address(_reserveToken); } /** * @notice The reserveToken that the lovToken levers up on */ function reserveToken() public override(OrigamiAbstractLovTokenManager,IOrigamiLovTokenManager) view returns (address) { return address(_reserveToken); } /** * @notice The asset which lovToken borrows to increase the A/L ratio */ function debtToken() external override view returns (address) { return address(_debtToken); } /** * @notice The debt of the lovToken to the money market, converted into the `reserveToken` * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function liabilities(IOrigamiOracle.PriceType debtPriceType) public override(OrigamiAbstractLovTokenManager,IOrigamiLovTokenManager) view returns (uint256) { // In [debtToken] terms. uint256 debt = borrowLend.debtBalance(); if (debt == 0) return 0; // Convert the [debtToken] into the [reserveToken] terms return debtTokenToReserveTokenOracle.convertAmount( address(_debtToken), debt, debtPriceType, OrigamiMath.Rounding.ROUND_UP ); } /** * @notice The current deposit fee based on market conditions. * Deposit fees are applied to the portion of lovToken shares the depositor * would have received. Instead that fee portion isn't minted (benefiting remaining users) * @dev represented in basis points */ function _dynamicDepositFeeBps() internal override view returns (uint256) { return DynamicFees.dynamicFeeBps( DynamicFees.FeeType.DEPOSIT_FEE, dynamicFeePriceOracle, dynamicFeeOracleBaseToken, _minDepositFeeBps, _feeLeverageFactor ); } /** * @notice The current exit fee based on market conditions. * Exit fees are applied to the lovToken shares the user is exiting. * That portion is burned prior to being redeemed (benefiting remaining users) * @dev represented in basis points */ function _dynamicExitFeeBps() internal override view returns (uint256) { return DynamicFees.dynamicFeeBps( DynamicFees.FeeType.EXIT_FEE, dynamicFeePriceOracle, dynamicFeeOracleBaseToken, _minExitFeeBps, _feeLeverageFactor ); } /** * @notice Deposit a number of `fromToken` into the `reserveToken` * This vault only accepts where `fromToken` == `reserveToken` */ function _depositIntoReserves(address fromToken, uint256 fromTokenAmount) internal override returns (uint256 newReservesAmount) { if (fromToken == address(_reserveToken)) { newReservesAmount = fromTokenAmount; // Supply into the money market IOrigamiMorphoBorrowAndLend _borrowLend = borrowLend; _reserveToken.safeTransfer(address(_borrowLend), fromTokenAmount); _borrowLend.supply(fromTokenAmount); } else { revert CommonEventsAndErrors.InvalidToken(fromToken); } } /** * @notice Calculate the amount of `reserveToken` will be deposited given an amount of `fromToken` * This vault only accepts where `fromToken` == `reserveToken` */ function _previewDepositIntoReserves(address fromToken, uint256 fromTokenAmount) internal override view returns (uint256 newReservesAmount) { return fromToken == address(_reserveToken) ? fromTokenAmount : 0; } /** * @notice Maximum amount of `fromToken` that can be deposited into the `reserveToken` * This vault only accepts where `fromToken` == `reserveToken` */ function _maxDepositIntoReserves(address fromToken) internal override view returns (uint256 fromTokenAmount) { if (fromToken == address(_reserveToken)) { (uint256 _supplyCap, uint256 _available) = borrowLend.availableToSupply(); return _supplyCap == 0 ? MAX_TOKEN_AMOUNT : _available; } // Anything else returns 0 } /** * @notice Calculate the number of `toToken` required in order to mint a given number of `reserveToken` * This vault only accepts where `fromToken` == `reserveToken` */ function _previewMintReserves(address toToken, uint256 reservesAmount) internal override view returns (uint256 newReservesAmount) { return toToken == address(_reserveToken) ? reservesAmount : 0; } /** * @notice Redeem a number of `reserveToken` into `toToken` * This vault only accepts where `fromToken` == `reserveToken` */ function _redeemFromReserves(uint256 reservesAmount, address toToken, address recipient) internal override returns (uint256 toTokenAmount) { if (toToken == address(_reserveToken)) { toTokenAmount = reservesAmount; uint256 _amountWithdrawn = borrowLend.withdraw(reservesAmount, recipient); if (_amountWithdrawn != reservesAmount) revert CommonEventsAndErrors.InvalidAmount(toToken, reservesAmount); } else { revert CommonEventsAndErrors.InvalidToken(toToken); } } /** * @notice Calculate the number of `toToken` recevied if redeeming a number of `reserveToken` * This vault only accepts where `fromToken` == `reserveToken` */ function _previewRedeemFromReserves(uint256 reservesAmount, address toToken) internal override view returns (uint256 toTokenAmount) { return toToken == address(_reserveToken) ? reservesAmount : 0; } /** * @notice Maximum amount of `reserveToken` that can be redeemed to `toToken` * This vault only accepts where `fromToken` == `reserveToken` */ function _maxRedeemFromReserves(address toToken, Cache memory /*cache*/) internal override view returns (uint256 reservesAmount) { if (toToken == address(_reserveToken)) { // Within Morpho, we can always withdraw our supplied collateral as it is siloed. reservesAmount = borrowLend.suppliedBalance(); } // Anything else returns 0 } /** * @dev Revert if the range is invalid comparing to upstrea Aave/Spark */ function _validateAlRange(Range.Data storage range) internal override view { if (!borrowLend.isSafeAlRatio(range.floor)) revert Range.InvalidRange(range.floor, range.ceiling); } function _validatedOracle( address oracleAddress, address baseAsset, address quoteAsset ) private view returns (IOrigamiOracle oracle) { if (oracleAddress == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0)); oracle = IOrigamiOracle(oracleAddress); // Validate the assets on the oracle match what this lovToken needs if (!oracle.matchAssets(baseAsset, quoteAsset)) { revert CommonEventsAndErrors.InvalidParam(); } } }
// 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: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; // EIP-2612 is Final as of 2022-11-01. This file is deprecated. import "./IERC20Permit.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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; /// @dev propose this as the new owner before revoking, for 2 step approval address private constant PROPOSED_DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD; function _init(address initialOwner) internal { if (owner != address(0)) revert CommonEventsAndErrors.InvalidAccess(); if (initialOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0)); owner = initialOwner; } /** * @notice Revoke ownership. * @dev To enforce a two-step revoke, it must first propose to 0x000...dEaD prior to calling. * This cannot be undone. */ function revokeOwnership() external override onlyElevatedAccess { if (_proposedNewOwner != PROPOSED_DEAD_ADDRESS) revert CommonEventsAndErrors.InvalidAddress(_proposedNewOwner); emit NewOwnerAccepted(owner, address(0)); owner = address(0); } /** * @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 (common/access/Whitelisted.sol) import { IWhitelisted } from "contracts/interfaces/common/access/IWhitelisted.sol"; import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol"; import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol"; /** * @title Whitelisted abstract contract * @notice Functionality to deny non-EOA addresses unless whitelisted */ abstract contract Whitelisted is IWhitelisted, OrigamiElevatedAccess { /** * @notice Allow all (both EOAs and contracts) without whitelisting */ bool public override allowAll; /** * @notice A mapping of whitelisted accounts (not required for EOAs) */ mapping(address account => bool allowed) public override allowedAccounts; /** * @notice Allow all callers without whitelisting */ function setAllowAll(bool value) external override onlyElevatedAccess { allowAll = value; emit AllowAllSet(value); } /** * @notice Set whether a given account is allowed or not */ function setAllowAccount(address account, bool value) external override onlyElevatedAccess { if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress(account); if (account.code.length == 0) revert CommonEventsAndErrors.InvalidAddress(account); allowedAccounts[account] = value; emit AllowAccountSet(account, value); } /** * @notice Returns false for contracts unless whitelisted, or until allowAll is set to true. * @dev This cannot block contracts which deposit within their constructor, but the goal is to minimise 3rd * party integrations. This will also deny contract based wallets (eg Gnosis Safe) */ function _isAllowed(address account) internal view returns (bool) { if (allowAll) return true; // Note: If the account is a contract and access is checked within it's constructor // then this will still return true (unavoidable). This is just a deterrant for non-approved integrations, // not intended as full protection. if (account.code.length == 0) return true; // Contracts need to be explicitly allowed return allowedAccounts[account]; } }
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 view returns (address); /** * @notice Explicit approval for an address to execute a function. * allowedCaller => function selector => true/false */ function explicitFunctionAccess(address contractAddr, bytes4 functionSelector) external view returns (bool); /** * @notice Revoke ownership. Be very certain before calling this, as no * further elevated access can be called. */ function revokeOwnership() external; /** * @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/access/Whitelisted.sol) /** * @title Whitelisted abstract contract * @notice Functionality to deny non-EOA addresses unless whitelisted */ interface IWhitelisted { event AllowAllSet(bool value); event AllowAccountSet(address indexed account, bool value); /** * @notice Allow all (both EOAs and contracts) without whitelisting */ function allowAll() external view returns (bool); /** * @notice A mapping of whitelisted accounts (not required for EOAs) */ function allowedAccounts(address account) external view returns (bool allowed); /** * @notice Allow all callers without whitelisting */ function setAllowAll(bool value) external; /** * @notice Set whether a given account is allowed or not */ function setAllowAccount(address account, bool value) 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/ITokenPrices.sol) /// @title Token Prices /// @notice A utility contract to pull token prices from on-chain. /// @dev composable functions (uisng encoded function calldata) to build up price formulas interface ITokenPrices { /// @notice How many decimals places are the token prices reported in function decimals() external view returns (uint8); /// @notice Retrieve the price for a given token. /// @dev If not mapped, or an underlying error occurs, FailedPriceLookup will be thrown. /// @dev 0x000...0 is the native chain token (ETH/AVAX/etc) function tokenPrice(address token) external view returns (uint256 price); /// @notice Retrieve the price for a list of tokens. /// @dev If any aren't mapped, or an underlying error occurs, FailedPriceLookup will be thrown. /// @dev Not particularly gas efficient - wouldn't recommend to use on-chain function tokenPrices(address[] memory tokens) external view returns (uint256[] memory prices); }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (interfaces/common/oracle/IOrigamiOracle.sol) import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol"; /** * @notice An oracle which returns prices for pairs of assets, where an asset * could refer to a token (eg DAI) or a currency (eg USD) * Convention is the same as the FX market. Given the DAI/USD pair: * - DAI = Base Asset (LHS of pair) * - USD = Quote Asset (RHS of pair) * This price defines how many USD you get if selling 1 DAI * * Further, an oracle can define two PriceType's: * - SPOT_PRICE: The latest spot price, for example from a chainlink oracle * - HISTORIC_PRICE: An expected (eg 1:1 peg) or calculated historic price (eg TWAP) * * For assets which do are not tokens (eg USD), an internal address reference will be used * since this is for internal purposes only */ interface IOrigamiOracle { error InvalidPrice(address oracle, int256 price); error InvalidOracleData(address oracle); error StalePrice(address oracle, uint256 lastUpdatedAt, int256 price); error UnknownPriceType(uint8 priceType); error BelowMinValidRange(address oracle, uint256 price, uint128 floor); error AboveMaxValidRange(address oracle, uint256 price, uint128 ceiling); event ValidPriceRangeSet(uint128 validFloor, uint128 validCeiling); enum PriceType { /// @notice The current spot price of this Oracle SPOT_PRICE, /// @notice The historic price of this Oracle. /// It may be a fixed expectation (eg DAI/USD would be fixed to 1) /// or use a TWAP or some other moving average, etc. HISTORIC_PRICE } /** * @dev Wrapped in a struct to remove stack-too-deep constraints */ struct BaseOracleParams { string description; address baseAssetAddress; uint8 baseAssetDecimals; address quoteAssetAddress; uint8 quoteAssetDecimals; } /** * @notice The address used to reference the baseAsset for amount conversions */ function baseAsset() external view returns (address); /** * @notice The address used to reference the quoteAsset for amount conversions */ function quoteAsset() external view returns (address); /** * @notice The number of decimals of precision the price is returned as */ function decimals() external view returns (uint8); /** * @notice The precision that the cross rate oracle price is returned as: `10^decimals` */ function precision() external view returns (uint256); /** * @notice When converting from baseAsset<->quoteAsset, the fixed point amounts * need to be scaled by this amount. */ function assetScalingFactor() external view returns (uint256); /** * @notice A human readable description for this oracle */ function description() external view returns (string memory); /** * @notice Return the latest oracle price, to `decimals` precision * @dev This may still revert - eg if deemed stale, div by 0, negative price * @param priceType What kind of price - Spot or Historic * @param roundingMode Round the price at each intermediate step such that the final price rounds in the specified direction. */ function latestPrice( PriceType priceType, OrigamiMath.Rounding roundingMode ) external view returns (uint256 price); /** * @notice Same as `latestPrice()` but for two separate prices from this oracle */ function latestPrices( PriceType priceType1, OrigamiMath.Rounding roundingMode1, PriceType priceType2, OrigamiMath.Rounding roundingMode2 ) external view returns ( uint256 price1, uint256 price2, address oracleBaseAsset, address oracleQuoteAsset ); /** * @notice Convert either the baseAsset->quoteAsset or quoteAsset->baseAsset * @dev The `fromAssetAmount` needs to be in it's natural fixed point precision (eg USDC=6dp) * The `toAssetAmount` will also be returned in it's natural fixed point precision */ function convertAmount( address fromAsset, uint256 fromAssetAmount, PriceType priceType, OrigamiMath.Rounding roundingMode ) external view returns (uint256 toAssetAmount); /** * @notice Match whether a pair of assets match the base and quote asset on this oracle, in either order */ function matchAssets(address asset1, address asset2) external view returns (bool); }
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(); error InvalidRouter(address router); event Swap(address indexed sellToken, uint256 sellTokenAmount, address indexed buyToken, uint256 buyTokenAmount); event RouterWhitelisted(address indexed router, bool allowed); /** * @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 (interfaces/investments/IOrigamiInvestment.sol) import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol"; /** * @title Origami Investment * @notice Users invest in the underlying protocol and receive a number of this Origami investment in return. * Origami will apply the accepted investment token into the underlying protocol in the most optimal way. */ interface IOrigamiInvestment is IERC20Metadata, IERC20Permit { event TokenPricesSet(address indexed _tokenPrices); event ManagerSet(address indexed manager); event PerformanceFeeSet(uint256 fee); /** * @notice Track the depoyed version of this contract. */ function apiVersion() external pure returns (string memory); /** * @notice The underlying token this investment wraps. * @dev For informational purposes only, eg integrations/FE * If the investment wraps a protocol without an ERC20 (eg a non-liquid staked position) * then this may be 0x0 */ function baseToken() external view returns (address); /** * @notice Emitted when a user makes a new investment * @param user The user who made the investment * @param fromTokenAmount The number of `fromToken` used to invest * @param fromToken The token used to invest, one of `acceptedInvestTokens()` * @param investmentAmount The number of investment tokens received, after fees **/ event Invested(address indexed user, uint256 fromTokenAmount, address indexed fromToken, uint256 investmentAmount); /** * @notice Emitted when a user exists a position in an investment * @param user The user who exited the investment * @param investmentAmount The number of Origami investment tokens sold * @param toToken The token the user exited into * @param toTokenAmount The number of `toToken` received, after fees * @param recipient The receipient address of the `toToken`s **/ event Exited(address indexed user, uint256 investmentAmount, address indexed toToken, uint256 toTokenAmount, address indexed recipient); /// @notice Errors for unsupported functions - for example if native chain ETH/AVAX/etc isn't a vaild investment error Unsupported(); /** * @notice The set of accepted tokens which can be used to invest. * If the native chain ETH/AVAX is accepted, 0x0 will also be included in this list. */ function acceptedInvestTokens() external view returns (address[] memory); /** * @notice The set of accepted tokens which can be used to exit into. * If the native chain ETH/AVAX is accepted, 0x0 will also be included in this list. */ function acceptedExitTokens() external view returns (address[] memory); /** * @notice Whether new investments are paused. */ function areInvestmentsPaused() external view returns (bool); /** * @notice Whether exits are temporarily paused. */ function areExitsPaused() external view returns (bool); /** * @notice Quote data required when entering into this investment. */ struct InvestQuoteData { /// @notice The token used to invest, which must be one of `acceptedInvestTokens()` address fromToken; /// @notice The quantity of `fromToken` to invest with uint256 fromTokenAmount; /// @notice The maximum acceptable slippage of the `expectedInvestmentAmount` uint256 maxSlippageBps; /// @notice The maximum deadline to execute the transaction. uint256 deadline; /// @notice The expected amount of this Origami Investment token to receive in return uint256 expectedInvestmentAmount; /// @notice The minimum amount of this Origami Investment Token to receive after /// slippage has been applied. uint256 minInvestmentAmount; /// @notice Any extra quote parameters required by the underlying investment bytes underlyingInvestmentQuoteData; } /** * @notice Quote data required when exoomg this investment. */ struct ExitQuoteData { /// @notice The amount of this investment to sell uint256 investmentTokenAmount; /// @notice The token to sell into, which must be one of `acceptedExitTokens()` address toToken; /// @notice The maximum acceptable slippage of the `expectedToTokenAmount` uint256 maxSlippageBps; /// @notice The maximum deadline to execute the transaction. uint256 deadline; /// @notice The expected amount of `toToken` to receive in return /// @dev Note slippage is applied to this when calling `invest()` uint256 expectedToTokenAmount; /// @notice The minimum amount of `toToken` to receive after /// slippage has been applied. uint256 minToTokenAmount; /// @notice Any extra quote parameters required by the underlying investment bytes underlyingInvestmentQuoteData; } /** * @notice Get a quote to buy this Origami investment using one of the accepted tokens. * @dev The 0x0 address can be used for native chain ETH/AVAX * @param fromTokenAmount How much of `fromToken` to invest with * @param fromToken What ERC20 token to purchase with. This must be one of `acceptedInvestTokens` * @param maxSlippageBps The maximum acceptable slippage of the received investment amount * @param deadline The maximum deadline to execute the exit. * @return quoteData The quote data, including any params required for the underlying investment type. * @return investFeeBps Any fees expected when investing with the given token, either from Origami or from the underlying investment. */ function investQuote( uint256 fromTokenAmount, address fromToken, uint256 maxSlippageBps, uint256 deadline ) external view returns ( InvestQuoteData memory quoteData, uint256[] memory investFeeBps ); /** * @notice User buys this Origami investment with an amount of one of the approved ERC20 tokens. * @param quoteData The quote data received from investQuote() * @return investmentAmount The actual number of this Origami investment tokens received. */ function investWithToken( InvestQuoteData calldata quoteData ) external returns ( uint256 investmentAmount ); /** * @notice User buys this Origami investment with an amount of native chain token (ETH/AVAX) * @param quoteData The quote data received from investQuote() * @return investmentAmount The actual number of this Origami investment tokens received. */ function investWithNative( InvestQuoteData calldata quoteData ) external payable returns ( uint256 investmentAmount ); /** * @notice Get a quote to sell this Origami investment to receive one of the accepted tokens. * @dev The 0x0 address can be used for native chain ETH/AVAX * @param investmentAmount The number of Origami investment tokens to sell * @param toToken The token to receive when selling. This must be one of `acceptedExitTokens` * @param maxSlippageBps The maximum acceptable slippage of the received `toToken` * @param deadline The maximum deadline to execute the exit. * @return quoteData The quote data, including any params required for the underlying investment type. * @return exitFeeBps Any fees expected when exiting the investment to the nominated token, either from Origami or from the underlying investment. */ function exitQuote( uint256 investmentAmount, address toToken, uint256 maxSlippageBps, uint256 deadline ) external view returns ( ExitQuoteData memory quoteData, uint256[] memory exitFeeBps ); /** * @notice Sell this Origami investment to receive one of the accepted tokens. * @param quoteData The quote data received from exitQuote() * @param recipient The receiving address of the `toToken` * @return toTokenAmount The number of `toToken` tokens received upon selling the Origami investment tokens. */ function exitToToken( ExitQuoteData calldata quoteData, address recipient ) external returns ( uint256 toTokenAmount ); /** * @notice Sell this Origami investment to native ETH/AVAX. * @param quoteData The quote data received from exitQuote() * @param recipient The receiving address of the native chain token. * @return nativeAmount The number of native chain ETH/AVAX/etc tokens received upon selling the Origami investment tokens. */ function exitToNative( ExitQuoteData calldata quoteData, address payable recipient ) external returns ( uint256 nativeAmount ); /** * @notice The maximum amount of fromToken's that can be deposited * taking any other underlying protocol constraints into consideration */ function maxInvest(address fromToken) external view returns (uint256 amount); /** * @notice The maximum amount of tokens that can be exited into the toToken * taking any other underlying protocol constraints into consideration */ function maxExit(address toToken) external view returns (uint256 amount); }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (interfaces/investments/IOrigamiOTokenManager.sol) import { IOrigamiInvestment } from "contracts/interfaces/investments/IOrigamiInvestment.sol"; import { IOrigamiManagerPausable } from "contracts/interfaces/investments/util/IOrigamiManagerPausable.sol"; import { DynamicFees } from "contracts/libraries/DynamicFees.sol"; /** * @title Origami oToken Manager (no native ETH/AVAX/etc) * @notice The delegated logic to handle deposits/exits into an oToken, and allocating the deposit tokens * into the underlying protocol */ interface IOrigamiOTokenManager is IOrigamiManagerPausable { event InKindFees(DynamicFees.FeeType feeType, uint256 feeBps, uint256 feeAmount); /** * @notice The underlying token this investment wraps. * @dev For informational purposes only, eg integrations/FE */ function baseToken() external view returns (address); /** * @notice The set of accepted tokens which can be used to invest. */ function acceptedInvestTokens() external view returns (address[] memory); /** * @notice The set of accepted tokens which can be used to exit into. */ function acceptedExitTokens() external view returns (address[] memory); /** * @notice Whether new investments are paused. */ function areInvestmentsPaused() external view returns (bool); /** * @notice Whether exits are temporarily paused. */ function areExitsPaused() external view returns (bool); /** * @notice Get a quote to buy this oToken using one of the accepted tokens. * @param fromTokenAmount How much of `fromToken` to invest with * @param fromToken What ERC20 token to purchase with. This must be one of `acceptedInvestTokens` * @param maxSlippageBps The maximum acceptable slippage of the received investment amount * @param deadline The maximum deadline to execute the exit. * @return quoteData The quote data, including any params required for the underlying investment type. * @return investFeeBps Any fees expected when investing with the given token, either from Origami or from the underlying investment. */ function investQuote( uint256 fromTokenAmount, address fromToken, uint256 maxSlippageBps, uint256 deadline ) external view returns ( IOrigamiInvestment.InvestQuoteData memory quoteData, uint256[] memory investFeeBps ); /** * @notice User buys this Origami investment with an amount of one of the approved ERC20 tokens. * @param account The account to deposit on behalf of * @param quoteData The quote data received from investQuote() * @return investmentAmount The actual number of this Origami investment tokens received. */ function investWithToken( address account, IOrigamiInvestment.InvestQuoteData calldata quoteData ) external returns ( uint256 investmentAmount ); /** * @notice Get a quote to sell this oToken to receive one of the accepted tokens. * @param investmentAmount The number of oTokens to sell * @param toToken The token to receive when selling. This must be one of `acceptedExitTokens` * @param maxSlippageBps The maximum acceptable slippage of the received `toToken` * @param deadline The maximum deadline to execute the exit. * @return quoteData The quote data, including any params required for the underlying investment type. * @return exitFeeBps Any fees expected when exiting the investment to the nominated token, either from Origami or from the underlying protocol. */ function exitQuote( uint256 investmentAmount, address toToken, uint256 maxSlippageBps, uint256 deadline ) external view returns ( IOrigamiInvestment.ExitQuoteData memory quoteData, uint256[] memory exitFeeBps ); /** * @notice Sell this oToken to receive one of the accepted tokens. * @param account The account to exit on behalf of * @param quoteData The quote data received from exitQuote() * @param recipient The receiving address of the `toToken` * @return toTokenAmount The number of `toToken` tokens received upon selling the oToken * @return toBurnAmount The number of oToken to be burnt after exiting this position */ function exitToToken( address account, IOrigamiInvestment.ExitQuoteData calldata quoteData, address recipient ) external returns (uint256 toTokenAmount, uint256 toBurnAmount); /** * @notice The maximum amount of fromToken's that can be deposited * taking any other underlying protocol constraints into consideration */ function maxInvest(address fromToken) external view returns (uint256 amount); /** * @notice The maximum amount of tokens that can be exited into the toToken * taking any other underlying protocol constraints into consideration */ function maxExit(address toToken) external view returns (uint256 amount); }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (interfaces/investments/lovToken/IOrigamiLovToken.sol) import { IOrigamiOTokenManager } from "contracts/interfaces/investments/IOrigamiOTokenManager.sol"; import { IOrigamiInvestment } from "contracts/interfaces/investments/IOrigamiInvestment.sol"; import { ITokenPrices } from "contracts/interfaces/common/ITokenPrices.sol"; /** * @title Origami lovToken * * @notice Users deposit with an accepted token and are minted lovTokens * Origami will rebalance to lever up on the underlying reserve token, targetting a * specific A/L (assets / liabilities) range * * @dev The logic on how to handle the specific deposits/exits for each lovToken is delegated * to a manager contract */ interface IOrigamiLovToken is IOrigamiInvestment { event PerformanceFeesCollected(address indexed feeCollector, uint256 mintAmount); event FeeCollectorSet(address indexed feeCollector); event MaxTotalSupplySet(uint256 maxTotalSupply); /** * @notice The token used to track reserves for this investment */ function reserveToken() external view returns (address); /** * @notice The Origami contract managing the deposits/exits and the application of * the deposit tokens into the underlying protocol */ function manager() external view returns (IOrigamiOTokenManager); /** * @notice Set the Origami lovToken Manager. */ function setManager(address _manager) external; /** * @notice Set the vault performance fee * @dev Represented in basis points */ function setAnnualPerformanceFee(uint48 _annualPerformanceFeeBps) external; /** * @notice Set the max total supply allowed for investments into this lovToken */ function setMaxTotalSupply(uint256 _maxTotalSupply) external; /** * @notice Set the Origami performance fee collector address */ function setFeeCollector(address _feeCollector) external; /** * @notice Set the helper to calculate current off-chain/subgraph integration */ function setTokenPrices(address _tokenPrices) external; /** * @notice Collect the performance fees to the Origami Treasury */ function collectPerformanceFees() external returns (uint256 amount); /** * @notice How many reserve tokens would one get given a number of lovToken shares * @dev Implementations must use the Oracle 'SPOT_PRICE' to value any debt in terms of the reserve token */ function sharesToReserves(uint256 shares) external view returns (uint256); /** * @notice How many lovToken shares would one get given a number of reserve tokens * @dev Implementations must use the Oracle 'SPOT_PRICE' to value any debt in terms of the reserve token */ function reservesToShares(uint256 reserves) external view returns (uint256); /** * @notice How many reserve tokens would one get given a single share, as of now * @dev Implementations must use the Oracle 'HISTORIC_PRICE' to value any debt in terms of the reserve token */ function reservesPerShare() external view returns (uint256); /** * @notice The current amount of available reserves for redemptions * @dev Implementations must use the Oracle 'SPOT_PRICE' to value any debt in terms of the reserve token */ function totalReserves() external view returns (uint256); /** * @notice The maximum allowed supply of this token for user investments * @dev The actual totalSupply() may be greater than `maxTotalSupply` * in order to start organically shrinking supply or from performance fees */ function maxTotalSupply() external view returns (uint256); /** * @notice Retrieve the current assets, liabilities and calculate the ratio * @dev Implementations must use the Oracle 'SPOT_PRICE' to value any debt in terms of the reserve token */ function assetsAndLiabilities() external view returns ( uint256 assets, uint256 liabilities, uint256 ratio ); /** * @notice The current effective exposure (EE) of this lovToken * to `PRECISION` precision * @dev = reserves / (reserves - liabilities) * Implementations must use the Oracle 'SPOT_PRICE' to value any debt in terms of the reserve token */ function effectiveExposure() external view returns (uint128); /** * @notice The valid lower and upper bounds of A/L allowed when users deposit/exit into lovToken * @dev Transactions will revert if the resulting A/L is outside of this range */ function userALRange() external view returns (uint128 floor, uint128 ceiling); /** * @notice The current deposit and exit fee based on market conditions. * Fees are the equivalent of burning lovToken shares - benefit remaining vault users * @dev represented in basis points */ function getDynamicFeesBps() external view returns (uint256 depositFeeBps, uint256 exitFeeBps); /** * @notice The address used to collect the Origami performance fees. */ function feeCollector() external view returns (address); /** * @notice The annual performance fee to Origami treasury * Represented in basis points */ function annualPerformanceFeeBps() external view returns (uint48); /** * @notice The last time the performance fee was collected */ function lastPerformanceFeeTime() external view returns (uint48); /** * @notice The helper contract to retrieve Origami USD prices * @dev Required for off-chain/subgraph integration */ function tokenPrices() external view returns (ITokenPrices); /** * @notice The performance fee amount which would be collected as of now, * based on the total supply */ function accruedPerformanceFee() external view returns (uint256); }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (interfaces/investments/lovToken/managers/IOrigamiLovTokenManager.sol) import { IOrigamiOTokenManager } from "contracts/interfaces/investments/IOrigamiOTokenManager.sol"; import { IWhitelisted } from "contracts/interfaces/common/access/IWhitelisted.sol"; import { IOrigamiOracle } from "contracts/interfaces/common/oracle/IOrigamiOracle.sol"; import { IOrigamiLovToken } from "contracts/interfaces/investments/lovToken/IOrigamiLovToken.sol"; /** * @title Origami lovToken Manager * @notice The delegated logic to handle deposits/exits, and borrow/repay (rebalances) into the underlying reserve token */ interface IOrigamiLovTokenManager is IOrigamiOTokenManager, IWhitelisted { event FeeConfigSet(uint16 maxExitFeeBps, uint16 minExitFeeBps, uint24 feeLeverageFactor); event UserALRangeSet(uint128 floor, uint128 ceiling); event RebalanceALRangeSet(uint128 floor, uint128 ceiling); event Rebalance( /// @dev positive when Origami supplies the `reserveToken` as new collateral, negative when Origami withdraws collateral /// Represented in the units of the `reserveToken` of this lovToken int256 collateralChange, /// @dev positive when Origami borrows new debt, negative when Origami repays debt /// Represented in the units of the `debtToken` of this lovToken int256 debtChange, /// @dev The Assets/Liabilities ratio before the rebalance uint256 alRatioBefore, /// @dev The Assets/Liabilities ratio after the rebalance uint256 alRatioAfter ); error ALTooLow(uint128 ratioBefore, uint128 ratioAfter, uint128 minRatio); error ALTooHigh(uint128 ratioBefore, uint128 ratioAfter, uint128 maxRatio); error NoAvailableReserves(); /** * @notice Set the minimum fee (in basis points) of lovToken's for deposit and exit, * and also the nominal leverage factor applied within the fee calculations * @dev feeLeverageFactor has 4dp precision */ function setFeeConfig(uint16 _minDepositFeeBps, uint16 _minExitFeeBps, uint24 _feeLeverageFactor) external; /** * @notice Set the valid lower and upper bounds of A/L when users deposit/exit into lovToken */ function setUserALRange(uint128 floor, uint128 ceiling) external; /** * @notice Set the valid range for when a rebalance is not required. */ function setRebalanceALRange(uint128 floor, uint128 ceiling) external; /** * @notice lovToken contract - eg lovDSR */ function lovToken() external view returns (IOrigamiLovToken); /** * @notice The min deposit/exit fee and feeLeverageFactor configuration * @dev feeLeverageFactor has 4dp precision */ function getFeeConfig() external view returns (uint64 minDepositFeeBps, uint64 minExitFeeBps, uint64 feeLeverageFactor); /** * @notice The current deposit and exit fee based on market conditions. * Fees are the equivalent of burning lovToken shares - benefit remaining vault users * @dev represented in basis points */ function getDynamicFeesBps() external view returns (uint256 depositFeeBps, uint256 exitFeeBps); /** * @notice The valid lower and upper bounds of A/L allowed when users deposit/exit into lovToken * @dev Transactions will revert if the resulting A/L is outside of this range */ function userALRange() external view returns (uint128 floor, uint128 ceiling); /** * @notice The valid range for when a rebalance is not required. * When a rebalance occurs, the transaction will revert if the resulting A/L is outside of this range. */ function rebalanceALRange() external view returns (uint128 floor, uint128 ceiling); /** * @notice The common precision used */ function PRECISION() external view returns (uint256); /** * @notice The reserveToken that the lovToken levers up on */ function reserveToken() external view returns (address); /** * @notice The token which lovToken borrows to increase the A/L ratio */ function debtToken() external view returns (address); /** * @notice The total balance of reserve tokens this lovToken holds, and also if deployed as collateral * in other platforms */ function reservesBalance() external view returns (uint256); /** * @notice The debt of the lovToken from the borrower, converted into the reserveToken * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function liabilities(IOrigamiOracle.PriceType debtPriceType) external view returns (uint256); /** * @notice The current asset/liability (A/L) of this lovToken * to `PRECISION` precision * @dev = reserves / liabilities */ function assetToLiabilityRatio() external view returns (uint128); /** * @notice Retrieve the current assets, liabilities and calculate the ratio * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function assetsAndLiabilities(IOrigamiOracle.PriceType debtPriceType) external view returns ( uint256 assets, uint256 liabilities, uint256 ratio ); /** * @notice The current effective exposure (EE) of this lovToken * to `PRECISION` precision * @dev = reserves / (reserves - liabilities) * Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function effectiveExposure(IOrigamiOracle.PriceType debtPriceType) external view returns (uint128); /** * @notice The amount of reserves that users may redeem their lovTokens as of this block * @dev = reserves - liabilities * Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function userRedeemableReserves(IOrigamiOracle.PriceType debtPriceType) external view returns (uint256); /** * @notice How many reserve tokens would one get given a number of lovToken shares * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function sharesToReserves(uint256 shares, IOrigamiOracle.PriceType debtPriceType) external view returns (uint256); /** * @notice How many lovToken shares would one get given a number of reserve tokens * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function reservesToShares(uint256 reserves, IOrigamiOracle.PriceType debtPriceType) external view returns (uint256); }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (interfaces/investments/lovToken/managers/IOrigamiLovTokenMorphoManager.sol) import { IOrigamiOracle } from "contracts/interfaces/common/oracle/IOrigamiOracle.sol"; import { IOrigamiLovTokenManager } from "contracts/interfaces/investments/lovToken/managers/IOrigamiLovTokenManager.sol"; import { IOrigamiMorphoBorrowAndLend } from "contracts/interfaces/common/borrowAndLend/IOrigamiMorphoBorrowAndLend.sol"; /** * @title Origami LovToken Manager, for use with Morpho markets * @notice The `reserveToken` is deposited by users and supplied into Morpho as collateral * Upon a rebalanceDown (to decrease the A/L), the position is levered up */ interface IOrigamiLovTokenMorphoManager is IOrigamiLovTokenManager { event OraclesSet(address indexed debtTokenToReserveTokenOracle, address indexed dynamicFeePriceOracle); event BorrowLendSet(address indexed addr); /** * @notice Set the `reserveToken` <--> `debtToken` oracle configuration */ function setOracles(address _debtTokenToReserveTokenOracle, address _dynamicFeePriceOracle) external; /** * @notice Set the Origami Borrow/Lend position holder */ function setBorrowLend(address _address) external; struct RebalanceUpParams { // The amount of `debtToken` to repay uint256 repayAmount; // The amount of `reserveToken` collateral to withdraw uint256 withdrawCollateralAmount; // The swap quote data to swap from `reserveToken` -> `debtToken` bytes swapData; // The min balance threshold for when surplus balance of `debtToken` is // repaid to the Morpho position uint256 repaySurplusThreshold; // The minimum acceptable A/L, will revert if below this uint128 minNewAL; // The maximum acceptable A/L, will revert if above this uint128 maxNewAL; } /** * @notice Increase the A/L by reducing liabilities. * Uses Morpho's callback mechanism to efficiently lever up */ function rebalanceUp(RebalanceUpParams calldata params) external; /** * @notice Force a rebalanceUp ignoring A/L ceiling/floor * @dev Separate function to above to have stricter control on who can force */ function forceRebalanceUp(RebalanceUpParams calldata params) external; struct RebalanceDownParams { // The amount of `reserveToken` collateral to supply uint256 supplyAmount; // The amount of `debtToken` to borrow uint256 borrowAmount; // The swap quote data to swap from `debtToken` -> `reserveToken` bytes swapData; // The min balance threshold for when surplus balance of `reserveToken` is added as // collateral to the Morpho position uint256 supplyCollateralSurplusThreshold; // The minimum acceptable A/L, will revert if below this uint128 minNewAL; // The maximum acceptable A/L, will revert if above this uint128 maxNewAL; } /** * @notice Decrease the A/L by increasing liabilities. * Uses Morpho's callback mechanism to efficiently lever up */ function rebalanceDown(RebalanceDownParams calldata params) external; /** * @notice Force a rebalanceDown ignoring A/L ceiling/floor * @dev Separate function to above to have stricter control on who can force */ function forceRebalanceDown(RebalanceDownParams calldata params) external; /** * @notice The contract responsible for borrow/lend via external markets */ function borrowLend() external view returns (IOrigamiMorphoBorrowAndLend); /** * @notice The oracle to convert `debtToken` <--> `reserveToken` */ function debtTokenToReserveTokenOracle() external view returns (IOrigamiOracle); /** * @notice The base asset used when retrieving the prices for dynamic fee calculations. */ function dynamicFeeOracleBaseToken() external view returns (address); /** * @notice The oracle to use when observing prices which are used for the dynamic fee calculations */ function dynamicFeePriceOracle() external view returns (IOrigamiOracle); }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (interfaces/investments/util/IOrigamiManagerPausable.sol) /** * @title A mixin to add pause/unpause for Origami manager contracts */ interface IOrigamiManagerPausable { struct Paused { bool investmentsPaused; bool exitsPaused; } event PauserSet(address indexed account, bool canPause); event PausedSet(Paused paused); /// @notice A set of accounts which are allowed to pause deposits/withdrawals immediately /// under emergency function pausers(address) external view returns (bool); /// @notice Pause/unpause deposits or withdrawals /// @dev Can only be called by allowed pausers or governance. function setPaused(Paused memory updatedPaused) external; /// @notice Allow/Deny an account to pause/unpause deposits or withdrawals function setPauser(address account, bool canPause) external; /// @notice Check if given account can pause investments/exits function isPauser(address account) external view returns (bool canPause); }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (investments/lovToken/managers/OrigamiAbstractLovTokenManager.sol) import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { IOrigamiInvestment } from "contracts/interfaces/investments/IOrigamiInvestment.sol"; import { IOrigamiLovTokenManager } from "contracts/interfaces/investments/lovToken/managers/IOrigamiLovTokenManager.sol"; import { IOrigamiOracle } from "contracts/interfaces/common/oracle/IOrigamiOracle.sol"; import { IOrigamiLovToken } from "contracts/interfaces/investments/lovToken/IOrigamiLovToken.sol"; import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol"; import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol"; import { OrigamiManagerPausable } from "contracts/investments/util/OrigamiManagerPausable.sol"; import { Range } from "contracts/libraries/Range.sol"; import { Whitelisted } from "contracts/common/access/Whitelisted.sol"; import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol"; import { DynamicFees } from "contracts/libraries/DynamicFees.sol"; /** * @title Abstract Origami lovToken Manager * @notice The delegated logic to handle deposits/exits, and borrow/repay (rebalances) into the underlying reserve token * @dev The `reserveToken` must have <= 18 decimal places. */ abstract contract OrigamiAbstractLovTokenManager is IOrigamiLovTokenManager, OrigamiElevatedAccess, OrigamiManagerPausable, Whitelisted { using Range for Range.Data; using OrigamiMath for uint256; /** * @notice lovToken contract - eg lovDSR */ IOrigamiLovToken public immutable override lovToken; /** * @notice The minimum fee (in basis points) when users deposit into from the lovToken. * The fee is applied on the lovToken shares -- which are not minted, benefiting remaining holders. */ uint64 internal _minDepositFeeBps; /** * @notice The minimum fee (in basis points) when users exit out from the lovToken. * The fee is applied on the lovToken shares which are being exited * These lovToken shares are burned, benefiting remaining holders. */ uint64 internal _minExitFeeBps; /** * @notice The nominal leverage factor applied to the difference between the * oracle SPOT_PRICE vs the HISTORIC_PRICE. Used within the fee calculation. * eg: depositFee = 15 * (HISTORIC_PRICE - SPOT_PRICE) [when spot < historic] * @dev feeLeverageFactor has 4dp precision */ uint64 internal _feeLeverageFactor; /** * @notice The valid lower and upper bounds of A/L allowed when users deposit/exit into lovToken * @dev Transactions will revert if the resulting A/L is outside of this range */ Range.Data public override userALRange; /** * @notice The valid range for when a rebalance is not required. * When a rebalance occurs, the transaction will revert if the resulting A/L is outside of this range. */ Range.Data public override rebalanceALRange; /** * @notice The common precision used */ uint256 public constant override PRECISION = 1e18; /** * @notice The maximum A/L ratio possible (eg if debt=0) */ uint128 internal constant MAX_AL_RATIO = type(uint128).max; /** * @notice The maxmimum EE ratio possible (eg if liabilities >= reserves) */ uint128 internal constant MAX_EFECTIVE_EXPOSURE = type(uint128).max; /** * @dev Max ERC20 token amount for supply/allowances/etc */ uint256 internal constant MAX_TOKEN_AMOUNT = type(uint256).max; enum AlValidationMode { LOWER_THAN_BEFORE, HIGHER_THAN_BEFORE } constructor( address _initialOwner, address _lovToken ) OrigamiElevatedAccess(_initialOwner) { lovToken = IOrigamiLovToken(_lovToken); } /** * @notice Set the minimum fee (in basis points) of lovToken's for deposit and exit, * and also the nominal leverage factor applied within the fee calculations * @dev feeLeverageFactor has 4dp precision */ function setFeeConfig( uint16 minDepositFeeBps, uint16 minExitFeeBps, uint24 feeLeverageFactor ) external override onlyElevatedAccess { if (minDepositFeeBps > OrigamiMath.BASIS_POINTS_DIVISOR) revert CommonEventsAndErrors.InvalidParam(); if (minExitFeeBps > OrigamiMath.BASIS_POINTS_DIVISOR) revert CommonEventsAndErrors.InvalidParam(); emit FeeConfigSet(minDepositFeeBps, minExitFeeBps, feeLeverageFactor); _minDepositFeeBps = minDepositFeeBps; _minExitFeeBps = minExitFeeBps; _feeLeverageFactor = feeLeverageFactor; } /** * @notice The min deposit/exit fee and feeLeverageFactor configuration * @dev feeLeverageFactor has 4dp precision */ function getFeeConfig() external override view returns (uint64, uint64, uint64) { return (_minDepositFeeBps, _minExitFeeBps, _feeLeverageFactor); } /** * @notice Set the valid lower and upper bounds of A/L when users deposit/exit into lovToken */ function setUserALRange(uint128 floor, uint128 ceiling) external override onlyElevatedAccess { if (floor <= PRECISION) revert Range.InvalidRange(floor, ceiling); emit UserALRangeSet(floor, ceiling); userALRange.set(floor, ceiling); // Any extra validation on AL depending on the strategy _validateAlRange(userALRange); } /** * @notice Set the valid range for when a rebalance is not required. */ function setRebalanceALRange(uint128 floor, uint128 ceiling) external override onlyElevatedAccess { if (floor <= PRECISION) revert Range.InvalidRange(floor, ceiling); emit RebalanceALRangeSet(floor, ceiling); rebalanceALRange.set(floor, ceiling); // Any extra validation on AL depending on the strategy _validateAlRange(rebalanceALRange); } /** * @notice Recover any token - should not be able to recover tokens which are normally * held in this contract * @param token Token to recover * @param to Recipient address * @param amount Amount to recover */ function recoverToken(address token, address to, uint256 amount) external virtual; /** * @notice Deposit into the reserve token on behalf of a user * @param account The user account which is investing. * @param quoteData The quote data to deposit into the reserve token * @return investmentAmount The actual number of receipt tokens received, inclusive of any fees. */ function investWithToken( address account, IOrigamiInvestment.InvestQuoteData calldata quoteData ) external virtual override onlyLovToken returns ( uint256 investmentAmount ) { if (_paused.investmentsPaused) revert CommonEventsAndErrors.IsPaused(); if (!_isAllowed(account)) revert CommonEventsAndErrors.InvalidAccess(); Cache memory cache = populateCache(IOrigamiOracle.PriceType.SPOT_PRICE); // Note this also checks that the debtToken/reserveToken oracle prices are valid. uint128 oldAL = _assetToLiabilityRatio(cache); uint256 newReservesAmount = _depositIntoReserves(quoteData.fromToken, quoteData.fromTokenAmount); // The number of shares is calculated based off this `newReservesAmount` // However not all of these shares are minted and given to the user -- the deposit fee is removed investmentAmount = _reservesToShares(cache, newReservesAmount); uint256 feeAmount; uint256 feeBps = _dynamicDepositFeeBps(); (investmentAmount, feeAmount) = investmentAmount.splitSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_DOWN); emit InKindFees(DynamicFees.FeeType.DEPOSIT_FEE, feeBps, feeAmount); // Verify the amount if (investmentAmount == 0) revert CommonEventsAndErrors.ExpectedNonZero(); if (investmentAmount < quoteData.minInvestmentAmount) { revert CommonEventsAndErrors.Slippage(quoteData.minInvestmentAmount, investmentAmount); } // A user deposit will raise the A/L (more reserves, but the same debt) // This needs to be validated so it doesn't go above the ceiling // Not required if there are not yet any liabilities (where A/L would be uint128.max) if (cache.liabilities != 0) { uint128 newAL = refreshCacheAL(cache, IOrigamiOracle.PriceType.SPOT_PRICE); _validateALRatio(userALRange, oldAL, newAL, AlValidationMode.HIGHER_THAN_BEFORE, cache); } } /** * @notice Exit from the reserve token on behalf of a user. * param account The account to exit on behalf of * @param quoteData The quote data received from exitQuote() * @param recipient The receiving address of the exit token * @return toTokenAmount The number of tokens received upon selling the lovToken * @return toBurnAmount The number of lovTokens to be burnt after exiting this position */ function exitToToken( address /*account*/, IOrigamiInvestment.ExitQuoteData calldata quoteData, address recipient ) external virtual override onlyLovToken returns ( uint256 toTokenAmount, uint256 toBurnAmount ) { if (_paused.exitsPaused) revert CommonEventsAndErrors.IsPaused(); Cache memory cache = populateCache(IOrigamiOracle.PriceType.SPOT_PRICE); // Note this also checks that the debtToken/reserveToken oracle prices are valid. uint128 oldAL = _assetToLiabilityRatio(cache); // The entire amount of lovTokens will be burned // But only the non-fee portion is redeemed to reserves and sent to the user toBurnAmount = quoteData.investmentTokenAmount; uint256 feeBps = _dynamicExitFeeBps(); (uint256 reservesAmount, uint256 feeAmount) = toBurnAmount.splitSubtractBps(feeBps, OrigamiMath.Rounding.ROUND_DOWN); emit InKindFees(DynamicFees.FeeType.EXIT_FEE, feeBps, feeAmount); // Given the number of redeemable lovToken's calculate how many reserves this equates to // at the current share price and the reserve supply prior to exiting reservesAmount = _sharesToReserves(cache, reservesAmount); // Now exit from the reserves and check slippage toTokenAmount = _redeemFromReserves(reservesAmount, quoteData.toToken, recipient); if (toTokenAmount < quoteData.minToTokenAmount) { revert CommonEventsAndErrors.Slippage(quoteData.minToTokenAmount, toTokenAmount); } // A user exit will lower the A/L (less reserves, but the same debt) // This needs to be validated so it doesn't go below the floor // Not required if there are not yet any liabilities (where A/L would be uint128.max) if (cache.liabilities != 0) { uint128 newAL = refreshCacheAL(cache, IOrigamiOracle.PriceType.SPOT_PRICE); _validateALRatio(userALRange, oldAL, newAL, AlValidationMode.LOWER_THAN_BEFORE, cache); } } /** * @notice Get a quote to buy this Origami investment using one of the accepted tokens. * @param fromTokenAmount How much of `fromToken` to invest with * @param fromToken What ERC20 token to purchase with. This must be one of `acceptedInvestTokens` * @param maxSlippageBps The maximum acceptable slippage of the received investment amount * @param deadline The maximum deadline to execute the exit. * @return quoteData The quote data, including any params required for the underlying investment type. * @return investFeeBps Any fees expected when investing with the given token, either from Origami or from the underlying investment. */ function investQuote( uint256 fromTokenAmount, address fromToken, uint256 maxSlippageBps, uint256 deadline ) external virtual override view returns ( IOrigamiInvestment.InvestQuoteData memory quoteData, uint256[] memory investFeeBps ) { if (fromTokenAmount == 0) revert CommonEventsAndErrors.ExpectedNonZero(); Cache memory cache = populateCache(IOrigamiOracle.PriceType.SPOT_PRICE); uint256 _newReservesAmount = _previewDepositIntoReserves(fromToken, fromTokenAmount); // The number of shares is calculated based off this `_newReservesAmount` // However not all of these shares are minted and given to the user -- the deposit fee is removed uint256 _investmentAmount = _reservesToShares(cache, _newReservesAmount); uint256 _depositFeeRate = _dynamicDepositFeeBps(); _investmentAmount = _investmentAmount.subtractBps(_depositFeeRate, OrigamiMath.Rounding.ROUND_DOWN); quoteData.fromToken = fromToken; quoteData.fromTokenAmount = fromTokenAmount; quoteData.maxSlippageBps = maxSlippageBps; quoteData.deadline = deadline; quoteData.expectedInvestmentAmount = _investmentAmount; quoteData.minInvestmentAmount = _investmentAmount.subtractBps(maxSlippageBps, OrigamiMath.Rounding.ROUND_UP); // quoteData.underlyingInvestmentQuoteData remains as bytes(0) investFeeBps = new uint256[](1); investFeeBps[0] = _depositFeeRate; } /** * @notice The maximum amount of fromToken's that can be deposited into the lovToken * taking into consideration: * 1/ The max reserves in possible until the A/L ceiling would be hit * 2/ Any other constraints of the underlying implementation */ function maxInvest(address fromToken) external override view returns (uint256 fromTokenAmount) { Cache memory cache = populateCache(IOrigamiOracle.PriceType.SPOT_PRICE); // First get the underlying implementation's max allowed fromTokenAmount = _maxDepositIntoReserves(fromToken); // Use the minimum number of reserves from both the lovToken.maxTotalSupply and userAL.ceiling restrictions uint256 _minRemainingCapacity = _reservesCapacityFromTotalSupply(cache); uint256 _remainingCapacityForAlCeiling = _reservesCapacityFromAlCeiling(cache); if (_remainingCapacityForAlCeiling < _minRemainingCapacity) { _minRemainingCapacity = _remainingCapacityForAlCeiling; } // Convert to the fromToken. Use previewMint as this amount of fromToken's // should return the exact shares when invested if (_minRemainingCapacity < type(uint256).max) { _minRemainingCapacity = _previewMintReserves(fromToken, _minRemainingCapacity); } // Finally, use this remaining capcity if it's less than the underlying implementation's max allowed of fromToken if (_minRemainingCapacity < fromTokenAmount) { fromTokenAmount = _minRemainingCapacity; } } /** * @notice Get a quote to sell this Origami investment to receive one of the accepted tokens. * @param investmentAmount The number of Origami investment tokens to sell * @param toToken The token to receive when selling. This must be one of `acceptedExitTokens` * @param maxSlippageBps The maximum acceptable slippage of the received `toToken` * @param deadline The maximum deadline to execute the exit. * @return quoteData The quote data, including any params required for the underlying investment type. * @return exitFeeBps Any fees expected when exiting the investment to the nominated token, either from Origami or from the underlying investment. */ function exitQuote( uint256 investmentAmount, address toToken, uint256 maxSlippageBps, uint256 deadline ) external virtual override view returns ( IOrigamiInvestment.ExitQuoteData memory quoteData, uint256[] memory exitFeeBps ) { if (investmentAmount == 0) revert CommonEventsAndErrors.ExpectedNonZero(); // Exit fees are taken from the lovToken amount, so get the non-fee amount to actually exit uint256 _exitFeeRate = _dynamicExitFeeBps(); uint256 toExitAmount = investmentAmount.subtractBps(_exitFeeRate, OrigamiMath.Rounding.ROUND_DOWN); Cache memory cache = populateCache(IOrigamiOracle.PriceType.SPOT_PRICE); // Convert to the underlying toToken toExitAmount = _previewRedeemFromReserves( // Convert the non-fee lovToken amount to ERC-4626 reserves _sharesToReserves(cache, toExitAmount), toToken ); quoteData.investmentTokenAmount = investmentAmount; quoteData.toToken = toToken; quoteData.maxSlippageBps = maxSlippageBps; quoteData.deadline = deadline; quoteData.expectedToTokenAmount = toExitAmount; quoteData.minToTokenAmount = toExitAmount.subtractBps(maxSlippageBps, OrigamiMath.Rounding.ROUND_UP); // quoteData.underlyingInvestmentQuoteData remains as bytes(0) exitFeeBps = new uint256[](1); exitFeeBps[0] = _exitFeeRate; } /** * @notice The maximum amount of lovToken shares that can be exited into the `toToken` * taking into consideration: * 1/ The max reserves out possible until the A/L floor would be hit * 2/ Any other constraints from the underyling implementation */ function maxExit(address toToken) external override view returns (uint256 sharesAmount) { // Calculate the max reserves which can be removed before the A/L floor is hit // Round up for the minimum reserves Cache memory cache = populateCache(IOrigamiOracle.PriceType.SPOT_PRICE); uint256 _minReserves = cache.liabilities.mulDiv( convertedAL(userALRange.floor, cache), PRECISION, OrigamiMath.Rounding.ROUND_UP ); // Only check the underlying implementation if there's capacity to remove reserves if (cache.assets > _minReserves) { // Calculate the max number of lovToken shares which can be exited given the A/L // floor on reserves uint256 _amountFromAvailableCapacity; unchecked { _amountFromAvailableCapacity = cache.assets - _minReserves; } // Check the underlying implementation's max reserves that can be redeemed uint256 _underlyingAmount = _maxRedeemFromReserves(toToken, cache); // Use the minimum of both the underlying implementation max and // the capacity based on the A/L floor if (_underlyingAmount < _amountFromAvailableCapacity) { _amountFromAvailableCapacity = _underlyingAmount; } // Convert reserves to lovToken shares sharesAmount = _reservesToShares(cache, _amountFromAvailableCapacity); // Since exit fees are taken when exiting (so these reserves aren't actually redeemed), // reverse out the fees // Round down to be the inverse of when they're applied (and rounded up) when exiting sharesAmount = sharesAmount.inverseSubtractBps(_dynamicExitFeeBps(), OrigamiMath.Rounding.ROUND_DOWN); // Finally use the min of the derived amount and the lovToken total supply if (sharesAmount > cache.totalSupply) { sharesAmount = cache.totalSupply; } } } /** * @notice The current deposit and exit fee based on market conditions. * Fees are the equivalent of burning lovToken shares - benefit remaining vault users * @dev represented in basis points */ function getDynamicFeesBps() external view returns (uint256 depositFeeBps, uint256 exitFeeBps) { depositFeeBps = _dynamicDepositFeeBps(); exitFeeBps = _dynamicExitFeeBps(); } /** * @notice Whether new investments are paused. */ function areInvestmentsPaused() external override view returns (bool) { return _paused.investmentsPaused; } /** * @notice Whether exits are temporarily paused. */ function areExitsPaused() external override view returns (bool) { return _paused.exitsPaused; } /** * @notice The reserveToken that the lovToken levers up on */ function reserveToken() public virtual override view returns (address); /** * @notice The total balance of reserve tokens this lovToken holds, and also if deployed as collateral * in other platforms * @dev Explicitly tracked rather than via reserveToken.balanceOf() to avoid donation/inflation vectors. */ function reservesBalance() public virtual override view returns (uint256); /** * @notice The debt of the lovToken from the borrower, converted into the reserveToken * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function liabilities(IOrigamiOracle.PriceType debtPriceType) public virtual override view returns (uint256); /** * @notice The current asset/liability (A/L) of this lovToken * to `PRECISION` precision * @dev = reserves / liabilities */ function assetToLiabilityRatio() external override view returns (uint128) { return _assetToLiabilityRatio(populateCache(IOrigamiOracle.PriceType.SPOT_PRICE)); } /** * @notice Retrieve the current assets, liabilities and calculate the ratio * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function assetsAndLiabilities(IOrigamiOracle.PriceType debtPriceType) external override view returns ( uint256 /*assets*/, uint256 /*liabilities*/, uint256 /*ratio*/ ) { Cache memory cache = populateCache(debtPriceType); return ( cache.assets, cache.liabilities, _assetToLiabilityRatio(cache) ); } /** * @notice The current effective exposure (EE) of this lovToken * to `PRECISION` precision * @dev = reserves / (reserves - liabilities) * Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function effectiveExposure(IOrigamiOracle.PriceType debtPriceType) external override view returns (uint128) { Cache memory cache = populateCache(debtPriceType); if (cache.assets > cache.liabilities) { uint256 redeemableReserves; unchecked { redeemableReserves = cache.assets - cache.liabilities; } // Round up for EE calc uint256 ee = cache.assets.mulDiv(PRECISION, redeemableReserves, OrigamiMath.Rounding.ROUND_UP); if (ee < MAX_EFECTIVE_EXPOSURE) { return uint128(ee); } } return MAX_EFECTIVE_EXPOSURE; } /** * @notice The amount of reserves that users may redeem their lovTokens as of this block * @dev = reserves - liabilities * Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function userRedeemableReserves(IOrigamiOracle.PriceType debtPriceType) external override view returns (uint256) { return _userRedeemableReserves(populateCache(debtPriceType)); } /** * @notice How many reserve tokens would one get given a number of lovToken shares * and the current lovToken totalSupply * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function sharesToReserves(uint256 shares, IOrigamiOracle.PriceType debtPriceType) external override view returns (uint256) { return _sharesToReserves(populateCache(debtPriceType), shares); } /** * @notice How many lovToken shares would one get given a number of reserve tokens * and the current lovToken totalSupply * @dev Use the Oracle `debtPriceType` to value any debt in terms of the reserve token */ function reservesToShares(uint256 reserves, IOrigamiOracle.PriceType debtPriceType) external override view returns (uint256) { return _reservesToShares(populateCache(debtPriceType), reserves); } // An internal cache to save having to recalculate struct Cache { uint256 assets; uint256 liabilities; uint256 totalSupply; // This slot can be used by an underlying implementation if required. uint256 implData; } function populateCache(IOrigamiOracle.PriceType debtPriceType) internal view returns (Cache memory cache) { cache.assets = reservesBalance(); cache.liabilities = liabilities(debtPriceType); cache.totalSupply = lovToken.totalSupply(); } function refreshCacheAL(Cache memory cache, IOrigamiOracle.PriceType debtPriceType) internal view returns (uint128) { cache.assets = reservesBalance(); cache.liabilities = liabilities(debtPriceType); return _assetToLiabilityRatio(cache); } /** * @dev If necessary, an implementation may convert the A/L. * For example if the money market liquidation LTV is defined in one way and needs converting to a 'market priced' LTV */ function convertedAL(uint128 al, Cache memory /*cache*/) internal virtual view returns (uint128) { return al; } /** * @notice The current deposit fee based on market conditions. * Deposit fees are applied to the portion of lovToken shares the depositor * would have received. Instead that fee portion isn't minted (benefiting remaining users) * @dev represented in basis points */ function _dynamicDepositFeeBps() internal virtual view returns (uint256); /** * @notice The current exit fee based on market conditions. * Exit fees are applied to the lovToken shares the user is exiting. * That portion is burned prior to being redeemed (benefiting remaining users) * @dev represented in basis points */ function _dynamicExitFeeBps() internal virtual view returns (uint256); /** * @dev Perform any extra validation on the A/L range * By default, nothing extra validation is required, however a manager implementation * may decide to perform extra. For example if borrowing from Aave/Spark, * this can check that the A/L floor is within a tolerable range which won't get liquidated * Since those parameters could be updated at a later date by Aave/Spark */ function _validateAlRange(Range.Data storage range) internal virtual view {} function _userRedeemableReserves(Cache memory cache) internal pure returns (uint256) { unchecked { return cache.assets > cache.liabilities ? cache.assets - cache.liabilities : 0; } } function _assetToLiabilityRatio(Cache memory cache) internal pure returns (uint128) { if (cache.liabilities != 0) { // Round down for A/L calc uint256 alr = cache.assets.mulDiv(PRECISION, cache.liabilities, OrigamiMath.Rounding.ROUND_DOWN); if (alr < MAX_AL_RATIO) { return uint128(alr); } } return MAX_AL_RATIO; } function _sharesToReserves(Cache memory cache, uint256 shares) internal view returns (uint256) { // If totalSupply is zero, then just return shares 1:1 scaled down to the reserveToken decimals // If > 0 then the decimal conversion is handled already (numerator cancels out denominator) // Round down for calculating reserves from shares return cache.totalSupply == 0 ? shares.scaleDown(_reservesToSharesScalar(), OrigamiMath.Rounding.ROUND_DOWN) : shares.mulDiv(_userRedeemableReserves(cache), cache.totalSupply, OrigamiMath.Rounding.ROUND_DOWN); } function _reservesToShares(Cache memory cache, uint256 reserves) private view returns (uint256) { // If totalSupply is zero, then just return reserves 1:1 scaled up to the shares decimals // If > 0 then the decimal conversion is handled already (numerator cancels out denominator) if (cache.totalSupply == 0) { return reserves.scaleUp(_reservesToSharesScalar()); } // In the unlikely case that no available reserves for user withdrawals (100% of reserves are held back to repay debt), // then revert uint256 _redeemableReserves = _userRedeemableReserves(cache); if (_redeemableReserves == 0) { revert NoAvailableReserves(); } // Round down for calculating shares from reserves return reserves.mulDiv(cache.totalSupply, _redeemableReserves, OrigamiMath.Rounding.ROUND_DOWN); } /** * @dev Calculate the asset scalar to convert from reserveToken --> 18 decimal places (`PRECISION`) * The reserveToken cannot have more than the lovToken decimals (18dp) */ function _reservesToSharesScalar() internal view returns (uint256) { uint8 _reservesDecimals = IERC20Metadata(reserveToken()).decimals(); uint8 _sharesDecimals = IERC20Metadata(address(lovToken)).decimals(); if (_reservesDecimals > _sharesDecimals) revert CommonEventsAndErrors.InvalidToken(reserveToken()); return 10 ** (_sharesDecimals - _reservesDecimals); } /** * @notice Deposit a number of `fromToken` into the `reserveToken` */ function _depositIntoReserves(address fromToken, uint256 fromTokenAmount) internal virtual returns (uint256 newReservesAmount); /** * @notice Calculate the amount of `reserveToken` will be deposited given an amount of `fromToken` */ function _previewDepositIntoReserves(address fromToken, uint256 fromTokenAmount) internal virtual view returns (uint256 newReservesAmount); /** * @notice Maximum amount of `fromToken` that can be deposited into the `reserveToken` */ function _maxDepositIntoReserves(address fromToken) internal virtual view returns (uint256 fromTokenAmount); /** * @notice Calculate the number of `toToken` required in order to mint a given number of `reserveTokens` */ function _previewMintReserves(address toToken, uint256 reservesAmount) internal virtual view returns (uint256 toTokenAmount); /** * @notice Redeem a number of `reserveToken` into `toToken` */ function _redeemFromReserves(uint256 reservesAmount, address toToken, address recipient) internal virtual returns (uint256 toTokenAmount); /** * @notice Calculate the number of `toToken` recevied if redeeming a number of `reserveToken` */ function _previewRedeemFromReserves(uint256 reservesAmount, address toToken) internal virtual view returns (uint256 toTokenAmount); /** * @notice Maximum amount of `reserveToken` that can be redeemed to `toToken` */ function _maxRedeemFromReserves(address toToken, Cache memory cache) internal virtual view returns (uint256 reservesAmount); /** * @notice Validate that the A/L ratio hasn't moved beyond the given A/L range. */ function _validateALRatio(Range.Data storage validRange, uint128 ratioBefore, uint128 ratioAfter, AlValidationMode alMode, Cache memory cache) internal virtual { if (alMode == AlValidationMode.LOWER_THAN_BEFORE) { // A/L needs to be decreasing (may be equal if a very small amount is deposited/exited) if (ratioAfter > ratioBefore) revert ALTooHigh(ratioBefore, ratioAfter, ratioBefore); // Check that the new A/L is not below the floor // In this mode, the A/L may be above the ceiling still, but should be decreasing // Note: The A/L may not be strictly decreasing in this mode since the liabilities (in reserve terms) is also // fluctuating uint128 convertedAlFloor = convertedAL(validRange.floor, cache); if (ratioAfter < convertedAlFloor) revert ALTooLow(ratioBefore, ratioAfter, convertedAlFloor); } else { // A/L needs to be increasing (may be equal if a very small amount is deposited/exited) if (ratioAfter < ratioBefore) revert ALTooLow(ratioBefore, ratioAfter, ratioBefore); // Check that the new A/L is not above the ceiling // In this mode, the A/L may be below the floor still, but should be increasing // Note: The A/L may not be strictly increasing in this mode since the liabilities (in reserve terms) is also // fluctuating uint128 convertedAlCeiling = convertedAL(validRange.ceiling, cache); if (ratioAfter > convertedAlCeiling) revert ALTooHigh(ratioBefore, ratioAfter, convertedAlCeiling); } } /** * @dev Recalculate the A/L and validate that it is still within the `rebalanceALRange` */ function _validateAfterRebalance( Cache memory cache, uint128 alRatioBefore, uint128 minNewAL, uint128 maxNewAL, AlValidationMode alValidationMode, bool force ) internal returns (uint128 alRatioAfter) { // Need to recalculate both the assets and liabilities in the cache alRatioAfter = refreshCacheAL(cache, IOrigamiOracle.PriceType.SPOT_PRICE); // Ensure the A/L is within the expected slippage range { // The `minNewAL` and `maxNewAL` are specified in the borrow lend terms // Convert them to 'market' so it's in the same terms as the `alRatioAfter` uint128 _convertedAL = convertedAL(minNewAL, cache); if (alRatioAfter < _convertedAL) revert ALTooLow(alRatioBefore, alRatioAfter, _convertedAL); _convertedAL = convertedAL(maxNewAL, cache); if (alRatioAfter > _convertedAL) revert ALTooHigh(alRatioBefore, alRatioAfter, _convertedAL); } if (!force) _validateALRatio(rebalanceALRange, alRatioBefore, alRatioAfter, alValidationMode, cache); } /** * @dev Calculate the free capacity for new reserves, given the lovToken maxTotalSupply restriction */ function _reservesCapacityFromTotalSupply(Cache memory cache) internal view returns (uint256) { uint256 _maxTotalSupply = lovToken.maxTotalSupply(); if (_maxTotalSupply == type(uint256).max) { return type(uint256).max; } // Number of lovToken shares available uint256 _availableShares; unchecked { _availableShares = _maxTotalSupply > cache.totalSupply ? _maxTotalSupply - cache.totalSupply : 0; } // Take deposit fees into account // Round down to be the inverse of when they're applied when depositing _availableShares = _availableShares.inverseSubtractBps(_dynamicDepositFeeBps(), OrigamiMath.Rounding.ROUND_DOWN); // Convert to reserve tokens return _sharesToReserves(cache, _availableShares); } /** * @dev Calculate the free capacity for new reserves, given the A/L ceiling restriction */ function _reservesCapacityFromAlCeiling(Cache memory cache) internal view returns (uint256) { if (cache.liabilities == 0) { return type(uint256).max; } // This is ever so slightly conservative, as it calculates maxReserves which would result in // an A/L strictly less than (<) the `userALRange.ceiling`, rather than exacly less-than-or-equal (<=) // This is intentional to provide a slightly more conservative max amount which can be deposited. // To get it exact, the userALRange.ceiling would need to be incremented by 1 (if not already type(uint128).max) uint256 _maxReservesForAlCeiling = cache.liabilities.mulDiv( convertedAL(userALRange.ceiling, cache), PRECISION, OrigamiMath.Rounding.ROUND_DOWN ); if (_maxReservesForAlCeiling > cache.assets) { unchecked { return _maxReservesForAlCeiling - cache.assets; } } return 0; } modifier onlyLovToken() { if (msg.sender != address(lovToken)) revert CommonEventsAndErrors.InvalidAccess(); _; } }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (investments/util/OrigamiManagerPausable.sol) import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol"; import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol"; import { IOrigamiManagerPausable } from "contracts/interfaces/investments/util/IOrigamiManagerPausable.sol"; /** * @title A mixin to add pause/unpause for Origami manager contracts */ abstract contract OrigamiManagerPausable is IOrigamiManagerPausable, OrigamiElevatedAccess { /** * @notice A set of accounts which are allowed to pause deposits/withdrawals immediately * under emergency */ mapping(address account => bool canPause) public pausers; /** * @notice The current paused/unpaused state of deposits/exits. */ Paused internal _paused; /** * @notice Pause/unpause deposits or exits * @dev Can only be called by allowed pausers. */ function setPaused(Paused calldata updatedPaused) external { if (!pausers[msg.sender]) revert CommonEventsAndErrors.InvalidAccess(); emit PausedSet(updatedPaused); _paused = updatedPaused; } /** * @notice Allow/Deny an account to pause/unpause deposits or exits */ function setPauser(address account, bool canPause) external onlyElevatedAccess { pausers[account] = canPause; emit PauserSet(account, canPause); } /** * @notice Check if given account can pause deposits/exits */ function isPauser(address account) external view override returns (bool canPause) { canPause = pausers[account]; } }
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/DynamicFees.sol) import { IOrigamiOracle } from "contracts/interfaces/common/oracle/IOrigamiOracle.sol"; import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol"; import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol"; /** * @notice A helper to calculate dynamic entry and exit fees based off the difference * between an oracle historic vs spot price */ library DynamicFees { using OrigamiMath for uint256; enum FeeType { DEPOSIT_FEE, EXIT_FEE } /** * @notice The current deposit or exit fee based on market conditions. * Fees are applied to the portion of lovToken shares the depositor * would have received. Instead that fee portion isn't minted (benefiting remaining users) * Ignoring the minFeeBps, deposit vs exit fees are symmetric: * - A 0.004 cent increase in price (away from expected historic) should result a deposit fee of X bps * - A 0.004 cent decrease in price (away from expected historic) should result an exit fee, also of X bps * ie X is the same in both cases. * @dev feeLeverageFactor has 4dp precision */ function dynamicFeeBps( FeeType feeType, IOrigamiOracle oracle, address expectedBaseAsset, uint64 minFeeBps, uint256 feeLeverageFactor ) internal view returns (uint256) { // Pull the spot and expected historic price from the oracle. // Round up for both to be consistent no matter if the oracle is in expected quoted order or not. (uint256 _spotPrice, uint256 _histPrice, address _baseAsset, address _quoteAsset) = oracle.latestPrices( IOrigamiOracle.PriceType.SPOT_PRICE, OrigamiMath.Rounding.ROUND_UP, IOrigamiOracle.PriceType.HISTORIC_PRICE, OrigamiMath.Rounding.ROUND_UP ); // Whether the expected 'base' asset of the oracle is indeed the base asset. // If not, then the delta and denominator is switched bool _inQuotedOrder; if (_baseAsset == expectedBaseAsset) { _inQuotedOrder = true; } else if (_quoteAsset != expectedBaseAsset) { revert CommonEventsAndErrors.InvalidToken(expectedBaseAsset); } uint256 _delta; uint256 _denominator; if (feeType == FeeType.DEPOSIT_FEE) { // If spot price is > than the expected historic, then they are exiting // at a price better than expected. The exit fee is based off the relative // difference of the expected spotPrice - historicPrice. // Or opposite if the oracle order is inverted unchecked { if (_inQuotedOrder) { if (_spotPrice < _histPrice) { (_delta, _denominator) = (_histPrice - _spotPrice, _histPrice); } } else { if (_spotPrice > _histPrice) { (_delta, _denominator) = (_spotPrice - _histPrice, _spotPrice); } } } } else { // If spot price is > than the expected historic, then they are exiting // at a price better than expected. The exit fee is based off the relative // difference of the expected spotPrice - historicPrice. // Or opposite if the oracle order is inverted unchecked { if (_inQuotedOrder) { if (_spotPrice > _histPrice) { (_delta, _denominator) = (_spotPrice - _histPrice, _histPrice); } } else { if (_spotPrice < _histPrice) { (_delta, _denominator) = (_histPrice - _spotPrice, _spotPrice); } } } } // If no delta, just return the min fee if (_delta == 0) { return minFeeBps; } // Relative diff multiply by a leverage factor to match the worst case lovToken // effective exposure // Result is in basis points, since `feeLeverageFactor` has 4dp precision uint256 _fee = _delta.mulDiv( feeLeverageFactor, _denominator, OrigamiMath.Rounding.ROUND_UP ); // Use the maximum of the calculated fee and a pre-set minimum. return minFeeBps > _fee ? minFeeBps : _fee; } }
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 ); } }
pragma solidity 0.8.19; // SPDX-License-Identifier: AGPL-3.0-or-later // Origami (libraries/Range.sol) /** * @notice A helper library to track a valid range from floor <= x <= ceiling */ library Range { error InvalidRange(uint128 floor, uint128 ceiling); struct Data { uint128 floor; uint128 ceiling; } function set(Data storage range, uint128 floor, uint128 ceiling) internal { if (floor > ceiling) { revert InvalidRange(floor, ceiling); } range.floor = floor; range.ceiling = ceiling; } }
{ "optimizer": { "enabled": true, "runs": 9999 }, "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":"_reserveToken_","type":"address"},{"internalType":"address","name":"_debtToken_","type":"address"},{"internalType":"address","name":"_dynamicFeeOracleBaseToken","type":"address"},{"internalType":"address","name":"_lovToken","type":"address"},{"internalType":"address","name":"_borrowLend","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint128","name":"ratioBefore","type":"uint128"},{"internalType":"uint128","name":"ratioAfter","type":"uint128"},{"internalType":"uint128","name":"maxRatio","type":"uint128"}],"name":"ALTooHigh","type":"error"},{"inputs":[{"internalType":"uint128","name":"ratioBefore","type":"uint128"},{"internalType":"uint128","name":"ratioAfter","type":"uint128"},{"internalType":"uint128","name":"minRatio","type":"uint128"}],"name":"ALTooLow","type":"error"},{"inputs":[],"name":"ExpectedNonZero","type":"error"},{"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":"uint128","name":"floor","type":"uint128"},{"internalType":"uint128","name":"ceiling","type":"uint128"}],"name":"InvalidRange","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"IsPaused","type":"error"},{"inputs":[],"name":"NoAvailableReserves","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":false,"internalType":"bool","name":"value","type":"bool"}],"name":"AllowAccountSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"value","type":"bool"}],"name":"AllowAllSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"addr","type":"address"}],"name":"BorrowLendSet","type":"event"},{"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":"uint16","name":"maxExitFeeBps","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"minExitFeeBps","type":"uint16"},{"indexed":false,"internalType":"uint24","name":"feeLeverageFactor","type":"uint24"}],"name":"FeeConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum DynamicFees.FeeType","name":"feeType","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"feeBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"}],"name":"InKindFees","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":"debtTokenToReserveTokenOracle","type":"address"},{"indexed":true,"internalType":"address","name":"dynamicFeePriceOracle","type":"address"}],"name":"OraclesSet","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bool","name":"investmentsPaused","type":"bool"},{"internalType":"bool","name":"exitsPaused","type":"bool"}],"indexed":false,"internalType":"struct IOrigamiManagerPausable.Paused","name":"paused","type":"tuple"}],"name":"PausedSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"bool","name":"canPause","type":"bool"}],"name":"PauserSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"int256","name":"collateralChange","type":"int256"},{"indexed":false,"internalType":"int256","name":"debtChange","type":"int256"},{"indexed":false,"internalType":"uint256","name":"alRatioBefore","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"alRatioAfter","type":"uint256"}],"name":"Rebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"floor","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"ceiling","type":"uint128"}],"name":"RebalanceALRangeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"floor","type":"uint128"},{"indexed":false,"internalType":"uint128","name":"ceiling","type":"uint128"}],"name":"UserALRangeSet","type":"event"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"acceptedExitTokens","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptedInvestTokens","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"allowedAccounts","outputs":[{"internalType":"bool","name":"allowed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"areExitsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"areInvestmentsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetToLiabilityRatio","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IOrigamiOracle.PriceType","name":"debtPriceType","type":"uint8"}],"name":"assetsAndLiabilities","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowLend","outputs":[{"internalType":"contract IOrigamiMorphoBorrowAndLend","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtTokenToReserveTokenOracle","outputs":[{"internalType":"contract IOrigamiOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dynamicFeeOracleBaseToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dynamicFeePriceOracle","outputs":[{"internalType":"contract IOrigamiOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IOrigamiOracle.PriceType","name":"debtPriceType","type":"uint8"}],"name":"effectiveExposure","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"investmentAmount","type":"uint256"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"maxSlippageBps","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"exitQuote","outputs":[{"components":[{"internalType":"uint256","name":"investmentTokenAmount","type":"uint256"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"maxSlippageBps","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"expectedToTokenAmount","type":"uint256"},{"internalType":"uint256","name":"minToTokenAmount","type":"uint256"},{"internalType":"bytes","name":"underlyingInvestmentQuoteData","type":"bytes"}],"internalType":"struct IOrigamiInvestment.ExitQuoteData","name":"quoteData","type":"tuple"},{"internalType":"uint256[]","name":"exitFeeBps","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"components":[{"internalType":"uint256","name":"investmentTokenAmount","type":"uint256"},{"internalType":"address","name":"toToken","type":"address"},{"internalType":"uint256","name":"maxSlippageBps","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"expectedToTokenAmount","type":"uint256"},{"internalType":"uint256","name":"minToTokenAmount","type":"uint256"},{"internalType":"bytes","name":"underlyingInvestmentQuoteData","type":"bytes"}],"internalType":"struct IOrigamiInvestment.ExitQuoteData","name":"quoteData","type":"tuple"},{"internalType":"address","name":"recipient","type":"address"}],"name":"exitToToken","outputs":[{"internalType":"uint256","name":"toTokenAmount","type":"uint256"},{"internalType":"uint256","name":"toBurnAmount","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":[{"components":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"uint256","name":"supplyCollateralSurplusThreshold","type":"uint256"},{"internalType":"uint128","name":"minNewAL","type":"uint128"},{"internalType":"uint128","name":"maxNewAL","type":"uint128"}],"internalType":"struct IOrigamiLovTokenMorphoManager.RebalanceDownParams","name":"params","type":"tuple"}],"name":"forceRebalanceDown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawCollateralAmount","type":"uint256"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"uint256","name":"repaySurplusThreshold","type":"uint256"},{"internalType":"uint128","name":"minNewAL","type":"uint128"},{"internalType":"uint128","name":"maxNewAL","type":"uint128"}],"internalType":"struct IOrigamiLovTokenMorphoManager.RebalanceUpParams","name":"params","type":"tuple"}],"name":"forceRebalanceUp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getDynamicFeesBps","outputs":[{"internalType":"uint256","name":"depositFeeBps","type":"uint256"},{"internalType":"uint256","name":"exitFeeBps","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFeeConfig","outputs":[{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"uint64","name":"","type":"uint64"},{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"fromTokenAmount","type":"uint256"},{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"uint256","name":"maxSlippageBps","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"investQuote","outputs":[{"components":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"uint256","name":"fromTokenAmount","type":"uint256"},{"internalType":"uint256","name":"maxSlippageBps","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"expectedInvestmentAmount","type":"uint256"},{"internalType":"uint256","name":"minInvestmentAmount","type":"uint256"},{"internalType":"bytes","name":"underlyingInvestmentQuoteData","type":"bytes"}],"internalType":"struct IOrigamiInvestment.InvestQuoteData","name":"quoteData","type":"tuple"},{"internalType":"uint256[]","name":"investFeeBps","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"components":[{"internalType":"address","name":"fromToken","type":"address"},{"internalType":"uint256","name":"fromTokenAmount","type":"uint256"},{"internalType":"uint256","name":"maxSlippageBps","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"expectedInvestmentAmount","type":"uint256"},{"internalType":"uint256","name":"minInvestmentAmount","type":"uint256"},{"internalType":"bytes","name":"underlyingInvestmentQuoteData","type":"bytes"}],"internalType":"struct IOrigamiInvestment.InvestQuoteData","name":"quoteData","type":"tuple"}],"name":"investWithToken","outputs":[{"internalType":"uint256","name":"investmentAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isPauser","outputs":[{"internalType":"bool","name":"canPause","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IOrigamiOracle.PriceType","name":"debtPriceType","type":"uint8"}],"name":"liabilities","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lovToken","outputs":[{"internalType":"contract IOrigamiLovToken","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"toToken","type":"address"}],"name":"maxExit","outputs":[{"internalType":"uint256","name":"sharesAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromToken","type":"address"}],"name":"maxInvest","outputs":[{"internalType":"uint256","name":"fromTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"pausers","outputs":[{"internalType":"bool","name":"canPause","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"proposeNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rebalanceALRange","outputs":[{"internalType":"uint128","name":"floor","type":"uint128"},{"internalType":"uint128","name":"ceiling","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"uint256","name":"supplyCollateralSurplusThreshold","type":"uint256"},{"internalType":"uint128","name":"minNewAL","type":"uint128"},{"internalType":"uint128","name":"maxNewAL","type":"uint128"}],"internalType":"struct IOrigamiLovTokenMorphoManager.RebalanceDownParams","name":"params","type":"tuple"}],"name":"rebalanceDown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawCollateralAmount","type":"uint256"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"uint256","name":"repaySurplusThreshold","type":"uint256"},{"internalType":"uint128","name":"minNewAL","type":"uint128"},{"internalType":"uint128","name":"maxNewAL","type":"uint128"}],"internalType":"struct IOrigamiLovTokenMorphoManager.RebalanceUpParams","name":"params","type":"tuple"}],"name":"rebalanceUp","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":[],"name":"reserveToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reservesBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserves","type":"uint256"},{"internalType":"enum IOrigamiOracle.PriceType","name":"debtPriceType","type":"uint8"}],"name":"reservesToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"revokeOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"value","type":"bool"}],"name":"setAllowAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value","type":"bool"}],"name":"setAllowAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"setBorrowLend","outputs":[],"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":"uint16","name":"minDepositFeeBps","type":"uint16"},{"internalType":"uint16","name":"minExitFeeBps","type":"uint16"},{"internalType":"uint24","name":"feeLeverageFactor","type":"uint24"}],"name":"setFeeConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_debtTokenToReserveTokenOracle","type":"address"},{"internalType":"address","name":"_dynamicFeePriceOracle","type":"address"}],"name":"setOracles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"investmentsPaused","type":"bool"},{"internalType":"bool","name":"exitsPaused","type":"bool"}],"internalType":"struct IOrigamiManagerPausable.Paused","name":"updatedPaused","type":"tuple"}],"name":"setPaused","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"canPause","type":"bool"}],"name":"setPauser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"floor","type":"uint128"},{"internalType":"uint128","name":"ceiling","type":"uint128"}],"name":"setRebalanceALRange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"floor","type":"uint128"},{"internalType":"uint128","name":"ceiling","type":"uint128"}],"name":"setUserALRange","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"enum IOrigamiOracle.PriceType","name":"debtPriceType","type":"uint8"}],"name":"sharesToReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"userALRange","outputs":[{"internalType":"uint128","name":"floor","type":"uint128"},{"internalType":"uint128","name":"ceiling","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum IOrigamiOracle.PriceType","name":"debtPriceType","type":"uint8"}],"name":"userRedeemableReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101006040523480156200001257600080fd5b5060405162004f2338038062004f23833981016040819052620000359162000222565b85828162000043816200018c565b506001600160a01b0390811660805286811660a05285811660c05284811660e052600a80546001600160a01b0319168483161790556040805163313ce56760e01b81529051600093509185169163313ce567916004808201926020929091908290030181865afa158015620000bc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e29190620002a3565b60ff16905080866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000127573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200014d9190620002a3565b60ff16146200017f5760405163961c9a4f60e01b81526001600160a01b03871660048201526024015b60405180910390fd5b50505050505050620002cf565b6000546001600160a01b031615620001b757604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116620001e357604051634726455360e11b81526000600482015260240162000176565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b80516001600160a01b03811681146200021d57600080fd5b919050565b60008060008060008060c087890312156200023c57600080fd5b620002478762000205565b9550620002576020880162000205565b9450620002676040880162000205565b9350620002776060880162000205565b9250620002876080880162000205565b91506200029760a0880162000205565b90509295509295509295565b600060208284031215620002b657600080fd5b815160ff81168114620002c857600080fd5b9392505050565b60805160a05160c05160e051614b79620003aa600039600081816107a201528181611155015281816128d10152612f1a0152600081816108e3015281816110f301528181611176015281816120b9015261262b0152600081816107ec015281816110d20152818161143b015281816127e9015281816129490152818161303f0152818161308e0152818161346b01528181613580015281816136a10152818161381101526139570152600081816104010152818161185d01528181611e480152818161275b01528181612a2e01526138970152614b796000f3fe608060405234801561001057600080fd5b506004361061036d5760003560e01c806380f51c12116101d3578063baaaa7b111610104578063e04610ed116100a2578063eea2f45c1161007c578063eea2f45c146108bb578063f4325d67146107ea578063f69959a3146108ce578063f8d89898146108e157600080fd5b8063e04610ed1461086f578063e664b02014610892578063ebbc4965146108b357600080fd5b8063c677e275116100de578063c677e27514610810578063d4da79b314610823578063da5e0f0e1461082e578063daeccc791461084157600080fd5b8063baaaa7b1146107c4578063bfccf0ec146107d7578063c55dae63146107ea57600080fd5b8063a7229fd911610171578063b07c63c71161014b578063b07c63c714610764578063b1f8100d14610777578063b5a2d9a91461078a578063ba2c46491461079d57600080fd5b8063a7229fd914610714578063a8e93cdb14610727578063aaf5eb681461075557600080fd5b806395c5b3b6116101ad57806395c5b3b6146106c157806395ea64a5146106d45780639a6b27cf146106e7578063a515b8ec146106f757600080fd5b806380f51c121461068b5780638a83c9cd146106505780638da5cb5b146106ae57600080fd5b80634d7d9c01116102ad5780635fbbc0d21161024b57806365f2ba2f1161022557806365f2ba2f1461063d5780636a1eb7b8146106505780637180c8ca1461066557806372e0c0f41461067857600080fd5b80635fbbc0d2146105cb5780636026220d14610617578063643b1e501461062a57600080fd5b806352e648f01161028757806352e648f01461057f57806353740e0414610592578063572a9302146105a55780635aa89173146105b857600080fd5b80634d7d9c01146105575780634edd74e81461056a5780634ee643a51461057257600080fd5b806329aa41361161031a578063431072f7116102f4578063431072f7146104ab57806346fbf68e146104be5780634c9da063146104fa5780634cdf587a1461052e57600080fd5b806329aa41361461047d5780632b96895814610490578063415a12711461049857600080fd5b806319000c421161034b57806319000c42146103fc5780631a0377d11461043b57806324b821ab1461045c57600080fd5b806302cb4ed1146103725780630bd7260d1461038757806313da2d4a1461039a575b600080fd5b610385610380366004613f4e565b610907565b005b610385610395366004613f4e565b610960565b6008546103ce906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b604080516fffffffffffffffffffffffffffffffff9384168152929091166020830152015b60405180910390f35b6104237f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103f3565b61044e610449366004613f98565b6109b6565b6040516103f392919061407e565b61046f61046a366004614104565b610b05565b6040519081526020016103f3565b61038561048b36600461413e565b610b22565b610385610c6c565b61046f6104a6366004614177565b610d6e565b6103856104b93660046141b4565b610dd9565b6104ea6104cc366004614177565b6001600160a01b031660009081526003602052604090205460ff1690565b60405190151581526020016103f3565b6009546103ce906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b610536610ef9565b6040516fffffffffffffffffffffffffffffffff90911681526020016103f3565b6103856105653660046141de565b610f12565b61046f610fa4565b6005546104ea9060ff1681565b600c54610423906001600160a01b031681565b6103856105a0366004613f4e565b61102b565b6103856105b33660046141fb565b611081565b6103856105c636600461423b565b611202565b6007546040805167ffffffffffffffff808416825268010000000000000000840481166020830152700100000000000000000000000000000000909304909216908201526060016103f3565b61046f61062536600461428a565b61139a565b61046f610638366004614104565b6113ad565b61038561064b366004613f4e565b6113c1565b610658611417565b6040516103f391906142a5565b61038561067336600461413e565b611490565b600b54610423906001600160a01b031681565b6104ea610699366004614177565b60036020526000908152604090205460ff1681565b600054610423906001600160a01b031681565b6103856106cf3660046141b4565b611533565b6103856106e2366004614177565b61164f565b600454610100900460ff166104ea565b6106ff61173f565b604080519283526020830191909152016103f3565b6103856107223660046142f2565b61175a565b61073a61073536600461428a565b61180b565b604080519384526020840192909252908201526060016103f3565b61046f670de0b6b3a764000081565b61046f610772366004614345565b611850565b610385610785366004614177565b611a58565b61046f610798366004614177565b611b67565b6104237f000000000000000000000000000000000000000000000000000000000000000081565b6103856107d2366004614395565b611c15565b6103856107e53660046143a7565b611c89565b7f0000000000000000000000000000000000000000000000000000000000000000610423565b6106ff61081e36600461442f565b611e3a565b60045460ff166104ea565b61046f61083c36600461428a565b611feb565b6104ea61084f3660046144b8565b600160209081526000928352604080842090915290825290205460ff1681565b6104ea61087d366004614177565b60066020526000908152604090205460ff1681565b6108a56108a0366004613f98565b612130565b6040516103f39291906144e4565b610385612277565b6105366108c936600461428a565b612312565b600a54610423906001600160a01b031681565b7f0000000000000000000000000000000000000000000000000000000000000000610423565b610935336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61095257604051633006171960e21b815260040160405180910390fd5b61095d8160006123f7565b50565b61098e336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b6109ab57604051633006171960e21b815260040160405180910390fd5b61095d81600061253c565b6109ff6040518060e0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001606081525090565b606085600003610a3b576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610a476000612712565b90506000610a5587896127e5565b90506000610a63838361282d565b90506000610a6f6128b6565b9050610a7d82826000612918565b6001600160a01b038a168752602087018b90526040870189905260608701889052608087018190529150610ab382896001612918565b60a087015260408051600180825281830190925290602080830190803683370190505094508085600081518110610aec57610aec614547565b6020026020010181815250505050505094509492505050565b6000610b19610b1383612712565b8461282d565b90505b92915050565b610b50336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b610b6d57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038216610bbd576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024015b60405180910390fd5b816001600160a01b03163b600003610c0c576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610bb4565b6001600160a01b038216600081815260066020908152604091829020805460ff191685151590811790915591519182527fee397872136f42e5319f2ebe127140e4741bee3ff02b86b7410a0b02778216de91015b60405180910390a25050565b610c9a336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b610cb757604051633006171960e21b815260040160405180910390fd5b6002546001600160a01b031661dead14610d0c576002546040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610bb4565b600080546040516001600160a01b03909116907f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d554908390a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600080610d7b6000612712565b9050610d8683612945565b91506000610d9382612a29565b90506000610da083612b01565b905081811015610dae578091505b600019821015610dc557610dc285836127e5565b91505b83821015610dd1578193505b505050919050565b610e07336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b610e2457604051633006171960e21b815260040160405180910390fd5b670de0b6b3a7640000826fffffffffffffffffffffffffffffffff1611610e93576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610bb4565b604080516fffffffffffffffffffffffffffffffff8085168252831660208201527f792e77bcc33e678fd2a12f7a1ae644dcef1311589d15e9e8abd47adf5aca5e18910160405180910390a1610eeb60088383612b7c565b610ef56008612c24565b5050565b6000610f0d610f086000612712565b612d28565b905090565b610f40336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b610f5d57604051633006171960e21b815260040160405180910390fd5b6005805460ff19168215159081179091556040519081527faf941e5e6c2b1ae06b5434c080d9f9ba2b0c2ac8e125a6010a22b57201f26a349060200160405180910390a150565b600a54604080517fe4e8895400000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163e4e889549160048083019260209291908290030181865afa158015611007573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0d9190614576565b611059336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61107657604051633006171960e21b815260040160405180910390fd5b61095d8160016123f7565b6110af336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b6110cc57604051633006171960e21b815260040160405180910390fd5b611117827f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612d8e565b600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039290921691909117905561119a817f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612d8e565b600c80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03928316179055604051828216918416907f4073b32a832187a54e475fc8fff20266eedffb63596adf4c0b8a2966e2b0025d90600090a35050565b611230336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61124d57604051633006171960e21b815260040160405180910390fd5b6127108361ffff16111561128d576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6127108261ffff1611156112cd576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161ffff85811682528416602082015262ffffff83168183015290517f91829fbe10211c7b1218c9e5b102bdabb13e9526b494b9741133a39e973f9f549181900360600190a16007805461ffff9485167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617929093166801000000000000000002919091177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1662ffffff9190911670010000000000000000000000000000000002179055565b6000610b1c6113a883612712565b612e98565b6000610b196113bb83612712565b84612ebc565b6113ef336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61140c57604051633006171960e21b815260040160405180910390fd5b61095d81600161253c565b604080516001808252818301909252606091602080830190803683370190505090507f00000000000000000000000000000000000000000000000000000000000000008160008151811061146d5761146d614547565b60200260200101906001600160a01b031690816001600160a01b03168152505090565b6114be336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b6114db57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038216600081815260036020908152604091829020805460ff191685151590811790915591519182527fa11b5803b8a35081b8f993e0dee5bc30301a3d83f644e5ab2ff39f972f0a807f9101610c60565b611561336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61157e57604051633006171960e21b815260040160405180910390fd5b670de0b6b3a7640000826fffffffffffffffffffffffffffffffff16116115ed576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610bb4565b604080516fffffffffffffffffffffffffffffffff8085168252831660208201527f23b4d5cfa83d92da7d76ae644647a3e53678133ad0fa7d2b526ad571af12d1ec910160405180910390a161164560098383612b7c565b610ef56009612c24565b61167d336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61169a57604051633006171960e21b815260040160405180910390fd5b6001600160a01b0381166116dd576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610bb4565b600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517ffd6cdd9cefbd87614b7f366cb2bd5f7ba60b9a4b9febfbaf6344ecd1e03d018390600090a250565b60008061174a6128b6565b9150611754612efe565b90509091565b611788336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b6117a557604051633006171960e21b815260040160405180910390fd5b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f9096836040516117ea91815260200190565b60405180910390a36118066001600160a01b0384168383612f6c565b505050565b60008060008061181a85612712565b90508060000151816020015161182f83612d28565b91955093506fffffffffffffffffffffffffffffffff169150509193909250565b6000336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461189b57604051633006171960e21b815260040160405180910390fd5b60045460ff16156118d8576040517f1309a56300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118e183612fec565b6118fe57604051633006171960e21b815260040160405180910390fd5b600061190a6000612712565b9050600061191782612d28565b9050600061193561192b6020870187614177565b866020013561303b565b9050611941838261282d565b935060008061194e6128b6565b905061195c8682600061316b565b60405191975092507f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb78091404279061199690600090849086906145f5565b60405180910390a1856000036119d8576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8660a00135861015611a23576040517f2746152a00000000000000000000000000000000000000000000000000000000815260a0880135600482015260248101879052604401610bb4565b602085015115611a4d576000611a3a866000613186565b9050611a4b6008868360018a6131a9565b505b505050505092915050565b611a86336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b611aa357604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611aee576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610bb4565b600254600080546040516001600160a01b03808616948116939216917f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d891a4600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b600080611b746000612712565b6008546020820151919250600091611ba8916fffffffffffffffffffffffffffffffff16670de0b6b3a764000060016133d4565b90508082600001511115611c0e5781518190036000611bc78685613467565b905081811015611bd5578091505b611bdf848361282d565b9450611bf5611bec612efe565b86906000613519565b94508360400151851115611c0b57836040015194505b50505b5050919050565b3360009081526003602052604090205460ff16611c4557604051633006171960e21b815260040160405180910390fd5b7f803ee193075547dae36361498f3de5e399cdb29b7e0c7b680533f3da8b733a1781604051611c749190614616565b60405180910390a18060046118068282614646565b611cb7336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b611cd457604051633006171960e21b815260040160405180910390fd5b6001600160a01b038316611d1f576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610bb4565b604080518082019091526000808252602082015260005b82811015611e3357838382818110611d5057611d50614547565b905060400201803603810190611d6691906146a9565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b038716600090815260018352604080822086517fffffffff0000000000000000000000000000000000000000000000000000000016835290935291909120805460ff1916911515919091179055611e2c8161475c565b9050611d36565b5050505050565b600080336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611e8657604051633006171960e21b815260040160405180910390fd5b600454610100900460ff1615611ec8576040517f1309a56300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ed46000612712565b90506000611ee182612d28565b8635935090506000611ef1612efe565b9050600080611f0186848361316b565b915091507f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb780914042760018483604051611f39939291906145f5565b60405180910390a1611f4b8583612ebc565b9150611f6782611f6160408c0160208d01614177565b8a61357c565b96508860a00135871015611fb4576040517f2746152a00000000000000000000000000000000000000000000000000000000815260a08a0135600482015260248101889052604401610bb4565b602085015115611fde576000611fcb866000613186565b9050611fdc6008868360008a6131a9565b505b5050505050935093915050565b600080600a60009054906101000a90046001600160a01b03166001600160a01b031663bf1eb64a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612041573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120659190614576565b9050806000036120785750600092915050565b600b546040517f7349615f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690637349615f906120e8907f00000000000000000000000000000000000000000000000000000000000000009085908890600190600401614776565b602060405180830381865afa158015612105573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121299190614576565b9392505050565b6121796040518060e001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001606081525090565b6060856000036121b5576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006121bf612efe565b905060006121ce888383612918565b905060006121dc6000612712565b90506121f16121eb8284612ebc565b8961369d565b8986526001600160a01b0389166020870152604086018890526060860187905260808601819052915061222682886001612918565b60a08601526040805160018082528183019092529060208083019080368337019050509350828460008151811061225f5761225f614547565b60200260200101818152505050505094509492505050565b6002546001600160a01b031633146122a257604051633006171960e21b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d55491a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600280549091169055565b60008061231e83612712565b905080602001518160000151111561237857602081015181519081039060009061235390670de0b6b3a76400008460016133d4565b90506fffffffffffffffffffffffffffffffff81101561237557949350505050565b50505b506fffffffffffffffffffffffffffffffff92915050565b600080546001600160a01b0384811691161480610b195750506001600160a01b039190911660009081526001602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000009094168352929052205460ff1690565b60006124036000612712565b9050600061241082612d28565b600a549091506000906001600160a01b031663e9e11f108635602088013561243b60408a018a6147b4565b8a606001356040518663ffffffff1660e01b8152600401612460959493929190614820565b6020604051808303816000875af115801561247f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a39190614576565b905060006124d484846124bc60a08a0160808b01614885565b6124cc60c08b0160a08c01614885565b60008a6136e6565b60408051848152602089810135908201526fffffffffffffffffffffffffffffffff868116828401528316606082015290519192507f43ae12d1ef33f7118bafdb1d8477fa6b6dbb0b21df5741fe86f4baa7a8bd13f9919081900360800190a1505050505050565b60006125486000612712565b9050600061255582612d28565b600a5490915060009081906001600160a01b031663eedc07138735602089013561258260408b018b6147b4565b8b606001356040518663ffffffff1660e01b81526004016125a7959493929190614820565b60408051808303816000875af11580156125c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e991906148a0565b90925090508535821461265f578461265f576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016600482015286356024820152604401610bb4565b600061268e858561267660a08b0160808c01614885565b61268660c08c0160a08d01614885565b60018b6136e6565b90507f43ae12d1ef33f7118bafdb1d8477fa6b6dbb0b21df5741fe86f4baa7a8bd13f96126be60208901356148c4565b6126c884866148fc565b6126d1906148c4565b6040805192835260208301919091526fffffffffffffffffffffffffffffffff8781168383015284166060830152519081900360800190a150505050505050565b61273d6040518060800160405280600081526020016000815260200160008152602001600081525090565b612745610fa4565b815261275082611feb565b8160200181815250507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127db9190614576565b6040820152919050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031614612827576000610b19565b50919050565b600082604001516000036128545761284d61284661380c565b83906139a0565b9050610b1c565b600061285f84612e98565b90508060000361289b576040517fefbc415500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408401516128ae9084908360006133d4565b949350505050565b600c54600754600091610f0d9183916001600160a01b0316907f00000000000000000000000000000000000000000000000000000000000000009067ffffffffffffffff808216917001000000000000000000000000000000009004166139b4565b600061271083810390841061292e57600061293c565b61293c8582612710866133d4565b95945050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031603612a2457600a54604080517f4eb75f40000000000000000000000000000000000000000000000000000000008152815160009384936001600160a01b0390911692634eb75f4092600480830193928290030181865afa1580156129e3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a0791906148a0565b9150915081600014612a1957806128ae565b600019949350505050565b919050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632ab4d0526040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aae9190614576565b90506000198103612ac3575060001992915050565b600083604001518211612ad7576000612adf565b836040015182035b9050612af5612aec6128b6565b82906000613519565b90506128ae8482612ebc565b60008160200151600003612b185750600019919050565b6008546020830151600091612b5d919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16670de0b6b3a7640000846133d4565b8351909150811115612b73579151909103919050565b50600092915050565b806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff161115612bf6576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610bb4565b6fffffffffffffffffffffffffffffffff908116700100000000000000000000000000000000029116179055565b600a5481546040517f3d33809d0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526001600160a01b0390911690633d33809d90602401602060405180830381865afa158015612c9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cbe919061490f565b61095d5780546040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff80831660048301527001000000000000000000000000000000009092049091166024820152604401610bb4565b60008160200151600014612d775760208201518251600091612d559190670de0b6b3a764000090846133d4565b90506fffffffffffffffffffffffffffffffff811015612d755792915050565b505b506fffffffffffffffffffffffffffffffff919050565b60006001600160a01b038416612dd3576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610bb4565b506040517f950212800000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528281166024830152849190821690639502128090604401602060405180830381865afa158015612e3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e62919061490f565b612129576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008160200151826000015111612eb0576000610b1c565b50602081015190510390565b60008260400151600014612eea57612ee5612ed684612e98565b604085015184919060006133d4565b610b19565b610b19612ef561380c565b83906000613b8a565b600c54600754600091610f0d916001916001600160a01b0316907f00000000000000000000000000000000000000000000000000000000000000009067ffffffffffffffff6801000000000000000082048116917001000000000000000000000000000000009004166139b4565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052611806908490613bf6565b60055460009060ff161561300257506001919050565b816001600160a01b03163b60000361301c57506001919050565b506001600160a01b031660009081526006602052604090205460ff1690565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b03160361312e5750600a5481906001600160a01b03908116906130b5907f0000000000000000000000000000000000000000000000000000000000000000168284612f6c565b6040517f35403023000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b03821690633540302390602401600060405180830381600087803b15801561311057600080fd5b505af1158015613124573d6000803e3d6000fd5b5050505050610b1c565b6040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610bb4565b600080613179858585612918565b9594869003949350505050565b6000613190610fa4565b835261319b82611feb565b6020840152610b1983612d28565b60008260018111156131bd576131bd61458f565b036132c057836fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff161115613244576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8086166004830181905290851660248301526044820152606401610bb4565b84546fffffffffffffffffffffffffffffffff9081169084168111156132ba576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8087166004830152808616602483015282166044820152606401610bb4565b50611e33565b836fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff161015613342576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8086166004830181905290851660248301526044820152606401610bb4565b84546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000090910481169084168110156133cc576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8087166004830152808616602483015282166044820152606401610bb4565b505050505050565b60006133e1858585613cf8565b905060018260018111156133f7576133f761458f565b036128ae57828061340a5761340a61492c565b848609156128ae57600019811015613424576001016128ae565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610bb4565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031603610b1c57600a60009054906101000a90046001600160a01b03166001600160a01b031663e4e889546040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b199190614576565b60008260000361352a575082612129565b6127108310613565576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008361271003905061293c8561271083866133d4565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b03160361312e5750600a546040517ef714ce000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b038381166024830152859260009291169062f714ce906044016020604051808303816000875af1158015613626573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061364a9190614576565b9050848114613697576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260248101869052604401610bb4565b50612129565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316146136df576000610b19565b5090919050565b60006136f3876000613186565b9050846fffffffffffffffffffffffffffffffff8082169083161015613769576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8089166004830152808416602483015282166044820152606401610bb4565b849050806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff1611156137ee576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8089166004830152808416602483015282166044820152606401610bb4565b50816138025761380260098783868b6131a9565b9695505050505050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561386d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613891919061495b565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613917919061495b565b90508060ff168260ff161115613984576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166004820152602401610bb4565b61398e828261497e565b61399990600a614a7b565b9250505090565b6000816001146136df57612ee58284614a8a565b6000806000806000886001600160a01b03166369994511600060018060016040518563ffffffff1660e01b81526004016139f19493929190614aa1565b608060405180830381865afa158015613a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a329190614ac9565b93509350935093506000886001600160a01b0316836001600160a01b031603613a5d57506001613ab3565b886001600160a01b0316826001600160a01b031614613ab3576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b038a166004820152602401610bb4565b600080808d6001811115613ac957613ac961458f565b03613afb578215613ae85785871015613ae3575050848403845b613b23565b85871115613ae357505083850385613b23565b8215613b145785871115613ae357505083850384613b23565b85871015613b23575050848403855b81600003613b44578967ffffffffffffffff1697505050505050505061293c565b6000613b53838b8460016133d4565b9050808b67ffffffffffffffff1611613b6c5780613b78565b8a67ffffffffffffffff165b9e9d5050505050505050505050505050565b600082600103613b9b575082612129565b6000826001811115613baf57613baf61458f565b03613bcb57828481613bc357613bc361492c565b049050612129565b8315613bec57826001850381613be357613be361492c565b046001016128ae565b6000949350505050565b6000613c4b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613de59092919063ffffffff16565b9050805160001480613c6c575080806020019051810190613c6c919061490f565b611806576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610bb4565b6000808060001985870985870292508281108382030391505080600003613d3257838281613d2857613d2861492c565b0492505050612129565b838110613d7c576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610bb4565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60606128ae848460008585600080866001600160a01b03168587604051613e0c9190614b14565b60006040518083038185875af1925050503d8060008114613e49576040519150601f19603f3d011682016040523d82523d6000602084013e613e4e565b606091505b5091509150613e5f87838387613e6a565b979650505050505050565b60608315613ef3578251600003613eec576001600160a01b0385163b613eec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bb4565b50816128ae565b6128ae8383815115613f085781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bb49190614b30565b600060c0828403121561282757600080fd5b600060208284031215613f6057600080fd5b813567ffffffffffffffff811115613f7757600080fd5b6128ae84828501613f3c565b6001600160a01b038116811461095d57600080fd5b60008060008060808587031215613fae57600080fd5b843593506020850135613fc081613f83565b93969395505050506040820135916060013590565b60005b83811015613ff0578181015183820152602001613fd8565b50506000910152565b60008151808452614011816020860160208601613fd5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600081518084526020808501945080840160005b8381101561407357815187529582019590820190600101614057565b509495945050505050565b604081526001600160a01b0383511660408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015260a083015160e0820152600060c084015160e06101008401526140e1610120840182613ff9565b9050828103602084015261293c8185614043565b803560028110612a2457600080fd5b6000806040838503121561411757600080fd5b82359150614127602084016140f5565b90509250929050565b801515811461095d57600080fd5b6000806040838503121561415157600080fd5b823561415c81613f83565b9150602083013561416c81614130565b809150509250929050565b60006020828403121561418957600080fd5b813561212981613f83565b80356fffffffffffffffffffffffffffffffff81168114612a2457600080fd5b600080604083850312156141c757600080fd5b6141d083614194565b915061412760208401614194565b6000602082840312156141f057600080fd5b813561212981614130565b6000806040838503121561420e57600080fd5b823561421981613f83565b9150602083013561416c81613f83565b803561ffff81168114612a2457600080fd5b60008060006060848603121561425057600080fd5b61425984614229565b925061426760208501614229565b9150604084013562ffffff8116811461427f57600080fd5b809150509250925092565b60006020828403121561429c57600080fd5b610b19826140f5565b6020808252825182820181905260009190848201906040850190845b818110156142e65783516001600160a01b0316835292840192918401916001016142c1565b50909695505050505050565b60008060006060848603121561430757600080fd5b833561431281613f83565b9250602084013561432281613f83565b929592945050506040919091013590565b600060e0828403121561282757600080fd5b6000806040838503121561435857600080fd5b823561436381613f83565b9150602083013567ffffffffffffffff81111561437f57600080fd5b61438b85828601614333565b9150509250929050565b60006040828403121561282757600080fd5b6000806000604084860312156143bc57600080fd5b83356143c781613f83565b9250602084013567ffffffffffffffff808211156143e457600080fd5b818601915086601f8301126143f857600080fd5b81358181111561440757600080fd5b8760208260061b850101111561441c57600080fd5b6020830194508093505050509250925092565b60008060006060848603121561444457600080fd5b833561444f81613f83565b9250602084013567ffffffffffffffff81111561446b57600080fd5b61447786828701614333565b925050604084013561427f81613f83565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114612a2457600080fd5b600080604083850312156144cb57600080fd5b82356144d681613f83565b915061412760208401614488565b60408152825160408201526001600160a01b03602084015116606082015260408301516080820152606083015160a0820152608083015160c082015260a083015160e0820152600060c084015160e06101008401526140e1610120840182613ff9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561458857600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061095d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60608101614602856145be565b938152602081019290925260409091015290565b60408101823561462581614130565b15158252602083013561463781614130565b80151560208401525092915050565b813561465181614130565b815460ff19811691151560ff169182178355602084013561467181614130565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009190911690911790151560081b61ff001617905550565b6000604082840312156146bb57600080fd5b6040516040810181811067ffffffffffffffff82111715614705577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405261471183614488565b8152602083013561472181614130565b60208201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000600019820361476f5761476f61472d565b5060010190565b6001600160a01b03851681526020810184905260808101614796846145be565b8360408301526147a5836145be565b82606083015295945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126147e957600080fd5b83018035915067ffffffffffffffff82111561480457600080fd5b60200191503681900382131561481957600080fd5b9250929050565b85815284602082015260806040820152826080820152828460a0830137600060a08483010152600060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011683010190508260608301529695505050505050565b60006020828403121561489757600080fd5b610b1982614194565b600080604083850312156148b357600080fd5b505080516020909101519092909150565b60007f800000000000000000000000000000000000000000000000000000000000000082036148f5576148f561472d565b5060000390565b80820180821115610b1c57610b1c61472d565b60006020828403121561492157600080fd5b815161212981614130565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561496d57600080fd5b815160ff8116811461212957600080fd5b60ff8281168282160390811115610b1c57610b1c61472d565b600181815b808511156149d25781600019048211156149b8576149b861472d565b808516156149c557918102915b93841c939080029061499c565b509250929050565b6000826149e957506001610b1c565b816149f657506000610b1c565b8160018114614a0c5760028114614a1657614a32565b6001915050610b1c565b60ff841115614a2757614a2761472d565b50506001821b610b1c565b5060208310610133831016604e8410600b8410161715614a55575081810a610b1c565b614a5f8383614997565b8060001904821115614a7357614a7361472d565b029392505050565b6000610b1960ff8416836149da565b8082028115828204841417610b1c57610b1c61472d565b60808101614aae866145be565b858252614aba856145be565b846020830152614796846145be565b60008060008060808587031215614adf57600080fd5b84519350602085015192506040850151614af881613f83565b6060860151909250614b0981613f83565b939692955090935050565b60008251614b26818460208701613fd5565b9190910192915050565b602081526000610b196020830184613ff956fea26469706673582212207bc8921c393199fb74fd050e0ff2915f0eb86f759f299c5db5de882514204ba064736f6c63430008130033000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d800000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a0205000000000000000000000000bb051dad562efe87a594cd1dd0a2263706abe125
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061036d5760003560e01c806380f51c12116101d3578063baaaa7b111610104578063e04610ed116100a2578063eea2f45c1161007c578063eea2f45c146108bb578063f4325d67146107ea578063f69959a3146108ce578063f8d89898146108e157600080fd5b8063e04610ed1461086f578063e664b02014610892578063ebbc4965146108b357600080fd5b8063c677e275116100de578063c677e27514610810578063d4da79b314610823578063da5e0f0e1461082e578063daeccc791461084157600080fd5b8063baaaa7b1146107c4578063bfccf0ec146107d7578063c55dae63146107ea57600080fd5b8063a7229fd911610171578063b07c63c71161014b578063b07c63c714610764578063b1f8100d14610777578063b5a2d9a91461078a578063ba2c46491461079d57600080fd5b8063a7229fd914610714578063a8e93cdb14610727578063aaf5eb681461075557600080fd5b806395c5b3b6116101ad57806395c5b3b6146106c157806395ea64a5146106d45780639a6b27cf146106e7578063a515b8ec146106f757600080fd5b806380f51c121461068b5780638a83c9cd146106505780638da5cb5b146106ae57600080fd5b80634d7d9c01116102ad5780635fbbc0d21161024b57806365f2ba2f1161022557806365f2ba2f1461063d5780636a1eb7b8146106505780637180c8ca1461066557806372e0c0f41461067857600080fd5b80635fbbc0d2146105cb5780636026220d14610617578063643b1e501461062a57600080fd5b806352e648f01161028757806352e648f01461057f57806353740e0414610592578063572a9302146105a55780635aa89173146105b857600080fd5b80634d7d9c01146105575780634edd74e81461056a5780634ee643a51461057257600080fd5b806329aa41361161031a578063431072f7116102f4578063431072f7146104ab57806346fbf68e146104be5780634c9da063146104fa5780634cdf587a1461052e57600080fd5b806329aa41361461047d5780632b96895814610490578063415a12711461049857600080fd5b806319000c421161034b57806319000c42146103fc5780631a0377d11461043b57806324b821ab1461045c57600080fd5b806302cb4ed1146103725780630bd7260d1461038757806313da2d4a1461039a575b600080fd5b610385610380366004613f4e565b610907565b005b610385610395366004613f4e565b610960565b6008546103ce906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b604080516fffffffffffffffffffffffffffffffff9384168152929091166020830152015b60405180910390f35b6104237f000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a020581565b6040516001600160a01b0390911681526020016103f3565b61044e610449366004613f98565b6109b6565b6040516103f392919061407e565b61046f61046a366004614104565b610b05565b6040519081526020016103f3565b61038561048b36600461413e565b610b22565b610385610c6c565b61046f6104a6366004614177565b610d6e565b6103856104b93660046141b4565b610dd9565b6104ea6104cc366004614177565b6001600160a01b031660009081526003602052604090205460ff1690565b60405190151581526020016103f3565b6009546103ce906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b610536610ef9565b6040516fffffffffffffffffffffffffffffffff90911681526020016103f3565b6103856105653660046141de565b610f12565b61046f610fa4565b6005546104ea9060ff1681565b600c54610423906001600160a01b031681565b6103856105a0366004613f4e565b61102b565b6103856105b33660046141fb565b611081565b6103856105c636600461423b565b611202565b6007546040805167ffffffffffffffff808416825268010000000000000000840481166020830152700100000000000000000000000000000000909304909216908201526060016103f3565b61046f61062536600461428a565b61139a565b61046f610638366004614104565b6113ad565b61038561064b366004613f4e565b6113c1565b610658611417565b6040516103f391906142a5565b61038561067336600461413e565b611490565b600b54610423906001600160a01b031681565b6104ea610699366004614177565b60036020526000908152604090205460ff1681565b600054610423906001600160a01b031681565b6103856106cf3660046141b4565b611533565b6103856106e2366004614177565b61164f565b600454610100900460ff166104ea565b6106ff61173f565b604080519283526020830191909152016103f3565b6103856107223660046142f2565b61175a565b61073a61073536600461428a565b61180b565b604080519384526020840192909252908201526060016103f3565b61046f670de0b6b3a764000081565b61046f610772366004614345565b611850565b610385610785366004614177565b611a58565b61046f610798366004614177565b611b67565b6104237f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b68581565b6103856107d2366004614395565b611c15565b6103856107e53660046143a7565b611c89565b7f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685610423565b6106ff61081e36600461442f565b611e3a565b60045460ff166104ea565b61046f61083c36600461428a565b611feb565b6104ea61084f3660046144b8565b600160209081526000928352604080842090915290825290205460ff1681565b6104ea61087d366004614177565b60066020526000908152604090205460ff1681565b6108a56108a0366004613f98565b612130565b6040516103f39291906144e4565b610385612277565b6105366108c936600461428a565b612312565b600a54610423906001600160a01b031681565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48610423565b610935336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61095257604051633006171960e21b815260040160405180910390fd5b61095d8160006123f7565b50565b61098e336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b6109ab57604051633006171960e21b815260040160405180910390fd5b61095d81600061253c565b6109ff6040518060e0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001606081525090565b606085600003610a3b576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610a476000612712565b90506000610a5587896127e5565b90506000610a63838361282d565b90506000610a6f6128b6565b9050610a7d82826000612918565b6001600160a01b038a168752602087018b90526040870189905260608701889052608087018190529150610ab382896001612918565b60a087015260408051600180825281830190925290602080830190803683370190505094508085600081518110610aec57610aec614547565b6020026020010181815250505050505094509492505050565b6000610b19610b1383612712565b8461282d565b90505b92915050565b610b50336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b610b6d57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038216610bbd576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024015b60405180910390fd5b816001600160a01b03163b600003610c0c576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610bb4565b6001600160a01b038216600081815260066020908152604091829020805460ff191685151590811790915591519182527fee397872136f42e5319f2ebe127140e4741bee3ff02b86b7410a0b02778216de91015b60405180910390a25050565b610c9a336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b610cb757604051633006171960e21b815260040160405180910390fd5b6002546001600160a01b031661dead14610d0c576002546040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b039091166004820152602401610bb4565b600080546040516001600160a01b03909116907f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d554908390a3600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055565b600080610d7b6000612712565b9050610d8683612945565b91506000610d9382612a29565b90506000610da083612b01565b905081811015610dae578091505b600019821015610dc557610dc285836127e5565b91505b83821015610dd1578193505b505050919050565b610e07336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b610e2457604051633006171960e21b815260040160405180910390fd5b670de0b6b3a7640000826fffffffffffffffffffffffffffffffff1611610e93576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610bb4565b604080516fffffffffffffffffffffffffffffffff8085168252831660208201527f792e77bcc33e678fd2a12f7a1ae644dcef1311589d15e9e8abd47adf5aca5e18910160405180910390a1610eeb60088383612b7c565b610ef56008612c24565b5050565b6000610f0d610f086000612712565b612d28565b905090565b610f40336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b610f5d57604051633006171960e21b815260040160405180910390fd5b6005805460ff19168215159081179091556040519081527faf941e5e6c2b1ae06b5434c080d9f9ba2b0c2ac8e125a6010a22b57201f26a349060200160405180910390a150565b600a54604080517fe4e8895400000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163e4e889549160048083019260209291908290030181865afa158015611007573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0d9190614576565b611059336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61107657604051633006171960e21b815260040160405180910390fd5b61095d8160016123f7565b6110af336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b6110cc57604051633006171960e21b815260040160405180910390fd5b611117827f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6857f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48612d8e565b600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039290921691909117905561119a817f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6857f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48612d8e565b600c80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03928316179055604051828216918416907f4073b32a832187a54e475fc8fff20266eedffb63596adf4c0b8a2966e2b0025d90600090a35050565b611230336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61124d57604051633006171960e21b815260040160405180910390fd5b6127108361ffff16111561128d576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6127108261ffff1611156112cd576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161ffff85811682528416602082015262ffffff83168183015290517f91829fbe10211c7b1218c9e5b102bdabb13e9526b494b9741133a39e973f9f549181900360600190a16007805461ffff9485167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617929093166801000000000000000002919091177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1662ffffff9190911670010000000000000000000000000000000002179055565b6000610b1c6113a883612712565b612e98565b6000610b196113bb83612712565b84612ebc565b6113ef336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61140c57604051633006171960e21b815260040160405180910390fd5b61095d81600161253c565b604080516001808252818301909252606091602080830190803683370190505090507f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6858160008151811061146d5761146d614547565b60200260200101906001600160a01b031690816001600160a01b03168152505090565b6114be336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b6114db57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038216600081815260036020908152604091829020805460ff191685151590811790915591519182527fa11b5803b8a35081b8f993e0dee5bc30301a3d83f644e5ab2ff39f972f0a807f9101610c60565b611561336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61157e57604051633006171960e21b815260040160405180910390fd5b670de0b6b3a7640000826fffffffffffffffffffffffffffffffff16116115ed576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610bb4565b604080516fffffffffffffffffffffffffffffffff8085168252831660208201527f23b4d5cfa83d92da7d76ae644647a3e53678133ad0fa7d2b526ad571af12d1ec910160405180910390a161164560098383612b7c565b610ef56009612c24565b61167d336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b61169a57604051633006171960e21b815260040160405180910390fd5b6001600160a01b0381166116dd576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610bb4565b600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517ffd6cdd9cefbd87614b7f366cb2bd5f7ba60b9a4b9febfbaf6344ecd1e03d018390600090a250565b60008061174a6128b6565b9150611754612efe565b90509091565b611788336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b6117a557604051633006171960e21b815260040160405180910390fd5b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f9096836040516117ea91815260200190565b60405180910390a36118066001600160a01b0384168383612f6c565b505050565b60008060008061181a85612712565b90508060000151816020015161182f83612d28565b91955093506fffffffffffffffffffffffffffffffff169150509193909250565b6000336001600160a01b037f000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a0205161461189b57604051633006171960e21b815260040160405180910390fd5b60045460ff16156118d8576040517f1309a56300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6118e183612fec565b6118fe57604051633006171960e21b815260040160405180910390fd5b600061190a6000612712565b9050600061191782612d28565b9050600061193561192b6020870187614177565b866020013561303b565b9050611941838261282d565b935060008061194e6128b6565b905061195c8682600061316b565b60405191975092507f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb78091404279061199690600090849086906145f5565b60405180910390a1856000036119d8576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8660a00135861015611a23576040517f2746152a00000000000000000000000000000000000000000000000000000000815260a0880135600482015260248101879052604401610bb4565b602085015115611a4d576000611a3a866000613186565b9050611a4b6008868360018a6131a9565b505b505050505092915050565b611a86336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b611aa357604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611aee576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610bb4565b600254600080546040516001600160a01b03808616948116939216917f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d891a4600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b600080611b746000612712565b6008546020820151919250600091611ba8916fffffffffffffffffffffffffffffffff16670de0b6b3a764000060016133d4565b90508082600001511115611c0e5781518190036000611bc78685613467565b905081811015611bd5578091505b611bdf848361282d565b9450611bf5611bec612efe565b86906000613519565b94508360400151851115611c0b57836040015194505b50505b5050919050565b3360009081526003602052604090205460ff16611c4557604051633006171960e21b815260040160405180910390fd5b7f803ee193075547dae36361498f3de5e399cdb29b7e0c7b680533f3da8b733a1781604051611c749190614616565b60405180910390a18060046118068282614646565b611cb7336000357fffffffff0000000000000000000000000000000000000000000000000000000016612390565b611cd457604051633006171960e21b815260040160405180910390fd5b6001600160a01b038316611d1f576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610bb4565b604080518082019091526000808252602082015260005b82811015611e3357838382818110611d5057611d50614547565b905060400201803603810190611d6691906146a9565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b038716600090815260018352604080822086517fffffffff0000000000000000000000000000000000000000000000000000000016835290935291909120805460ff1916911515919091179055611e2c8161475c565b9050611d36565b5050505050565b600080336001600160a01b037f000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a02051614611e8657604051633006171960e21b815260040160405180910390fd5b600454610100900460ff1615611ec8576040517f1309a56300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611ed46000612712565b90506000611ee182612d28565b8635935090506000611ef1612efe565b9050600080611f0186848361316b565b915091507f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb780914042760018483604051611f39939291906145f5565b60405180910390a1611f4b8583612ebc565b9150611f6782611f6160408c0160208d01614177565b8a61357c565b96508860a00135871015611fb4576040517f2746152a00000000000000000000000000000000000000000000000000000000815260a08a0135600482015260248101889052604401610bb4565b602085015115611fde576000611fcb866000613186565b9050611fdc6008868360008a6131a9565b505b5050505050935093915050565b600080600a60009054906101000a90046001600160a01b03166001600160a01b031663bf1eb64a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612041573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120659190614576565b9050806000036120785750600092915050565b600b546040517f7349615f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690637349615f906120e8907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489085908890600190600401614776565b602060405180830381865afa158015612105573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906121299190614576565b9392505050565b6121796040518060e001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001606081525090565b6060856000036121b5576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006121bf612efe565b905060006121ce888383612918565b905060006121dc6000612712565b90506121f16121eb8284612ebc565b8961369d565b8986526001600160a01b0389166020870152604086018890526060860187905260808601819052915061222682886001612918565b60a08601526040805160018082528183019092529060208083019080368337019050509350828460008151811061225f5761225f614547565b60200260200101818152505050505094509492505050565b6002546001600160a01b031633146122a257604051633006171960e21b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d55491a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600280549091169055565b60008061231e83612712565b905080602001518160000151111561237857602081015181519081039060009061235390670de0b6b3a76400008460016133d4565b90506fffffffffffffffffffffffffffffffff81101561237557949350505050565b50505b506fffffffffffffffffffffffffffffffff92915050565b600080546001600160a01b0384811691161480610b195750506001600160a01b039190911660009081526001602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000009094168352929052205460ff1690565b60006124036000612712565b9050600061241082612d28565b600a549091506000906001600160a01b031663e9e11f108635602088013561243b60408a018a6147b4565b8a606001356040518663ffffffff1660e01b8152600401612460959493929190614820565b6020604051808303816000875af115801561247f573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a39190614576565b905060006124d484846124bc60a08a0160808b01614885565b6124cc60c08b0160a08c01614885565b60008a6136e6565b60408051848152602089810135908201526fffffffffffffffffffffffffffffffff868116828401528316606082015290519192507f43ae12d1ef33f7118bafdb1d8477fa6b6dbb0b21df5741fe86f4baa7a8bd13f9919081900360800190a1505050505050565b60006125486000612712565b9050600061255582612d28565b600a5490915060009081906001600160a01b031663eedc07138735602089013561258260408b018b6147b4565b8b606001356040518663ffffffff1660e01b81526004016125a7959493929190614820565b60408051808303816000875af11580156125c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125e991906148a0565b90925090508535821461265f578461265f576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816600482015286356024820152604401610bb4565b600061268e858561267660a08b0160808c01614885565b61268660c08c0160a08d01614885565b60018b6136e6565b90507f43ae12d1ef33f7118bafdb1d8477fa6b6dbb0b21df5741fe86f4baa7a8bd13f96126be60208901356148c4565b6126c884866148fc565b6126d1906148c4565b6040805192835260208301919091526fffffffffffffffffffffffffffffffff8781168383015284166060830152519081900360800190a150505050505050565b61273d6040518060800160405280600081526020016000815260200160008152602001600081525090565b612745610fa4565b815261275082611feb565b8160200181815250507f000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a02056001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127b7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127db9190614576565b6040820152919050565b60007f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6856001600160a01b0316836001600160a01b031614612827576000610b19565b50919050565b600082604001516000036128545761284d61284661380c565b83906139a0565b9050610b1c565b600061285f84612e98565b90508060000361289b576040517fefbc415500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408401516128ae9084908360006133d4565b949350505050565b600c54600754600091610f0d9183916001600160a01b0316907f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6859067ffffffffffffffff808216917001000000000000000000000000000000009004166139b4565b600061271083810390841061292e57600061293c565b61293c8582612710866133d4565b95945050505050565b60007f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6856001600160a01b0316826001600160a01b031603612a2457600a54604080517f4eb75f40000000000000000000000000000000000000000000000000000000008152815160009384936001600160a01b0390911692634eb75f4092600480830193928290030181865afa1580156129e3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a0791906148a0565b9150915081600014612a1957806128ae565b600019949350505050565b919050565b6000807f000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a02056001600160a01b0316632ab4d0526040518163ffffffff1660e01b8152600401602060405180830381865afa158015612a8a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612aae9190614576565b90506000198103612ac3575060001992915050565b600083604001518211612ad7576000612adf565b836040015182035b9050612af5612aec6128b6565b82906000613519565b90506128ae8482612ebc565b60008160200151600003612b185750600019919050565b6008546020830151600091612b5d919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16670de0b6b3a7640000846133d4565b8351909150811115612b73579151909103919050565b50600092915050565b806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff161115612bf6576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610bb4565b6fffffffffffffffffffffffffffffffff908116700100000000000000000000000000000000029116179055565b600a5481546040517f3d33809d0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526001600160a01b0390911690633d33809d90602401602060405180830381865afa158015612c9a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612cbe919061490f565b61095d5780546040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff80831660048301527001000000000000000000000000000000009092049091166024820152604401610bb4565b60008160200151600014612d775760208201518251600091612d559190670de0b6b3a764000090846133d4565b90506fffffffffffffffffffffffffffffffff811015612d755792915050565b505b506fffffffffffffffffffffffffffffffff919050565b60006001600160a01b038416612dd3576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610bb4565b506040517f950212800000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528281166024830152849190821690639502128090604401602060405180830381865afa158015612e3e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e62919061490f565b612129576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008160200151826000015111612eb0576000610b1c565b50602081015190510390565b60008260400151600014612eea57612ee5612ed684612e98565b604085015184919060006133d4565b610b19565b610b19612ef561380c565b83906000613b8a565b600c54600754600091610f0d916001916001600160a01b0316907f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6859067ffffffffffffffff6801000000000000000082048116917001000000000000000000000000000000009004166139b4565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb00000000000000000000000000000000000000000000000000000000179052611806908490613bf6565b60055460009060ff161561300257506001919050565b816001600160a01b03163b60000361301c57506001919050565b506001600160a01b031660009081526006602052604090205460ff1690565b60007f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6856001600160a01b0316836001600160a01b03160361312e5750600a5481906001600160a01b03908116906130b5907f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685168284612f6c565b6040517f35403023000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b03821690633540302390602401600060405180830381600087803b15801561311057600080fd5b505af1158015613124573d6000803e3d6000fd5b5050505050610b1c565b6040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610bb4565b600080613179858585612918565b9594869003949350505050565b6000613190610fa4565b835261319b82611feb565b6020840152610b1983612d28565b60008260018111156131bd576131bd61458f565b036132c057836fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff161115613244576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8086166004830181905290851660248301526044820152606401610bb4565b84546fffffffffffffffffffffffffffffffff9081169084168111156132ba576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8087166004830152808616602483015282166044820152606401610bb4565b50611e33565b836fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff161015613342576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8086166004830181905290851660248301526044820152606401610bb4565b84546fffffffffffffffffffffffffffffffff70010000000000000000000000000000000090910481169084168110156133cc576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8087166004830152808616602483015282166044820152606401610bb4565b505050505050565b60006133e1858585613cf8565b905060018260018111156133f7576133f761458f565b036128ae57828061340a5761340a61492c565b848609156128ae57600019811015613424576001016128ae565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610bb4565b60007f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6856001600160a01b0316836001600160a01b031603610b1c57600a60009054906101000a90046001600160a01b03166001600160a01b031663e4e889546040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b199190614576565b60008260000361352a575082612129565b6127108310613565576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008361271003905061293c8561271083866133d4565b60007f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6856001600160a01b0316836001600160a01b03160361312e5750600a546040517ef714ce000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b038381166024830152859260009291169062f714ce906044016020604051808303816000875af1158015613626573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061364a9190614576565b9050848114613697576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260248101869052604401610bb4565b50612129565b60007f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6856001600160a01b0316826001600160a01b0316146136df576000610b19565b5090919050565b60006136f3876000613186565b9050846fffffffffffffffffffffffffffffffff8082169083161015613769576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8089166004830152808416602483015282166044820152606401610bb4565b849050806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff1611156137ee576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8089166004830152808416602483015282166044820152606401610bb4565b50816138025761380260098783868b6131a9565b9695505050505050565b6000807f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b6856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561386d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613891919061495b565b905060007f000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a02056001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156138f3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613917919061495b565b90508060ff168260ff161115613984576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685166004820152602401610bb4565b61398e828261497e565b61399990600a614a7b565b9250505090565b6000816001146136df57612ee58284614a8a565b6000806000806000886001600160a01b03166369994511600060018060016040518563ffffffff1660e01b81526004016139f19493929190614aa1565b608060405180830381865afa158015613a0e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a329190614ac9565b93509350935093506000886001600160a01b0316836001600160a01b031603613a5d57506001613ab3565b886001600160a01b0316826001600160a01b031614613ab3576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b038a166004820152602401610bb4565b600080808d6001811115613ac957613ac961458f565b03613afb578215613ae85785871015613ae3575050848403845b613b23565b85871115613ae357505083850385613b23565b8215613b145785871115613ae357505083850384613b23565b85871015613b23575050848403855b81600003613b44578967ffffffffffffffff1697505050505050505061293c565b6000613b53838b8460016133d4565b9050808b67ffffffffffffffff1611613b6c5780613b78565b8a67ffffffffffffffff165b9e9d5050505050505050505050505050565b600082600103613b9b575082612129565b6000826001811115613baf57613baf61458f565b03613bcb57828481613bc357613bc361492c565b049050612129565b8315613bec57826001850381613be357613be361492c565b046001016128ae565b6000949350505050565b6000613c4b826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613de59092919063ffffffff16565b9050805160001480613c6c575080806020019051810190613c6c919061490f565b611806576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610bb4565b6000808060001985870985870292508281108382030391505080600003613d3257838281613d2857613d2861492c565b0492505050612129565b838110613d7c576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610bb4565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b60606128ae848460008585600080866001600160a01b03168587604051613e0c9190614b14565b60006040518083038185875af1925050503d8060008114613e49576040519150601f19603f3d011682016040523d82523d6000602084013e613e4e565b606091505b5091509150613e5f87838387613e6a565b979650505050505050565b60608315613ef3578251600003613eec576001600160a01b0385163b613eec576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bb4565b50816128ae565b6128ae8383815115613f085781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610bb49190614b30565b600060c0828403121561282757600080fd5b600060208284031215613f6057600080fd5b813567ffffffffffffffff811115613f7757600080fd5b6128ae84828501613f3c565b6001600160a01b038116811461095d57600080fd5b60008060008060808587031215613fae57600080fd5b843593506020850135613fc081613f83565b93969395505050506040820135916060013590565b60005b83811015613ff0578181015183820152602001613fd8565b50506000910152565b60008151808452614011816020860160208601613fd5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600081518084526020808501945080840160005b8381101561407357815187529582019590820190600101614057565b509495945050505050565b604081526001600160a01b0383511660408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015260a083015160e0820152600060c084015160e06101008401526140e1610120840182613ff9565b9050828103602084015261293c8185614043565b803560028110612a2457600080fd5b6000806040838503121561411757600080fd5b82359150614127602084016140f5565b90509250929050565b801515811461095d57600080fd5b6000806040838503121561415157600080fd5b823561415c81613f83565b9150602083013561416c81614130565b809150509250929050565b60006020828403121561418957600080fd5b813561212981613f83565b80356fffffffffffffffffffffffffffffffff81168114612a2457600080fd5b600080604083850312156141c757600080fd5b6141d083614194565b915061412760208401614194565b6000602082840312156141f057600080fd5b813561212981614130565b6000806040838503121561420e57600080fd5b823561421981613f83565b9150602083013561416c81613f83565b803561ffff81168114612a2457600080fd5b60008060006060848603121561425057600080fd5b61425984614229565b925061426760208501614229565b9150604084013562ffffff8116811461427f57600080fd5b809150509250925092565b60006020828403121561429c57600080fd5b610b19826140f5565b6020808252825182820181905260009190848201906040850190845b818110156142e65783516001600160a01b0316835292840192918401916001016142c1565b50909695505050505050565b60008060006060848603121561430757600080fd5b833561431281613f83565b9250602084013561432281613f83565b929592945050506040919091013590565b600060e0828403121561282757600080fd5b6000806040838503121561435857600080fd5b823561436381613f83565b9150602083013567ffffffffffffffff81111561437f57600080fd5b61438b85828601614333565b9150509250929050565b60006040828403121561282757600080fd5b6000806000604084860312156143bc57600080fd5b83356143c781613f83565b9250602084013567ffffffffffffffff808211156143e457600080fd5b818601915086601f8301126143f857600080fd5b81358181111561440757600080fd5b8760208260061b850101111561441c57600080fd5b6020830194508093505050509250925092565b60008060006060848603121561444457600080fd5b833561444f81613f83565b9250602084013567ffffffffffffffff81111561446b57600080fd5b61447786828701614333565b925050604084013561427f81613f83565b80357fffffffff0000000000000000000000000000000000000000000000000000000081168114612a2457600080fd5b600080604083850312156144cb57600080fd5b82356144d681613f83565b915061412760208401614488565b60408152825160408201526001600160a01b03602084015116606082015260408301516080820152606083015160a0820152608083015160c082015260a083015160e0820152600060c084015160e06101008401526140e1610120840182613ff9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561458857600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061095d577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60608101614602856145be565b938152602081019290925260409091015290565b60408101823561462581614130565b15158252602083013561463781614130565b80151560208401525092915050565b813561465181614130565b815460ff19811691151560ff169182178355602084013561467181614130565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009190911690911790151560081b61ff001617905550565b6000604082840312156146bb57600080fd5b6040516040810181811067ffffffffffffffff82111715614705577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60405261471183614488565b8152602083013561472181614130565b60208201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000600019820361476f5761476f61472d565b5060010190565b6001600160a01b03851681526020810184905260808101614796846145be565b8360408301526147a5836145be565b82606083015295945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126147e957600080fd5b83018035915067ffffffffffffffff82111561480457600080fd5b60200191503681900382131561481957600080fd5b9250929050565b85815284602082015260806040820152826080820152828460a0830137600060a08483010152600060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011683010190508260608301529695505050505050565b60006020828403121561489757600080fd5b610b1982614194565b600080604083850312156148b357600080fd5b505080516020909101519092909150565b60007f800000000000000000000000000000000000000000000000000000000000000082036148f5576148f561472d565b5060000390565b80820180821115610b1c57610b1c61472d565b60006020828403121561492157600080fd5b815161212981614130565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006020828403121561496d57600080fd5b815160ff8116811461212957600080fd5b60ff8281168282160390811115610b1c57610b1c61472d565b600181815b808511156149d25781600019048211156149b8576149b861472d565b808516156149c557918102915b93841c939080029061499c565b509250929050565b6000826149e957506001610b1c565b816149f657506000610b1c565b8160018114614a0c5760028114614a1657614a32565b6001915050610b1c565b60ff841115614a2757614a2761472d565b50506001821b610b1c565b5060208310610133831016604e8410600b8410161715614a55575081810a610b1c565b614a5f8383614997565b8060001904821115614a7357614a7361472d565b029392505050565b6000610b1960ff8416836149da565b8082028115828204841417610b1c57610b1c61472d565b60808101614aae866145be565b858252614aba856145be565b846020830152614796846145be565b60008060008060808587031215614adf57600080fd5b84519350602085015192506040850151614af881613f83565b6060860151909250614b0981613f83565b939692955090935050565b60008251614b26818460208701613fd5565b9190910192915050565b602081526000610b196020830184613ff956fea26469706673582212207bc8921c393199fb74fd050e0ff2915f0eb86f759f299c5db5de882514204ba064736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d800000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb480000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a0205000000000000000000000000bb051dad562efe87a594cd1dd0a2263706abe125
-----Decoded View---------------
Arg [0] : _initialOwner (address): 0xb20AaE0Fe007519b7cE6f090a2aB8353B3Da5d80
Arg [1] : _reserveToken_ (address): 0x5BaE9a5D67d1CA5b09B14c91935f635CFBF3b685
Arg [2] : _debtToken_ (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [3] : _dynamicFeeOracleBaseToken (address): 0x5BaE9a5D67d1CA5b09B14c91935f635CFBF3b685
Arg [4] : _lovToken (address): 0xBd46AbF8999E979C4Ec507E8bE06b5D4402A0205
Arg [5] : _borrowLend (address): 0xBB051daD562eFE87a594Cd1DD0A2263706ABe125
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d80
Arg [1] : 0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685
Arg [2] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [3] : 0000000000000000000000005bae9a5d67d1ca5b09b14c91935f635cfbf3b685
Arg [4] : 000000000000000000000000bd46abf8999e979c4ec507e8be06b5d4402a0205
Arg [5] : 000000000000000000000000bb051dad562efe87a594cd1dd0a2263706abe125
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.