Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 from a total of 97 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Rebalance Up | 22170610 | 327 days ago | IN | 0 ETH | 0.00062442 | ||||
| Rebalance Up | 22170596 | 327 days ago | IN | 0 ETH | 0.00059038 | ||||
| Rebalance Up | 22170575 | 327 days ago | IN | 0 ETH | 0.00064759 | ||||
| Rebalance Up | 22170555 | 327 days ago | IN | 0 ETH | 0.00071884 | ||||
| Rebalance Up | 22170516 | 327 days ago | IN | 0 ETH | 0.00067333 | ||||
| Rebalance Up | 22170476 | 327 days ago | IN | 0 ETH | 0.00088286 | ||||
| Rebalance Up | 22170456 | 327 days ago | IN | 0 ETH | 0.00084769 | ||||
| Rebalance Up | 22170436 | 327 days ago | IN | 0 ETH | 0.00123434 | ||||
| Rebalance Up | 22170421 | 327 days ago | IN | 0 ETH | 0.00173776 | ||||
| Rebalance Up | 22170386 | 327 days ago | IN | 0 ETH | 0.00222152 | ||||
| Rebalance Up | 22170366 | 327 days ago | IN | 0 ETH | 0.00247458 | ||||
| Rebalance Up | 22170331 | 327 days ago | IN | 0 ETH | 0.00230942 | ||||
| Rebalance Up | 22170316 | 327 days ago | IN | 0 ETH | 0.00249696 | ||||
| Rebalance Up | 22170296 | 327 days ago | IN | 0 ETH | 0.00230614 | ||||
| Rebalance Down | 22167143 | 328 days ago | IN | 0 ETH | 0.00135587 | ||||
| Rebalance Down | 22165514 | 328 days ago | IN | 0 ETH | 0.0022026 | ||||
| Rebalance Up | 22158347 | 329 days ago | IN | 0 ETH | 0.00207636 | ||||
| Rebalance Up | 22157946 | 329 days ago | IN | 0 ETH | 0.00144648 | ||||
| Rebalance Up | 22157649 | 329 days ago | IN | 0 ETH | 0.00141157 | ||||
| Rebalance Up | 22157345 | 329 days ago | IN | 0 ETH | 0.00145738 | ||||
| Rebalance Up | 22155104 | 329 days ago | IN | 0 ETH | 0.00218855 | ||||
| Rebalance Up | 22145989 | 330 days ago | IN | 0 ETH | 0.00145135 | ||||
| Rebalance Up | 22145275 | 331 days ago | IN | 0 ETH | 0.00172434 | ||||
| Rebalance Up | 22144971 | 331 days ago | IN | 0 ETH | 0.00163316 | ||||
| Rebalance Up | 22144671 | 331 days ago | IN | 0 ETH | 0.00182722 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
OrigamiLovTokenMorphoManager
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 10000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (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 security@morpho.org
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user) external view returns (Position memory p);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
/// interest accrual.
function market(Id id) external view returns (Market memory m);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id) external view returns (MarketParams memory);
}// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/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;
function _init(address initialOwner) internal {
if (owner != address(0)) revert CommonEventsAndErrors.InvalidAccess();
if (initialOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
owner = initialOwner;
}
/**
* @notice Proposes a new Owner.
* Can only be called by the current owner
*/
function proposeNewOwner(address account) external override onlyElevatedAccess {
if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress(account);
emit NewOwnerProposed(owner, _proposedNewOwner, account);
_proposedNewOwner = account;
}
/**
* @notice Caller accepts the role as new Owner.
* Can only be called by the proposed owner
*/
function acceptOwner() external override {
if (msg.sender != _proposedNewOwner) revert CommonEventsAndErrors.InvalidAccess();
emit NewOwnerAccepted(owner, msg.sender);
owner = msg.sender;
delete _proposedNewOwner;
}
/**
* @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
* @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
*/
function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external override onlyElevatedAccess {
if (allowedCaller == address(0)) revert CommonEventsAndErrors.InvalidAddress(allowedCaller);
ExplicitAccess memory _access;
for (uint256 i; i < access.length; ++i) {
_access = access[i];
emit ExplicitAccessSet(allowedCaller, _access.fnSelector, _access.allowed);
explicitFunctionAccess[allowedCaller][_access.fnSelector] = _access.allowed;
}
}
function isElevatedAccess(address caller, bytes4 fnSelector) internal view returns (bool) {
return (
caller == owner ||
explicitFunctionAccess[caller][fnSelector]
);
}
/**
* @notice The owner is allowed to call, or if explicit access has been given to the caller.
* @dev Important: Only for use when called from an *external* contract.
* If a function with this modifier is called internally then the `msg.sig`
* will still refer to the top level externally called function.
*/
modifier onlyElevatedAccess() {
if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
_;
}
}pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (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 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/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 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";
/**
* @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 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": 10000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"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":[{"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
6101006040523480156200001257600080fd5b5060405162004ea438038062004ea4833981016040819052620000359162000222565b85828162000043816200018c565b506001600160a01b0390811660805286811660a05285811660c05284811660e052600a80546001600160a01b0319168483161790556040805163313ce56760e01b81529051600093509185169163313ce567916004808201926020929091908290030181865afa158015620000bc573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e29190620002a3565b60ff16905080866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000127573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200014d9190620002a3565b60ff16146200017f5760405163961c9a4f60e01b81526001600160a01b03871660048201526024015b60405180910390fd5b50505050505050620002cf565b6000546001600160a01b031615620001b757604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116620001e357604051634726455360e11b81526000600482015260240162000176565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b80516001600160a01b03811681146200021d57600080fd5b919050565b60008060008060008060c087890312156200023c57600080fd5b620002478762000205565b9550620002576020880162000205565b9450620002676040880162000205565b9350620002776060880162000205565b9250620002876080880162000205565b91506200029760a0880162000205565b90509295509295509295565b600060208284031215620002b657600080fd5b815160ff81168114620002c857600080fd5b9392505050565b60805160a05160c05160e051614afa620003aa6000396000818161078f0152818161107c015281816128340152612e7d0152600081816108d00152818161101a0152818161109d0152818161201c015261258e0152600081816107d901528181610ff9015281816113620152818161274c015281816128ac01528181612fa201528181612ff1015281816133ce015281816134e3015281816136040152818161377401526138ba0152600081816103f6015281816117a201528181611dab015281816126be0152818161299101526137fa0152614afa6000f3fe608060405234801561001057600080fd5b50600436106103625760003560e01c80638a83c9cd116101c8578063baaaa7b111610104578063e04610ed116100a2578063eea2f45c1161007c578063eea2f45c146108a8578063f4325d67146107d7578063f69959a3146108bb578063f8d89898146108ce57600080fd5b8063e04610ed1461085c578063e664b0201461087f578063ebbc4965146108a057600080fd5b8063c677e275116100de578063c677e275146107fd578063d4da79b314610810578063da5e0f0e1461081b578063daeccc791461082e57600080fd5b8063baaaa7b1146107b1578063bfccf0ec146107c4578063c55dae63146107d757600080fd5b8063a7229fd911610171578063b07c63c71161014b578063b07c63c714610751578063b1f8100d14610764578063b5a2d9a914610777578063ba2c46491461078a57600080fd5b8063a7229fd914610701578063a8e93cdb14610714578063aaf5eb681461074257600080fd5b806395ea64a5116101a257806395ea64a5146106c15780639a6b27cf146106d4578063a515b8ec146106e457600080fd5b80638a83c9cd1461063d5780638da5cb5b1461069b57806395c5b3b6146106ae57600080fd5b80634edd74e8116102a25780636026220d116102405780636a1eb7b81161021a5780636a1eb7b81461063d5780637180c8ca1461065257806372e0c0f41461066557806380f51c121461067857600080fd5b80636026220d14610604578063643b1e501461061757806365f2ba2f1461062a57600080fd5b806353740e041161027c57806353740e041461057f578063572a9302146105925780635aa89173146105a55780635fbbc0d2146105b857600080fd5b80634edd74e8146105575780634ee643a51461055f57806352e648f01461056c57600080fd5b806329aa41361161030f57806346fbf68e116102e957806346fbf68e146104ab5780634c9da063146104e75780634cdf587a1461051b5780634d7d9c011461054457600080fd5b806329aa413614610472578063415a127114610485578063431072f71461049857600080fd5b806319000c421161034057806319000c42146103f15780631a0377d11461043057806324b821ab1461045157600080fd5b806302cb4ed1146103675780630bd7260d1461037c57806313da2d4a1461038f575b600080fd5b61037a610375366004613eb1565b6108f4565b005b61037a61038a366004613eb1565b61094d565b6008546103c3906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b604080516fffffffffffffffffffffffffffffffff9384168152929091166020830152015b60405180910390f35b6104187f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103e8565b61044361043e366004613efb565b6109a3565b6040516103e8929190613fe1565b61046461045f366004614067565b610af2565b6040519081526020016103e8565b61037a6104803660046140a1565b610b0f565b6104646104933660046140da565b610c77565b61037a6104a6366004614117565b610ce2565b6104d76104b93660046140da565b6001600160a01b031660009081526003602052604090205460ff1690565b60405190151581526020016103e8565b6009546103c3906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b610523610e02565b6040516fffffffffffffffffffffffffffffffff90911681526020016103e8565b61037a610552366004614141565b610e1b565b610464610ecb565b6005546104d79060ff1681565b600c54610418906001600160a01b031681565b61037a61058d366004613eb1565b610f52565b61037a6105a036600461415e565b610fa8565b61037a6105b336600461419e565b611129565b6007546040805167ffffffffffffffff808416825268010000000000000000840481166020830152700100000000000000000000000000000000909304909216908201526060016103e8565b6104646106123660046141ed565b6112c1565b610464610625366004614067565b6112d4565b61037a610638366004613eb1565b6112e8565b61064561133e565b6040516103e89190614208565b61037a6106603660046140a1565b6113b7565b600b54610418906001600160a01b031681565b6104d76106863660046140da565b60036020526000908152604090205460ff1681565b600054610418906001600160a01b031681565b61037a6106bc366004614117565b611478565b61037a6106cf3660046140da565b611594565b600454610100900460ff166104d7565b6106ec611684565b604080519283526020830191909152016103e8565b61037a61070f366004614255565b61169f565b6107276107223660046141ed565b611750565b604080519384526020840192909252908201526060016103e8565b610464670de0b6b3a764000081565b61046461075f3660046142a8565b611795565b61037a6107723660046140da565b61199d565b6104646107853660046140da565b611aac565b6104187f000000000000000000000000000000000000000000000000000000000000000081565b61037a6107bf3660046142f8565b611b5a565b61037a6107d236600461430a565b611bce565b7f0000000000000000000000000000000000000000000000000000000000000000610418565b6106ec61080b366004614392565b611d9d565b60045460ff166104d7565b6104646108293660046141ed565b611f4e565b6104d761083c36600461441b565b600160209081526000928352604080842090915290825290205460ff1681565b6104d761086a3660046140da565b60066020526000908152604090205460ff1681565b61089261088d366004613efb565b612093565b6040516103e8929190614447565b61037a6121da565b6105236108b63660046141ed565b612275565b600a54610418906001600160a01b031681565b7f0000000000000000000000000000000000000000000000000000000000000000610418565b610922336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61093f57604051633006171960e21b815260040160405180910390fd5b61094a81600061235a565b50565b61097b336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61099857604051633006171960e21b815260040160405180910390fd5b61094a81600061249f565b6109ec6040518060e0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001606081525090565b606085600003610a28576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610a346000612675565b90506000610a428789612748565b90506000610a508383612790565b90506000610a5c612819565b9050610a6a8282600061287b565b6001600160a01b038a168752602087018b90526040870189905260608701889052608087018190529150610aa08289600161287b565b60a087015260408051600180825281830190925290602080830190803683370190505094508085600081518110610ad957610ad96144aa565b6020026020010181815250505050505094509492505050565b6000610b06610b0083612675565b84612790565b90505b92915050565b610b3d336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610b5a57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038216610baa576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024015b60405180910390fd5b816001600160a01b03163b600003610bf9576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610ba1565b6001600160a01b03821660008181526006602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527fee397872136f42e5319f2ebe127140e4741bee3ff02b86b7410a0b02778216de91015b60405180910390a25050565b600080610c846000612675565b9050610c8f836128a8565b91506000610c9c8261298c565b90506000610ca983612a64565b905081811015610cb7578091505b600019821015610cce57610ccb8583612748565b91505b83821015610cda578193505b505050919050565b610d10336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610d2d57604051633006171960e21b815260040160405180910390fd5b670de0b6b3a7640000826fffffffffffffffffffffffffffffffff1611610d9c576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610ba1565b604080516fffffffffffffffffffffffffffffffff8085168252831660208201527f792e77bcc33e678fd2a12f7a1ae644dcef1311589d15e9e8abd47adf5aca5e18910160405180910390a1610df460088383612adf565b610dfe6008612b87565b5050565b6000610e16610e116000612675565b612c8b565b905090565b610e49336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610e6657604051633006171960e21b815260040160405180910390fd5b600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168215159081179091556040519081527faf941e5e6c2b1ae06b5434c080d9f9ba2b0c2ac8e125a6010a22b57201f26a349060200160405180910390a150565b600a54604080517fe4e8895400000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163e4e889549160048083019260209291908290030181865afa158015610f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1691906144d9565b610f80336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610f9d57604051633006171960e21b815260040160405180910390fd5b61094a81600161235a565b610fd6336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610ff357604051633006171960e21b815260040160405180910390fd5b61103e827f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612cf1565b600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790556110c1817f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000612cf1565b600c80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03928316179055604051828216918416907f4073b32a832187a54e475fc8fff20266eedffb63596adf4c0b8a2966e2b0025d90600090a35050565b611157336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61117457604051633006171960e21b815260040160405180910390fd5b6127108361ffff1611156111b4576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6127108261ffff1611156111f4576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161ffff85811682528416602082015262ffffff83168183015290517f91829fbe10211c7b1218c9e5b102bdabb13e9526b494b9741133a39e973f9f549181900360600190a16007805461ffff9485167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617929093166801000000000000000002919091177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1662ffffff9190911670010000000000000000000000000000000002179055565b6000610b096112cf83612675565b612dfb565b6000610b066112e283612675565b84612e1f565b611316336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61133357604051633006171960e21b815260040160405180910390fd5b61094a81600161249f565b604080516001808252818301909252606091602080830190803683370190505090507f000000000000000000000000000000000000000000000000000000000000000081600081518110611394576113946144aa565b60200260200101906001600160a01b031690816001600160a01b03168152505090565b6113e5336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61140257604051633006171960e21b815260040160405180910390fd5b6001600160a01b03821660008181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527fa11b5803b8a35081b8f993e0dee5bc30301a3d83f644e5ab2ff39f972f0a807f9101610c6b565b6114a6336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b6114c357604051633006171960e21b815260040160405180910390fd5b670de0b6b3a7640000826fffffffffffffffffffffffffffffffff1611611532576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610ba1565b604080516fffffffffffffffffffffffffffffffff8085168252831660208201527f23b4d5cfa83d92da7d76ae644647a3e53678133ad0fa7d2b526ad571af12d1ec910160405180910390a161158a60098383612adf565b610dfe6009612b87565b6115c2336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b6115df57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611622576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610ba1565b600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517ffd6cdd9cefbd87614b7f366cb2bd5f7ba60b9a4b9febfbaf6344ecd1e03d018390600090a250565b60008061168f612819565b9150611699612e61565b90509091565b6116cd336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b6116ea57604051633006171960e21b815260040160405180910390fd5b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f90968360405161172f91815260200190565b60405180910390a361174b6001600160a01b0384168383612ecf565b505050565b60008060008061175f85612675565b90508060000151816020015161177483612c8b565b91955093506fffffffffffffffffffffffffffffffff169150509193909250565b6000336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146117e057604051633006171960e21b815260040160405180910390fd5b60045460ff161561181d576040517f1309a56300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61182683612f4f565b61184357604051633006171960e21b815260040160405180910390fd5b600061184f6000612675565b9050600061185c82612c8b565b9050600061187a61187060208701876140da565b8660200135612f9e565b90506118868382612790565b9350600080611893612819565b90506118a1868260006130ce565b60405191975092507f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb7809140427906118db9060009084908690614558565b60405180910390a18560000361191d576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8660a00135861015611968576040517f2746152a00000000000000000000000000000000000000000000000000000000815260a0880135600482015260248101879052604401610ba1565b60208501511561199257600061197f8660006130e9565b90506119906008868360018a61310c565b505b505050505092915050565b6119cb336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b6119e857604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611a33576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610ba1565b600254600080546040516001600160a01b03808616948116939216917f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d891a4600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b600080611ab96000612675565b6008546020820151919250600091611aed916fffffffffffffffffffffffffffffffff16670de0b6b3a76400006001613337565b90508082600001511115611b535781518190036000611b0c86856133ca565b905081811015611b1a578091505b611b248483612790565b9450611b3a611b31612e61565b8690600061347c565b94508360400151851115611b5057836040015194505b50505b5050919050565b3360009081526003602052604090205460ff16611b8a57604051633006171960e21b815260040160405180910390fd5b7f803ee193075547dae36361498f3de5e399cdb29b7e0c7b680533f3da8b733a1781604051611bb99190614579565b60405180910390a180600461174b82826145a9565b611bfc336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b611c1957604051633006171960e21b815260040160405180910390fd5b6001600160a01b038316611c64576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610ba1565b604080518082019091526000808252602082015260005b82811015611d9657838382818110611c9557611c956144aa565b905060400201803603810190611cab919061462a565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b038716600090815260018352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055611d8f816146dd565b9050611c7b565b5050505050565b600080336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611de957604051633006171960e21b815260040160405180910390fd5b600454610100900460ff1615611e2b576040517f1309a56300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611e376000612675565b90506000611e4482612c8b565b8635935090506000611e54612e61565b9050600080611e648684836130ce565b915091507f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb780914042760018483604051611e9c93929190614558565b60405180910390a1611eae8583612e1f565b9150611eca82611ec460408c0160208d016140da565b8a6134df565b96508860a00135871015611f17576040517f2746152a00000000000000000000000000000000000000000000000000000000815260a08a0135600482015260248101889052604401610ba1565b602085015115611f41576000611f2e8660006130e9565b9050611f3f6008868360008a61310c565b505b5050505050935093915050565b600080600a60009054906101000a90046001600160a01b03166001600160a01b031663bf1eb64a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fa4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc891906144d9565b905080600003611fdb5750600092915050565b600b546040517f7349615f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690637349615f9061204b907f000000000000000000000000000000000000000000000000000000000000000090859088906001906004016146f7565b602060405180830381865afa158015612068573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208c91906144d9565b9392505050565b6120dc6040518060e001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001606081525090565b606085600003612118576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612122612e61565b9050600061213188838361287b565b9050600061213f6000612675565b905061215461214e8284612e1f565b89613600565b8986526001600160a01b038916602087015260408601889052606086018790526080860181905291506121898288600161287b565b60a0860152604080516001808252818301909252906020808301908036833701905050935082846000815181106121c2576121c26144aa565b60200260200101818152505050505094509492505050565b6002546001600160a01b0316331461220557604051633006171960e21b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d55491a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600280549091169055565b60008061228183612675565b90508060200151816000015111156122db5760208101518151908103906000906122b690670de0b6b3a7640000846001613337565b90506fffffffffffffffffffffffffffffffff8110156122d857949350505050565b50505b506fffffffffffffffffffffffffffffffff92915050565b600080546001600160a01b0384811691161480610b065750506001600160a01b039190911660009081526001602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000009094168352929052205460ff1690565b60006123666000612675565b9050600061237382612c8b565b600a549091506000906001600160a01b031663e9e11f108635602088013561239e60408a018a614735565b8a606001356040518663ffffffff1660e01b81526004016123c39594939291906147a1565b6020604051808303816000875af11580156123e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061240691906144d9565b90506000612437848461241f60a08a0160808b01614806565b61242f60c08b0160a08c01614806565b60008a613649565b60408051848152602089810135908201526fffffffffffffffffffffffffffffffff868116828401528316606082015290519192507f43ae12d1ef33f7118bafdb1d8477fa6b6dbb0b21df5741fe86f4baa7a8bd13f9919081900360800190a1505050505050565b60006124ab6000612675565b905060006124b882612c8b565b600a5490915060009081906001600160a01b031663eedc0713873560208901356124e560408b018b614735565b8b606001356040518663ffffffff1660e01b815260040161250a9594939291906147a1565b60408051808303816000875af1158015612528573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061254c9190614821565b9092509050853582146125c257846125c2576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016600482015286356024820152604401610ba1565b60006125f185856125d960a08b0160808c01614806565b6125e960c08c0160a08d01614806565b60018b613649565b90507f43ae12d1ef33f7118bafdb1d8477fa6b6dbb0b21df5741fe86f4baa7a8bd13f96126216020890135614845565b61262b848661487d565b61263490614845565b6040805192835260208301919091526fffffffffffffffffffffffffffffffff8781168383015284166060830152519081900360800190a150505050505050565b6126a06040518060800160405280600081526020016000815260200160008152602001600081525090565b6126a8610ecb565b81526126b382611f4e565b8160200181815250507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561271a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273e91906144d9565b6040820152919050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b03161461278a576000610b06565b50919050565b600082604001516000036127b7576127b06127a961376f565b8390613903565b9050610b09565b60006127c284612dfb565b9050806000036127fe576040517fefbc415500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040840151612811908490836000613337565b949350505050565b600c54600754600091610e169183916001600160a01b0316907f00000000000000000000000000000000000000000000000000000000000000009067ffffffffffffffff80821691700100000000000000000000000000000000900416613917565b600061271083810390841061289157600061289f565b61289f858261271086613337565b95945050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361298757600a54604080517f4eb75f40000000000000000000000000000000000000000000000000000000008152815160009384936001600160a01b0390911692634eb75f4092600480830193928290030181865afa158015612946573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296a9190614821565b915091508160001461297c5780612811565b600019949350505050565b919050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316632ab4d0526040518163ffffffff1660e01b8152600401602060405180830381865afa1580156129ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a1191906144d9565b90506000198103612a26575060001992915050565b600083604001518211612a3a576000612a42565b836040015182035b9050612a58612a4f612819565b8290600061347c565b90506128118482612e1f565b60008160200151600003612a7b5750600019919050565b6008546020830151600091612ac0919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16670de0b6b3a764000084613337565b8351909150811115612ad6579151909103919050565b50600092915050565b806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff161115612b59576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610ba1565b6fffffffffffffffffffffffffffffffff908116700100000000000000000000000000000000029116179055565b600a5481546040517f3d33809d0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526001600160a01b0390911690633d33809d90602401602060405180830381865afa158015612bfd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c219190614890565b61094a5780546040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff80831660048301527001000000000000000000000000000000009092049091166024820152604401610ba1565b60008160200151600014612cda5760208201518251600091612cb89190670de0b6b3a76400009084613337565b90506fffffffffffffffffffffffffffffffff811015612cd85792915050565b505b506fffffffffffffffffffffffffffffffff919050565b60006001600160a01b038416612d36576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610ba1565b506040517f950212800000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528281166024830152849190821690639502128090604401602060405180830381865afa158015612da1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc59190614890565b61208c576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008160200151826000015111612e13576000610b09565b50602081015190510390565b60008260400151600014612e4d57612e48612e3984612dfb565b60408501518491906000613337565b610b06565b610b06612e5861376f565b83906000613aed565b600c54600754600091610e16916001916001600160a01b0316907f00000000000000000000000000000000000000000000000000000000000000009067ffffffffffffffff680100000000000000008204811691700100000000000000000000000000000000900416613917565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905261174b908490613b59565b60055460009060ff1615612f6557506001919050565b816001600160a01b03163b600003612f7f57506001919050565b506001600160a01b031660009081526006602052604090205460ff1690565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316036130915750600a5481906001600160a01b0390811690613018907f0000000000000000000000000000000000000000000000000000000000000000168284612ecf565b6040517f35403023000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b03821690633540302390602401600060405180830381600087803b15801561307357600080fd5b505af1158015613087573d6000803e3d6000fd5b5050505050610b09565b6040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610ba1565b6000806130dc85858561287b565b9594869003949350505050565b60006130f3610ecb565b83526130fe82611f4e565b6020840152610b0683612c8b565b6000826001811115613120576131206144f2565b0361322357836fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff1611156131a7576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8086166004830181905290851660248301526044820152606401610ba1565b84546fffffffffffffffffffffffffffffffff90811690841681111561321d576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8087166004830152808616602483015282166044820152606401610ba1565b50611d96565b836fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff1610156132a5576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8086166004830181905290851660248301526044820152606401610ba1565b84546fffffffffffffffffffffffffffffffff700100000000000000000000000000000000909104811690841681101561332f576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8087166004830152808616602483015282166044820152606401610ba1565b505050505050565b6000613344858585613c5b565b9050600182600181111561335a5761335a6144f2565b0361281157828061336d5761336d6148ad565b848609156128115760001981101561338757600101612811565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610ba1565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b031603610b0957600a60009054906101000a90046001600160a01b03166001600160a01b031663e4e889546040518163ffffffff1660e01b8152600401602060405180830381865afa158015613458573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0691906144d9565b60008260000361348d57508261208c565b61271083106134c8576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008361271003905061289f856127108386613337565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b0316036130915750600a546040517ef714ce000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b038381166024830152859260009291169062f714ce906044016020604051808303816000875af1158015613589573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ad91906144d9565b90508481146135fa576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260248101869052604401610ba1565b5061208c565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031614613642576000610b06565b5090919050565b60006136568760006130e9565b9050846fffffffffffffffffffffffffffffffff80821690831610156136cc576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8089166004830152808416602483015282166044820152606401610ba1565b849050806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff161115613751576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8089166004830152808416602483015282166044820152606401610ba1565b50816137655761376560098783868b61310c565b9695505050505050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f491906148dc565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613856573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061387a91906148dc565b90508060ff168260ff1611156138e7576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166004820152602401610ba1565b6138f182826148ff565b6138fc90600a6149fc565b9250505090565b60008160011461364257612e488284614a0b565b6000806000806000886001600160a01b03166369994511600060018060016040518563ffffffff1660e01b81526004016139549493929190614a22565b608060405180830381865afa158015613971573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139959190614a4a565b93509350935093506000886001600160a01b0316836001600160a01b0316036139c057506001613a16565b886001600160a01b0316826001600160a01b031614613a16576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b038a166004820152602401610ba1565b600080808d6001811115613a2c57613a2c6144f2565b03613a5e578215613a4b5785871015613a46575050848403845b613a86565b85871115613a4657505083850385613a86565b8215613a775785871115613a4657505083850384613a86565b85871015613a86575050848403855b81600003613aa7578967ffffffffffffffff1697505050505050505061289f565b6000613ab6838b846001613337565b9050808b67ffffffffffffffff1611613acf5780613adb565b8a67ffffffffffffffff165b9e9d5050505050505050505050505050565b600082600103613afe57508261208c565b6000826001811115613b1257613b126144f2565b03613b2e57828481613b2657613b266148ad565b04905061208c565b8315613b4f57826001850381613b4657613b466148ad565b04600101612811565b6000949350505050565b6000613bae826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613d489092919063ffffffff16565b9050805160001480613bcf575080806020019051810190613bcf9190614890565b61174b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610ba1565b6000808060001985870985870292508281108382030391505080600003613c9557838281613c8b57613c8b6148ad565b049250505061208c565b838110613cdf576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610ba1565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6060612811848460008585600080866001600160a01b03168587604051613d6f9190614a95565b60006040518083038185875af1925050503d8060008114613dac576040519150601f19603f3d011682016040523d82523d6000602084013e613db1565b606091505b5091509150613dc287838387613dcd565b979650505050505050565b60608315613e56578251600003613e4f576001600160a01b0385163b613e4f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610ba1565b5081612811565b6128118383815115613e6b5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ba19190614ab1565b600060c0828403121561278a57600080fd5b600060208284031215613ec357600080fd5b813567ffffffffffffffff811115613eda57600080fd5b61281184828501613e9f565b6001600160a01b038116811461094a57600080fd5b60008060008060808587031215613f1157600080fd5b843593506020850135613f2381613ee6565b93969395505050506040820135916060013590565b60005b83811015613f53578181015183820152602001613f3b565b50506000910152565b60008151808452613f74816020860160208601613f38565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600081518084526020808501945080840160005b83811015613fd657815187529582019590820190600101613fba565b509495945050505050565b604081526001600160a01b0383511660408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015260a083015160e0820152600060c084015160e0610100840152614044610120840182613f5c565b9050828103602084015261289f8185613fa6565b80356002811061298757600080fd5b6000806040838503121561407a57600080fd5b8235915061408a60208401614058565b90509250929050565b801515811461094a57600080fd5b600080604083850312156140b457600080fd5b82356140bf81613ee6565b915060208301356140cf81614093565b809150509250929050565b6000602082840312156140ec57600080fd5b813561208c81613ee6565b80356fffffffffffffffffffffffffffffffff8116811461298757600080fd5b6000806040838503121561412a57600080fd5b614133836140f7565b915061408a602084016140f7565b60006020828403121561415357600080fd5b813561208c81614093565b6000806040838503121561417157600080fd5b823561417c81613ee6565b915060208301356140cf81613ee6565b803561ffff8116811461298757600080fd5b6000806000606084860312156141b357600080fd5b6141bc8461418c565b92506141ca6020850161418c565b9150604084013562ffffff811681146141e257600080fd5b809150509250925092565b6000602082840312156141ff57600080fd5b610b0682614058565b6020808252825182820181905260009190848201906040850190845b818110156142495783516001600160a01b031683529284019291840191600101614224565b50909695505050505050565b60008060006060848603121561426a57600080fd5b833561427581613ee6565b9250602084013561428581613ee6565b929592945050506040919091013590565b600060e0828403121561278a57600080fd5b600080604083850312156142bb57600080fd5b82356142c681613ee6565b9150602083013567ffffffffffffffff8111156142e257600080fd5b6142ee85828601614296565b9150509250929050565b60006040828403121561278a57600080fd5b60008060006040848603121561431f57600080fd5b833561432a81613ee6565b9250602084013567ffffffffffffffff8082111561434757600080fd5b818601915086601f83011261435b57600080fd5b81358181111561436a57600080fd5b8760208260061b850101111561437f57600080fd5b6020830194508093505050509250925092565b6000806000606084860312156143a757600080fd5b83356143b281613ee6565b9250602084013567ffffffffffffffff8111156143ce57600080fd5b6143da86828701614296565b92505060408401356141e281613ee6565b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461298757600080fd5b6000806040838503121561442e57600080fd5b823561443981613ee6565b915061408a602084016143eb565b60408152825160408201526001600160a01b03602084015116606082015260408301516080820152606083015160a0820152608083015160c082015260a083015160e0820152600060c084015160e0610100840152614044610120840182613f5c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156144eb57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061094a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6060810161456585614521565b938152602081019290925260409091015290565b60408101823561458881614093565b15158252602083013561459a81614093565b80151560208401525092915050565b81356145b481614093565b81547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00811691151560ff16918217835560208401356145f281614093565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009190911690911790151560081b61ff001617905550565b60006040828403121561463c57600080fd5b6040516040810181811067ffffffffffffffff82111715614686577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052614692836143eb565b815260208301356146a281614093565b60208201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600060001982036146f0576146f06146ae565b5060010190565b6001600160a01b0385168152602081018490526080810161471784614521565b83604083015261472683614521565b82606083015295945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261476a57600080fd5b83018035915067ffffffffffffffff82111561478557600080fd5b60200191503681900382131561479a57600080fd5b9250929050565b85815284602082015260806040820152826080820152828460a0830137600060a08483010152600060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011683010190508260608301529695505050505050565b60006020828403121561481857600080fd5b610b06826140f7565b6000806040838503121561483457600080fd5b505080516020909101519092909150565b60007f80000000000000000000000000000000000000000000000000000000000000008203614876576148766146ae565b5060000390565b80820180821115610b0957610b096146ae565b6000602082840312156148a257600080fd5b815161208c81614093565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000602082840312156148ee57600080fd5b815160ff8116811461208c57600080fd5b60ff8281168282160390811115610b0957610b096146ae565b600181815b80851115614953578160001904821115614939576149396146ae565b8085161561494657918102915b93841c939080029061491d565b509250929050565b60008261496a57506001610b09565b8161497757506000610b09565b816001811461498d5760028114614997576149b3565b6001915050610b09565b60ff8411156149a8576149a86146ae565b50506001821b610b09565b5060208310610133831016604e8410600b84101617156149d6575081810a610b09565b6149e08383614918565b80600019048211156149f4576149f46146ae565b029392505050565b6000610b0660ff84168361495b565b8082028115828204841417610b0957610b096146ae565b60808101614a2f86614521565b858252614a3b85614521565b84602083015261471784614521565b60008060008060808587031215614a6057600080fd5b84519350602085015192506040850151614a7981613ee6565b6060860151909250614a8a81613ee6565b939692955090935050565b60008251614aa7818460208701613f38565b9190910192915050565b602081526000610b066020830184613f5c56fea26469706673582212206945234a2556d274a92d757df9dc105b7543fe86fa530a81d92fd10ed45e93ef64736f6c63430008130033000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d80000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c000000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a000000000000000000000000e6b1f872b1073408a1e619319718e0bce3e48f17
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106103625760003560e01c80638a83c9cd116101c8578063baaaa7b111610104578063e04610ed116100a2578063eea2f45c1161007c578063eea2f45c146108a8578063f4325d67146107d7578063f69959a3146108bb578063f8d89898146108ce57600080fd5b8063e04610ed1461085c578063e664b0201461087f578063ebbc4965146108a057600080fd5b8063c677e275116100de578063c677e275146107fd578063d4da79b314610810578063da5e0f0e1461081b578063daeccc791461082e57600080fd5b8063baaaa7b1146107b1578063bfccf0ec146107c4578063c55dae63146107d757600080fd5b8063a7229fd911610171578063b07c63c71161014b578063b07c63c714610751578063b1f8100d14610764578063b5a2d9a914610777578063ba2c46491461078a57600080fd5b8063a7229fd914610701578063a8e93cdb14610714578063aaf5eb681461074257600080fd5b806395ea64a5116101a257806395ea64a5146106c15780639a6b27cf146106d4578063a515b8ec146106e457600080fd5b80638a83c9cd1461063d5780638da5cb5b1461069b57806395c5b3b6146106ae57600080fd5b80634edd74e8116102a25780636026220d116102405780636a1eb7b81161021a5780636a1eb7b81461063d5780637180c8ca1461065257806372e0c0f41461066557806380f51c121461067857600080fd5b80636026220d14610604578063643b1e501461061757806365f2ba2f1461062a57600080fd5b806353740e041161027c57806353740e041461057f578063572a9302146105925780635aa89173146105a55780635fbbc0d2146105b857600080fd5b80634edd74e8146105575780634ee643a51461055f57806352e648f01461056c57600080fd5b806329aa41361161030f57806346fbf68e116102e957806346fbf68e146104ab5780634c9da063146104e75780634cdf587a1461051b5780634d7d9c011461054457600080fd5b806329aa413614610472578063415a127114610485578063431072f71461049857600080fd5b806319000c421161034057806319000c42146103f15780631a0377d11461043057806324b821ab1461045157600080fd5b806302cb4ed1146103675780630bd7260d1461037c57806313da2d4a1461038f575b600080fd5b61037a610375366004613eb1565b6108f4565b005b61037a61038a366004613eb1565b61094d565b6008546103c3906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b604080516fffffffffffffffffffffffffffffffff9384168152929091166020830152015b60405180910390f35b6104187f00000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a81565b6040516001600160a01b0390911681526020016103e8565b61044361043e366004613efb565b6109a3565b6040516103e8929190613fe1565b61046461045f366004614067565b610af2565b6040519081526020016103e8565b61037a6104803660046140a1565b610b0f565b6104646104933660046140da565b610c77565b61037a6104a6366004614117565b610ce2565b6104d76104b93660046140da565b6001600160a01b031660009081526003602052604090205460ff1690565b60405190151581526020016103e8565b6009546103c3906fffffffffffffffffffffffffffffffff8082169170010000000000000000000000000000000090041682565b610523610e02565b6040516fffffffffffffffffffffffffffffffff90911681526020016103e8565b61037a610552366004614141565b610e1b565b610464610ecb565b6005546104d79060ff1681565b600c54610418906001600160a01b031681565b61037a61058d366004613eb1565b610f52565b61037a6105a036600461415e565b610fa8565b61037a6105b336600461419e565b611129565b6007546040805167ffffffffffffffff808416825268010000000000000000840481166020830152700100000000000000000000000000000000909304909216908201526060016103e8565b6104646106123660046141ed565b6112c1565b610464610625366004614067565b6112d4565b61037a610638366004613eb1565b6112e8565b61064561133e565b6040516103e89190614208565b61037a6106603660046140a1565b6113b7565b600b54610418906001600160a01b031681565b6104d76106863660046140da565b60036020526000908152604090205460ff1681565b600054610418906001600160a01b031681565b61037a6106bc366004614117565b611478565b61037a6106cf3660046140da565b611594565b600454610100900460ff166104d7565b6106ec611684565b604080519283526020830191909152016103e8565b61037a61070f366004614255565b61169f565b6107276107223660046141ed565b611750565b604080519384526020840192909252908201526060016103e8565b610464670de0b6b3a764000081565b61046461075f3660046142a8565b611795565b61037a6107723660046140da565b61199d565b6104646107853660046140da565b611aac565b6104187f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c081565b61037a6107bf3660046142f8565b611b5a565b61037a6107d236600461430a565b611bce565b7f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c0610418565b6106ec61080b366004614392565b611d9d565b60045460ff166104d7565b6104646108293660046141ed565b611f4e565b6104d761083c36600461441b565b600160209081526000928352604080842090915290825290205460ff1681565b6104d761086a3660046140da565b60066020526000908152604090205460ff1681565b61089261088d366004613efb565b612093565b6040516103e8929190614447565b61037a6121da565b6105236108b63660046141ed565b612275565b600a54610418906001600160a01b031681565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2610418565b610922336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61093f57604051633006171960e21b815260040160405180910390fd5b61094a81600061235a565b50565b61097b336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61099857604051633006171960e21b815260040160405180910390fd5b61094a81600061249f565b6109ec6040518060e0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000815260200160008152602001606081525090565b606085600003610a28576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000610a346000612675565b90506000610a428789612748565b90506000610a508383612790565b90506000610a5c612819565b9050610a6a8282600061287b565b6001600160a01b038a168752602087018b90526040870189905260608701889052608087018190529150610aa08289600161287b565b60a087015260408051600180825281830190925290602080830190803683370190505094508085600081518110610ad957610ad96144aa565b6020026020010181815250505050505094509492505050565b6000610b06610b0083612675565b84612790565b90505b92915050565b610b3d336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610b5a57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038216610baa576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b03831660048201526024015b60405180910390fd5b816001600160a01b03163b600003610bf9576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610ba1565b6001600160a01b03821660008181526006602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527fee397872136f42e5319f2ebe127140e4741bee3ff02b86b7410a0b02778216de91015b60405180910390a25050565b600080610c846000612675565b9050610c8f836128a8565b91506000610c9c8261298c565b90506000610ca983612a64565b905081811015610cb7578091505b600019821015610cce57610ccb8583612748565b91505b83821015610cda578193505b505050919050565b610d10336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610d2d57604051633006171960e21b815260040160405180910390fd5b670de0b6b3a7640000826fffffffffffffffffffffffffffffffff1611610d9c576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610ba1565b604080516fffffffffffffffffffffffffffffffff8085168252831660208201527f792e77bcc33e678fd2a12f7a1ae644dcef1311589d15e9e8abd47adf5aca5e18910160405180910390a1610df460088383612adf565b610dfe6008612b87565b5050565b6000610e16610e116000612675565b612c8b565b905090565b610e49336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610e6657604051633006171960e21b815260040160405180910390fd5b600580547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168215159081179091556040519081527faf941e5e6c2b1ae06b5434c080d9f9ba2b0c2ac8e125a6010a22b57201f26a349060200160405180910390a150565b600a54604080517fe4e8895400000000000000000000000000000000000000000000000000000000815290516000926001600160a01b03169163e4e889549160048083019260209291908290030181865afa158015610f2e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1691906144d9565b610f80336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610f9d57604051633006171960e21b815260040160405180910390fd5b61094a81600161235a565b610fd6336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b610ff357604051633006171960e21b815260040160405180910390fd5b61103e827f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c07f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2612cf1565b600b80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03929092169190911790556110c1817f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c07f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2612cf1565b600c80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03928316179055604051828216918416907f4073b32a832187a54e475fc8fff20266eedffb63596adf4c0b8a2966e2b0025d90600090a35050565b611157336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61117457604051633006171960e21b815260040160405180910390fd5b6127108361ffff1611156111b4576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6127108261ffff1611156111f4576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161ffff85811682528416602082015262ffffff83168183015290517f91829fbe10211c7b1218c9e5b102bdabb13e9526b494b9741133a39e973f9f549181900360600190a16007805461ffff9485167fffffffffffffffffffffffffffffffff0000000000000000000000000000000090911617929093166801000000000000000002919091177fffffffffffffffff0000000000000000ffffffffffffffffffffffffffffffff1662ffffff9190911670010000000000000000000000000000000002179055565b6000610b096112cf83612675565b612dfb565b6000610b066112e283612675565b84612e1f565b611316336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61133357604051633006171960e21b815260040160405180910390fd5b61094a81600161249f565b604080516001808252818301909252606091602080830190803683370190505090507f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c081600081518110611394576113946144aa565b60200260200101906001600160a01b031690816001600160a01b03168152505090565b6113e5336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b61140257604051633006171960e21b815260040160405180910390fd5b6001600160a01b03821660008181526003602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527fa11b5803b8a35081b8f993e0dee5bc30301a3d83f644e5ab2ff39f972f0a807f9101610c6b565b6114a6336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b6114c357604051633006171960e21b815260040160405180910390fd5b670de0b6b3a7640000826fffffffffffffffffffffffffffffffff1611611532576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610ba1565b604080516fffffffffffffffffffffffffffffffff8085168252831660208201527f23b4d5cfa83d92da7d76ae644647a3e53678133ad0fa7d2b526ad571af12d1ec910160405180910390a161158a60098383612adf565b610dfe6009612b87565b6115c2336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b6115df57604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611622576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610ba1565b600a80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040517ffd6cdd9cefbd87614b7f366cb2bd5f7ba60b9a4b9febfbaf6344ecd1e03d018390600090a250565b60008061168f612819565b9150611699612e61565b90509091565b6116cd336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b6116ea57604051633006171960e21b815260040160405180910390fd5b826001600160a01b0316826001600160a01b03167f879f92dded0f26b83c3e00b12e0395dc72cfc3077343d1854ed6988edd1f90968360405161172f91815260200190565b60405180910390a361174b6001600160a01b0384168383612ecf565b505050565b60008060008061175f85612675565b90508060000151816020015161177483612c8b565b91955093506fffffffffffffffffffffffffffffffff169150509193909250565b6000336001600160a01b037f00000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a16146117e057604051633006171960e21b815260040160405180910390fd5b60045460ff161561181d576040517f1309a56300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61182683612f4f565b61184357604051633006171960e21b815260040160405180910390fd5b600061184f6000612675565b9050600061185c82612c8b565b9050600061187a61187060208701876140da565b8660200135612f9e565b90506118868382612790565b9350600080611893612819565b90506118a1868260006130ce565b60405191975092507f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb7809140427906118db9060009084908690614558565b60405180910390a18560000361191d576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8660a00135861015611968576040517f2746152a00000000000000000000000000000000000000000000000000000000815260a0880135600482015260248101879052604401610ba1565b60208501511561199257600061197f8660006130e9565b90506119906008868360018a61310c565b505b505050505092915050565b6119cb336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b6119e857604051633006171960e21b815260040160405180910390fd5b6001600160a01b038116611a33576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610ba1565b600254600080546040516001600160a01b03808616948116939216917f64420d4a41c6ed4de2bccbf33192eea18e576c5b23c79c3a722d4e9534c2e8d891a4600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b600080611ab96000612675565b6008546020820151919250600091611aed916fffffffffffffffffffffffffffffffff16670de0b6b3a76400006001613337565b90508082600001511115611b535781518190036000611b0c86856133ca565b905081811015611b1a578091505b611b248483612790565b9450611b3a611b31612e61565b8690600061347c565b94508360400151851115611b5057836040015194505b50505b5050919050565b3360009081526003602052604090205460ff16611b8a57604051633006171960e21b815260040160405180910390fd5b7f803ee193075547dae36361498f3de5e399cdb29b7e0c7b680533f3da8b733a1781604051611bb99190614579565b60405180910390a180600461174b82826145a9565b611bfc336000357fffffffff00000000000000000000000000000000000000000000000000000000166122f3565b611c1957604051633006171960e21b815260040160405180910390fd5b6001600160a01b038316611c64576040517f8e4c8aa60000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610ba1565b604080518082019091526000808252602082015260005b82811015611d9657838382818110611c9557611c956144aa565b905060400201803603810190611cab919061462a565b91508160200151151582600001517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916866001600160a01b03167ff5736e75de2c751f775d4c5ed517289f77074f8c337f451ba4c0c3ed1dd7f9ad60405160405180910390a46020828101516001600160a01b038716600090815260018352604080822086517fffffffff000000000000000000000000000000000000000000000000000000001683529093529190912080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016911515919091179055611d8f816146dd565b9050611c7b565b5050505050565b600080336001600160a01b037f00000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a1614611de957604051633006171960e21b815260040160405180910390fd5b600454610100900460ff1615611e2b576040517f1309a56300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000611e376000612675565b90506000611e4482612c8b565b8635935090506000611e54612e61565b9050600080611e648684836130ce565b915091507f7e81c1439e2f6851efe3288a5d0ae235c1729a6272f98ed0a4b5eb780914042760018483604051611e9c93929190614558565b60405180910390a1611eae8583612e1f565b9150611eca82611ec460408c0160208d016140da565b8a6134df565b96508860a00135871015611f17576040517f2746152a00000000000000000000000000000000000000000000000000000000815260a08a0135600482015260248101889052604401610ba1565b602085015115611f41576000611f2e8660006130e9565b9050611f3f6008868360008a61310c565b505b5050505050935093915050565b600080600a60009054906101000a90046001600160a01b03166001600160a01b031663bf1eb64a6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611fa4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611fc891906144d9565b905080600003611fdb5750600092915050565b600b546040517f7349615f0000000000000000000000000000000000000000000000000000000081526001600160a01b0390911690637349615f9061204b907f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc290859088906001906004016146f7565b602060405180830381865afa158015612068573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061208c91906144d9565b9392505050565b6120dc6040518060e001604052806000815260200160006001600160a01b0316815260200160008152602001600081526020016000815260200160008152602001606081525090565b606085600003612118576040517f54db0c8c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612122612e61565b9050600061213188838361287b565b9050600061213f6000612675565b905061215461214e8284612e1f565b89613600565b8986526001600160a01b038916602087015260408601889052606086018790526080860181905291506121898288600161287b565b60a0860152604080516001808252818301909252906020808301908036833701905050935082846000815181106121c2576121c26144aa565b60200260200101818152505050505094509492505050565b6002546001600160a01b0316331461220557604051633006171960e21b815260040160405180910390fd5b6000805460405133926001600160a01b03909216917f5cd6b24c0149d980c82592262b3a81294b39f8f6e3c004126aaf0828c787d55491a3600080547fffffffffffffffffffffffff00000000000000000000000000000000000000009081163317909155600280549091169055565b60008061228183612675565b90508060200151816000015111156122db5760208101518151908103906000906122b690670de0b6b3a7640000846001613337565b90506fffffffffffffffffffffffffffffffff8110156122d857949350505050565b50505b506fffffffffffffffffffffffffffffffff92915050565b600080546001600160a01b0384811691161480610b065750506001600160a01b039190911660009081526001602090815260408083207fffffffff000000000000000000000000000000000000000000000000000000009094168352929052205460ff1690565b60006123666000612675565b9050600061237382612c8b565b600a549091506000906001600160a01b031663e9e11f108635602088013561239e60408a018a614735565b8a606001356040518663ffffffff1660e01b81526004016123c39594939291906147a1565b6020604051808303816000875af11580156123e2573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061240691906144d9565b90506000612437848461241f60a08a0160808b01614806565b61242f60c08b0160a08c01614806565b60008a613649565b60408051848152602089810135908201526fffffffffffffffffffffffffffffffff868116828401528316606082015290519192507f43ae12d1ef33f7118bafdb1d8477fa6b6dbb0b21df5741fe86f4baa7a8bd13f9919081900360800190a1505050505050565b60006124ab6000612675565b905060006124b882612c8b565b600a5490915060009081906001600160a01b031663eedc0713873560208901356124e560408b018b614735565b8b606001356040518663ffffffff1660e01b815260040161250a9594939291906147a1565b60408051808303816000875af1158015612528573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061254c9190614821565b9092509050853582146125c257846125c2576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216600482015286356024820152604401610ba1565b60006125f185856125d960a08b0160808c01614806565b6125e960c08c0160a08d01614806565b60018b613649565b90507f43ae12d1ef33f7118bafdb1d8477fa6b6dbb0b21df5741fe86f4baa7a8bd13f96126216020890135614845565b61262b848661487d565b61263490614845565b6040805192835260208301919091526fffffffffffffffffffffffffffffffff8781168383015284166060830152519081900360800190a150505050505050565b6126a06040518060800160405280600081526020016000815260200160008152602001600081525090565b6126a8610ecb565b81526126b382611f4e565b8160200181815250507f00000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a6001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561271a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061273e91906144d9565b6040820152919050565b60007f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c06001600160a01b0316836001600160a01b03161461278a576000610b06565b50919050565b600082604001516000036127b7576127b06127a961376f565b8390613903565b9050610b09565b60006127c284612dfb565b9050806000036127fe576040517fefbc415500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040840151612811908490836000613337565b949350505050565b600c54600754600091610e169183916001600160a01b0316907f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c09067ffffffffffffffff80821691700100000000000000000000000000000000900416613917565b600061271083810390841061289157600061289f565b61289f858261271086613337565b95945050505050565b60007f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c06001600160a01b0316826001600160a01b03160361298757600a54604080517f4eb75f40000000000000000000000000000000000000000000000000000000008152815160009384936001600160a01b0390911692634eb75f4092600480830193928290030181865afa158015612946573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296a9190614821565b915091508160001461297c5780612811565b600019949350505050565b919050565b6000807f00000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a6001600160a01b0316632ab4d0526040518163ffffffff1660e01b8152600401602060405180830381865afa1580156129ed573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a1191906144d9565b90506000198103612a26575060001992915050565b600083604001518211612a3a576000612a42565b836040015182035b9050612a58612a4f612819565b8290600061347c565b90506128118482612e1f565b60008160200151600003612a7b5750600019919050565b6008546020830151600091612ac0919070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16670de0b6b3a764000084613337565b8351909150811115612ad6579151909103919050565b50600092915050565b806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff161115612b59576040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff808416600483015282166024820152604401610ba1565b6fffffffffffffffffffffffffffffffff908116700100000000000000000000000000000000029116179055565b600a5481546040517f3d33809d0000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff90911660048201526001600160a01b0390911690633d33809d90602401602060405180830381865afa158015612bfd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c219190614890565b61094a5780546040517f33693ae60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff80831660048301527001000000000000000000000000000000009092049091166024820152604401610ba1565b60008160200151600014612cda5760208201518251600091612cb89190670de0b6b3a76400009084613337565b90506fffffffffffffffffffffffffffffffff811015612cd85792915050565b505b506fffffffffffffffffffffffffffffffff919050565b60006001600160a01b038416612d36576040517f8e4c8aa600000000000000000000000000000000000000000000000000000000815260006004820152602401610ba1565b506040517f950212800000000000000000000000000000000000000000000000000000000081526001600160a01b0383811660048301528281166024830152849190821690639502128090604401602060405180830381865afa158015612da1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612dc59190614890565b61208c576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008160200151826000015111612e13576000610b09565b50602081015190510390565b60008260400151600014612e4d57612e48612e3984612dfb565b60408501518491906000613337565b610b06565b610b06612e5861376f565b83906000613aed565b600c54600754600091610e16916001916001600160a01b0316907f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c09067ffffffffffffffff680100000000000000008204811691700100000000000000000000000000000000900416613917565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb0000000000000000000000000000000000000000000000000000000017905261174b908490613b59565b60055460009060ff1615612f6557506001919050565b816001600160a01b03163b600003612f7f57506001919050565b506001600160a01b031660009081526006602052604090205460ff1690565b60007f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c06001600160a01b0316836001600160a01b0316036130915750600a5481906001600160a01b0390811690613018907f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c0168284612ecf565b6040517f35403023000000000000000000000000000000000000000000000000000000008152600481018490526001600160a01b03821690633540302390602401600060405180830381600087803b15801561307357600080fd5b505af1158015613087573d6000803e3d6000fd5b5050505050610b09565b6040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610ba1565b6000806130dc85858561287b565b9594869003949350505050565b60006130f3610ecb565b83526130fe82611f4e565b6020840152610b0683612c8b565b6000826001811115613120576131206144f2565b0361322357836fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff1611156131a7576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8086166004830181905290851660248301526044820152606401610ba1565b84546fffffffffffffffffffffffffffffffff90811690841681111561321d576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8087166004830152808616602483015282166044820152606401610ba1565b50611d96565b836fffffffffffffffffffffffffffffffff16836fffffffffffffffffffffffffffffffff1610156132a5576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8086166004830181905290851660248301526044820152606401610ba1565b84546fffffffffffffffffffffffffffffffff700100000000000000000000000000000000909104811690841681101561332f576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8087166004830152808616602483015282166044820152606401610ba1565b505050505050565b6000613344858585613c5b565b9050600182600181111561335a5761335a6144f2565b0361281157828061336d5761336d6148ad565b848609156128115760001981101561338757600101612811565b6040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018690526024810185905260448101849052606401610ba1565b60007f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c06001600160a01b0316836001600160a01b031603610b0957600a60009054906101000a90046001600160a01b03166001600160a01b031663e4e889546040518163ffffffff1660e01b8152600401602060405180830381865afa158015613458573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b0691906144d9565b60008260000361348d57508261208c565b61271083106134c8576040517fd252903400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008361271003905061289f856127108386613337565b60007f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c06001600160a01b0316836001600160a01b0316036130915750600a546040517ef714ce000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b038381166024830152859260009291169062f714ce906044016020604051808303816000875af1158015613589573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906135ad91906144d9565b90508481146135fa576040517fb2b3b53b0000000000000000000000000000000000000000000000000000000081526001600160a01b038516600482015260248101869052604401610ba1565b5061208c565b60007f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c06001600160a01b0316826001600160a01b031614613642576000610b06565b5090919050565b60006136568760006130e9565b9050846fffffffffffffffffffffffffffffffff80821690831610156136cc576040517f2fa926530000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8089166004830152808416602483015282166044820152606401610ba1565b849050806fffffffffffffffffffffffffffffffff16826fffffffffffffffffffffffffffffffff161115613751576040517fdeb029d60000000000000000000000000000000000000000000000000000000081526fffffffffffffffffffffffffffffffff8089166004830152808416602483015282166044820152606401610ba1565b50816137655761376560098783868b61310c565b9695505050505050565b6000807f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c06001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156137d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137f491906148dc565b905060007f00000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015613856573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061387a91906148dc565b90508060ff168260ff1611156138e7576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c0166004820152602401610ba1565b6138f182826148ff565b6138fc90600a6149fc565b9250505090565b60008160011461364257612e488284614a0b565b6000806000806000886001600160a01b03166369994511600060018060016040518563ffffffff1660e01b81526004016139549493929190614a22565b608060405180830381865afa158015613971573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139959190614a4a565b93509350935093506000886001600160a01b0316836001600160a01b0316036139c057506001613a16565b886001600160a01b0316826001600160a01b031614613a16576040517f961c9a4f0000000000000000000000000000000000000000000000000000000081526001600160a01b038a166004820152602401610ba1565b600080808d6001811115613a2c57613a2c6144f2565b03613a5e578215613a4b5785871015613a46575050848403845b613a86565b85871115613a4657505083850385613a86565b8215613a775785871115613a4657505083850384613a86565b85871015613a86575050848403855b81600003613aa7578967ffffffffffffffff1697505050505050505061289f565b6000613ab6838b846001613337565b9050808b67ffffffffffffffff1611613acf5780613adb565b8a67ffffffffffffffff165b9e9d5050505050505050505050505050565b600082600103613afe57508261208c565b6000826001811115613b1257613b126144f2565b03613b2e57828481613b2657613b266148ad565b04905061208c565b8315613b4f57826001850381613b4657613b466148ad565b04600101612811565b6000949350505050565b6000613bae826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613d489092919063ffffffff16565b9050805160001480613bcf575080806020019051810190613bcf9190614890565b61174b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610ba1565b6000808060001985870985870292508281108382030391505080600003613c9557838281613c8b57613c8b6148ad565b049250505061208c565b838110613cdf576040517f63a05778000000000000000000000000000000000000000000000000000000008152600481018790526024810186905260448101859052606401610ba1565b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6060612811848460008585600080866001600160a01b03168587604051613d6f9190614a95565b60006040518083038185875af1925050503d8060008114613dac576040519150601f19603f3d011682016040523d82523d6000602084013e613db1565b606091505b5091509150613dc287838387613dcd565b979650505050505050565b60608315613e56578251600003613e4f576001600160a01b0385163b613e4f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610ba1565b5081612811565b6128118383815115613e6b5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610ba19190614ab1565b600060c0828403121561278a57600080fd5b600060208284031215613ec357600080fd5b813567ffffffffffffffff811115613eda57600080fd5b61281184828501613e9f565b6001600160a01b038116811461094a57600080fd5b60008060008060808587031215613f1157600080fd5b843593506020850135613f2381613ee6565b93969395505050506040820135916060013590565b60005b83811015613f53578181015183820152602001613f3b565b50506000910152565b60008151808452613f74816020860160208601613f38565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b600081518084526020808501945080840160005b83811015613fd657815187529582019590820190600101613fba565b509495945050505050565b604081526001600160a01b0383511660408201526020830151606082015260408301516080820152606083015160a0820152608083015160c082015260a083015160e0820152600060c084015160e0610100840152614044610120840182613f5c565b9050828103602084015261289f8185613fa6565b80356002811061298757600080fd5b6000806040838503121561407a57600080fd5b8235915061408a60208401614058565b90509250929050565b801515811461094a57600080fd5b600080604083850312156140b457600080fd5b82356140bf81613ee6565b915060208301356140cf81614093565b809150509250929050565b6000602082840312156140ec57600080fd5b813561208c81613ee6565b80356fffffffffffffffffffffffffffffffff8116811461298757600080fd5b6000806040838503121561412a57600080fd5b614133836140f7565b915061408a602084016140f7565b60006020828403121561415357600080fd5b813561208c81614093565b6000806040838503121561417157600080fd5b823561417c81613ee6565b915060208301356140cf81613ee6565b803561ffff8116811461298757600080fd5b6000806000606084860312156141b357600080fd5b6141bc8461418c565b92506141ca6020850161418c565b9150604084013562ffffff811681146141e257600080fd5b809150509250925092565b6000602082840312156141ff57600080fd5b610b0682614058565b6020808252825182820181905260009190848201906040850190845b818110156142495783516001600160a01b031683529284019291840191600101614224565b50909695505050505050565b60008060006060848603121561426a57600080fd5b833561427581613ee6565b9250602084013561428581613ee6565b929592945050506040919091013590565b600060e0828403121561278a57600080fd5b600080604083850312156142bb57600080fd5b82356142c681613ee6565b9150602083013567ffffffffffffffff8111156142e257600080fd5b6142ee85828601614296565b9150509250929050565b60006040828403121561278a57600080fd5b60008060006040848603121561431f57600080fd5b833561432a81613ee6565b9250602084013567ffffffffffffffff8082111561434757600080fd5b818601915086601f83011261435b57600080fd5b81358181111561436a57600080fd5b8760208260061b850101111561437f57600080fd5b6020830194508093505050509250925092565b6000806000606084860312156143a757600080fd5b83356143b281613ee6565b9250602084013567ffffffffffffffff8111156143ce57600080fd5b6143da86828701614296565b92505060408401356141e281613ee6565b80357fffffffff000000000000000000000000000000000000000000000000000000008116811461298757600080fd5b6000806040838503121561442e57600080fd5b823561443981613ee6565b915061408a602084016143eb565b60408152825160408201526001600160a01b03602084015116606082015260408301516080820152606083015160a0820152608083015160c082015260a083015160e0820152600060c084015160e0610100840152614044610120840182613f5c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6000602082840312156144eb57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6002811061094a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6060810161456585614521565b938152602081019290925260409091015290565b60408101823561458881614093565b15158252602083013561459a81614093565b80151560208401525092915050565b81356145b481614093565b81547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00811691151560ff16918217835560208401356145f281614093565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009190911690911790151560081b61ff001617905550565b60006040828403121561463c57600080fd5b6040516040810181811067ffffffffffffffff82111715614686577f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604052614692836143eb565b815260208301356146a281614093565b60208201529392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600060001982036146f0576146f06146ae565b5060010190565b6001600160a01b0385168152602081018490526080810161471784614521565b83604083015261472683614521565b82606083015295945050505050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261476a57600080fd5b83018035915067ffffffffffffffff82111561478557600080fd5b60200191503681900382131561479a57600080fd5b9250929050565b85815284602082015260806040820152826080820152828460a0830137600060a08483010152600060a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f86011683010190508260608301529695505050505050565b60006020828403121561481857600080fd5b610b06826140f7565b6000806040838503121561483457600080fd5b505080516020909101519092909150565b60007f80000000000000000000000000000000000000000000000000000000000000008203614876576148766146ae565b5060000390565b80820180821115610b0957610b096146ae565b6000602082840312156148a257600080fd5b815161208c81614093565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6000602082840312156148ee57600080fd5b815160ff8116811461208c57600080fd5b60ff8281168282160390811115610b0957610b096146ae565b600181815b80851115614953578160001904821115614939576149396146ae565b8085161561494657918102915b93841c939080029061491d565b509250929050565b60008261496a57506001610b09565b8161497757506000610b09565b816001811461498d5760028114614997576149b3565b6001915050610b09565b60ff8411156149a8576149a86146ae565b50506001821b610b09565b5060208310610133831016604e8410600b84101617156149d6575081810a610b09565b6149e08383614918565b80600019048211156149f4576149f46146ae565b029392505050565b6000610b0660ff84168361495b565b8082028115828204841417610b0957610b096146ae565b60808101614a2f86614521565b858252614a3b85614521565b84602083015261471784614521565b60008060008060808587031215614a6057600080fd5b84519350602085015192506040850151614a7981613ee6565b6060860151909250614a8a81613ee6565b939692955090935050565b60008251614aa7818460208701613f38565b9190910192915050565b602081526000610b066020830184613f5c56fea26469706673582212206945234a2556d274a92d757df9dc105b7543fe86fa530a81d92fd10ed45e93ef64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d80000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c0000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c000000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a000000000000000000000000e6b1f872b1073408a1e619319718e0bce3e48f17
-----Decoded View---------------
Arg [0] : _initialOwner (address): 0xb20AaE0Fe007519b7cE6f090a2aB8353B3Da5d80
Arg [1] : _reserveToken_ (address): 0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0
Arg [2] : _debtToken_ (address): 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
Arg [3] : _dynamicFeeOracleBaseToken (address): 0xFAe103DC9cf190eD75350761e95403b7b8aFa6c0
Arg [4] : _lovToken (address): 0x71520ce2DB377AFa999bc6fdc1af896B21b2F26a
Arg [5] : _borrowLend (address): 0xe6B1F872b1073408a1E619319718E0Bce3E48F17
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 000000000000000000000000b20aae0fe007519b7ce6f090a2ab8353b3da5d80
Arg [1] : 000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c0
Arg [2] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [3] : 000000000000000000000000fae103dc9cf190ed75350761e95403b7b8afa6c0
Arg [4] : 00000000000000000000000071520ce2db377afa999bc6fdc1af896b21b2f26a
Arg [5] : 000000000000000000000000e6b1f872b1073408a1e619319718e0bce3e48f17
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.