Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 3 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x6101a060 | 24279473 | 63 days ago | Contract Creation | 0 ETH | |||
| 0x6101a060 | 24279473 | 63 days ago | Contract Creation | 0 ETH | |||
| 0x6101a060 | 24279469 | 63 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
MorphoBlueLenderBorrowerFactory
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;
import {MorphoBlueLenderBorrower} from "./MorphoBlueLenderBorrower.sol";
import {IStrategyInterface} from "./interfaces/IStrategyInterface.sol";
import {Id} from "./interfaces/morpho/IMorpho.sol";
contract MorphoBlueLenderBorrowerFactory {
event NewStrategy(address indexed strategy, Id indexed marketId);
address public immutable GOV;
address public immutable morpho;
address public immutable router;
address public management;
address public performanceFeeRecipient;
address public keeper;
address public emergencyAdmin;
/// @notice Track deployments by market id.
mapping(Id => address) public deployments;
constructor(
address _management,
address _performanceFeeRecipient,
address _keeper,
address _emergencyAdmin,
address _gov,
address _morpho,
address _router
) {
require(_gov != address(0));
management = _management;
performanceFeeRecipient = _performanceFeeRecipient;
keeper = _keeper;
emergencyAdmin = _emergencyAdmin;
GOV = _gov;
morpho = _morpho;
router = _router;
}
function newStrategy(
address _asset,
string calldata _name,
address _borrowToken,
address _lenderVault,
Id _marketId,
address _borrowUsdOracle
) external virtual returns (address) {
require(deployments[_marketId] == address(0));
IStrategyInterface _newStrategy = IStrategyInterface(
address(
new MorphoBlueLenderBorrower(
_asset,
_name,
_borrowToken,
_lenderVault,
GOV,
morpho,
_marketId,
_borrowUsdOracle,
router
)
)
);
_newStrategy.setPerformanceFeeRecipient(performanceFeeRecipient);
_newStrategy.setKeeper(keeper);
_newStrategy.setPendingManagement(management);
_newStrategy.setEmergencyAdmin(emergencyAdmin);
emit NewStrategy(address(_newStrategy), _marketId);
deployments[_marketId] = address(_newStrategy);
return address(_newStrategy);
}
function setAddresses(
address _management,
address _performanceFeeRecipient,
address _keeper,
address _emergencyAdmin
) external {
require(msg.sender == management, "!management");
management = _management;
performanceFeeRecipient = _performanceFeeRecipient;
keeper = _keeper;
emergencyAdmin = _emergencyAdmin;
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BaseLenderBorrower} from "./BaseLenderBorrower.sol";
import {IMorpho, Id, MarketParams, Position} from "./interfaces/morpho/IMorpho.sol";
import {IChainlinkAggregator} from "./interfaces/IChainlinkAggregator.sol";
import {IOracle} from "./interfaces/morpho/IOracle.sol";
import {MorphoBalancesLib, MorphoLib} from "./libraries/morpho/periphery/MorphoBalancesLib.sol";
import {SharesMathLib} from "./libraries/morpho/SharesMathLib.sol";
import {UniswapV3Swapper} from "@periphery/swappers/UniswapV3Swapper.sol";
contract MorphoBlueLenderBorrower is BaseLenderBorrower, UniswapV3Swapper {
using SafeERC20 for ERC20;
using MorphoBalancesLib for IMorpho;
using MorphoLib for IMorpho;
uint256 internal constant ORACLE_PRICE_SCALE = 1e36;
IMorpho public immutable morpho;
Id public immutable marketId;
MarketParams public marketParams;
address public immutable GOV;
uint256 internal immutable COLL_SCALE;
uint256 internal immutable BORROW_SCALE;
/// @notice USD price feed (1e8) for borrow token.
/// @dev Collateral price is derived using Morpho's oracle (collateral -> borrow) then this oracle (borrow -> USD).
address public borrowUsdOracle;
constructor(
address _asset,
string memory _name,
address _borrowToken,
address _lenderVault,
address _gov,
address _morpho,
Id _marketId,
address _borrowUsdOracle,
address _router
) BaseLenderBorrower(_asset, _name, _borrowToken, _lenderVault) {
GOV = _gov;
morpho = IMorpho(_morpho);
marketId = _marketId;
marketParams = morpho.idToMarketParams(_marketId);
require(
marketParams.loanToken == _borrowToken &&
marketParams.collateralToken == _asset
);
COLL_SCALE = 10 ** ERC20(_asset).decimals();
BORROW_SCALE = 10 ** ERC20(_borrowToken).decimals();
ERC20(_asset).forceApprove(_morpho, type(uint256).max);
ERC20(_borrowToken).forceApprove(_morpho, type(uint256).max);
_setMinAmountToSell(1e4);
router = _router;
require(IChainlinkAggregator(_borrowUsdOracle).decimals() == 8);
borrowUsdOracle = _borrowUsdOracle;
}
/*//////////////////////////////////////////////////////////////
WRITE FUNCTIONS
//////////////////////////////////////////////////////////////*/
function _supplyCollateral(uint256 amount) internal virtual override {
if (amount == 0) return;
morpho.supplyCollateral(marketParams, amount, address(this), "");
}
function _withdrawCollateral(uint256 amount) internal virtual override {
if (amount == 0) return;
morpho.withdrawCollateral(
marketParams,
amount,
address(this),
address(this)
);
}
function _borrow(uint256 amount) internal virtual override {
if (amount == 0) return;
morpho.borrow(marketParams, amount, 0, address(this), address(this));
}
// Need to give shares as input to avoid rounding errors on full repays.
function _repay(uint256 amount) internal virtual override {
if (amount == 0) return;
(
,
,
uint256 totalBorrowAssets,
uint256 totalBorrowShares
) = MorphoBalancesLib.expectedMarketBalances(morpho, marketParams);
uint256 shares = Math.min(
SharesMathLib.toSharesDown(
amount,
totalBorrowAssets,
totalBorrowShares
),
morpho.borrowShares(marketId, address(this))
);
morpho.repay(marketParams, 0, shares, address(this), "");
}
/*//////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
function _getPrice(
address _asset
) internal view virtual override returns (uint256 price) {
if (_asset == borrowToken) {
price = _readUsdOracle();
} else if (_asset == address(asset)) {
// Use Morpho's oracle to get collateral price in borrow token, then convert to USD
// Morpho oracle returns: (collateral amount) / (borrow token amount) scaled by 1e36 relative to decimals.
uint256 borrowUsd = _readUsdOracle();
uint256 ratio = IOracle(marketParams.oracle).price();
price = (ratio * borrowUsd * COLL_SCALE) / (BORROW_SCALE * ORACLE_PRICE_SCALE);
} else {
revert();
}
}
function _isSupplyPaused() internal view virtual override returns (bool) {
return false;
}
function _isBorrowPaused() internal view virtual override returns (bool) {
return false;
}
function _isLiquidatable() internal view virtual override returns (bool) {
Position memory p = morpho.position(marketId, address(this));
if (p.borrowShares == 0) return false;
uint256 collateralValue = (uint256(p.collateral) *
IOracle(marketParams.oracle).price()) / ORACLE_PRICE_SCALE;
uint256 maxBorrow = (collateralValue * marketParams.lltv) / WAD;
return balanceOfDebt() > maxBorrow;
}
function _maxCollateralDeposit()
internal
view
virtual
override
returns (uint256)
{
return type(uint256).max;
}
function _maxBorrowAmount()
internal
view
virtual
override
returns (uint256)
{
(uint256 totalSupplyAssets, , uint256 totalBorrowAssets, ) = morpho
.expectedMarketBalances(marketParams);
return
totalSupplyAssets > totalBorrowAssets
? totalSupplyAssets - totalBorrowAssets
: 0;
}
function getNetBorrowApr(
uint256 /* newAmount */
) public view virtual override returns (uint256) {
return 1;
}
function getNetRewardApr(
uint256 /* newAmount */
) public view virtual override returns (uint256) {
// Hardcoded high reward APR to keep borrowing favored over costs.
return 1e20;
}
function getLiquidateCollateralFactor()
public
view
virtual
override
returns (uint256)
{
return marketParams.lltv;
}
function balanceOfCollateral()
public
view
virtual
override
returns (uint256)
{
Position memory p = morpho.position(marketId, address(this));
return p.collateral;
}
function balanceOfDebt() public view virtual override returns (uint256) {
return morpho.expectedBorrowAssets(marketParams, address(this));
}
/*//////////////////////////////////////////////////////////////
HARVEST / SWAP LOGIC
//////////////////////////////////////////////////////////////*/
function _claimRewards() internal virtual override {}
function _claimAndSellRewards() internal virtual override {
uint256 have = balanceOfLentAssets() + balanceOfBorrowToken();
uint256 owe = balanceOfDebt();
if (have > owe) {
uint256 amountToSell = have - owe;
_withdrawFromLender(amountToSell);
_sellBorrowToken(Math.min(amountToSell, balanceOfBorrowToken()));
}
}
function _buyBorrowToken() internal virtual override {
uint256 _amount = borrowTokenOwedBalance();
uint256 maxAssetIn = (_fromUsd(
_toUsd(_amount, borrowToken),
address(asset)
) * (MAX_BPS + slippage)) / MAX_BPS;
if (maxAssetIn == 0) return;
_swapTo(address(asset), borrowToken, _amount, maxAssetIn);
}
function _sellBorrowToken(uint256 _amount) internal virtual override {
if (_amount == 0) return;
_swapFrom(
borrowToken,
address(asset),
_amount,
_getAmountOut(_amount, borrowToken, address(asset))
);
}
/*//////////////////////////////////////////////////////////////
MANAGEMENT UTILITIES
//////////////////////////////////////////////////////////////*/
function setUniFees(
address _token0,
address _token1,
uint24 _fee
) external onlyManagement {
_setUniFees(_token0, _token1, _fee);
}
function setUniBase(address _base) external onlyManagement {
base = _base;
}
function setMinAmountToSell(
uint256 _minAmountToSell
) external onlyManagement {
_setMinAmountToSell(_minAmountToSell);
}
function setBorrowUsdOracle(
address _borrowUsdOracle
) external onlyManagement {
require(IChainlinkAggregator(_borrowUsdOracle).decimals() == 8);
borrowUsdOracle = _borrowUsdOracle;
}
/// @notice Sweep of non-asset ERC20 tokens to governance
/// @param _token The ERC20 token to sweep
function sweep(address _token) external {
require(msg.sender == GOV, "!gov");
require(
_token != address(asset) &&
_token != address(borrowToken) &&
_token != address(lenderVault),
"!sweep"
);
ERC20(_token).safeTransfer(GOV, ERC20(_token).balanceOf(address(this)));
}
function _readUsdOracle() internal view returns (uint256) {
int256 answer = IChainlinkAggregator(borrowUsdOracle).latestAnswer();
require(answer > 0, "0");
return uint256(answer);
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.18;
import {IStrategy} from "@tokenized-strategy/interfaces/IStrategy.sol";
import {ILenderBorrower} from "./ILenderBorrower.sol";
import {IMorpho} from "./morpho/IMorpho.sol";
import {Id} from "./morpho/IMorpho.sol";
interface IStrategyInterface is IStrategy, ILenderBorrower {
//TODO: Add your specific implementation interface in here.
function GOV() external view returns (address);
function sweep(address _token) external;
// Morpho-specific views
function morpho() external view returns (IMorpho);
function marketId() external view returns (Id);
function marketParams()
external
view
returns (address, address, address, address, uint256);
function lenderVault() external view returns (address);
function base() external view returns (address);
function router() external view returns (address);
// Morpho-specific management setters
function setRewardAprOracle(address _oracle) external;
function setUsdOracles(
address _assetUsdOracle,
address _borrowUsdOracle
) external;
function setUniFees(address _token0, address _token1, uint24 _fee) external;
function setUniBase(address _base) external;
function setMinAmountToSell(uint256 _minAmountToSell) external;
function setAuction(address _auction) external;
function setUseAuction(bool _useAuction) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
type Id is bytes32;
struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
}
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
uint256 supplyShares;
uint128 borrowShares;
uint128 collateral;
}
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
uint128 totalSupplyAssets;
uint128 totalSupplyShares;
uint128 totalBorrowAssets;
uint128 totalBorrowShares;
uint128 lastUpdate;
uint128 fee;
}
struct Authorization {
address authorizer;
address authorized;
bool isAuthorized;
uint256 nonce;
uint256 deadline;
}
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
/// @notice The EIP-712 domain separator.
/// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on chains sharing the
/// same chain id and on forks because the domain separator would be the same.
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice The owner of the contract.
/// @dev It has the power to change the owner.
/// @dev It has the power to set fees on markets and set the fee recipient.
/// @dev It has the power to enable but not disable IRMs and LLTVs.
function owner() external view returns (address);
/// @notice The fee recipient of all markets.
/// @dev The recipient receives the fees of a given market through a supply position on that market.
function feeRecipient() external view returns (address);
/// @notice Whether the `irm` is enabled.
function isIrmEnabled(address irm) external view returns (bool);
/// @notice Whether the `lltv` is enabled.
function isLltvEnabled(uint256 lltv) external view returns (bool);
/// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
/// @dev Anyone is authorized to modify their own positions, regardless of this variable.
function isAuthorized(
address authorizer,
address authorized
) external view returns (bool);
/// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
function nonce(address authorizer) external view returns (uint256);
/// @notice Sets `newOwner` as `owner` of the contract.
/// @dev Warning: No two-step transfer ownership.
/// @dev Warning: The owner can be set to the zero address.
function setOwner(address newOwner) external;
/// @notice Enables `irm` as a possible IRM for market creation.
/// @dev Warning: It is not possible to disable an IRM.
function enableIrm(address irm) external;
/// @notice Enables `lltv` as a possible LLTV for market creation.
/// @dev Warning: It is not possible to disable a LLTV.
function enableLltv(uint256 lltv) external;
/// @notice Sets the `newFee` for the given market `marketParams`.
/// @param newFee The new fee, scaled by WAD.
/// @dev Warning: The recipient can be the zero address.
function setFee(MarketParams memory marketParams, uint256 newFee) external;
/// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
/// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
/// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
/// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
function setFeeRecipient(address newFeeRecipient) external;
/// @notice Creates the market `marketParams`.
/// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
/// Morpho behaves as expected:
/// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
/// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
/// burn functions are not supported.
/// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
/// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
/// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
/// - The IRM should not re-enter Morpho.
/// - The oracle should return a price with the correct scaling.
/// - The oracle price should not be able to change instantly such that the new price is less than the old price
/// multiplied by LLTV*LIF. In particular, if the loan asset is a vault that can receive donations, the oracle
/// should not price its shares using the AUM.
/// @dev Here is a list of assumptions on the market's dependencies which, if broken, could break Morpho's liveness
/// properties (funds could get stuck):
/// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
/// - The amount of assets supplied and borrowed should not be too high (max ~1e32), otherwise the number of shares
/// might not fit within 128 bits.
/// - The IRM should not revert on `borrowRate`.
/// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
/// `_accrueInterest` can overflow).
/// - The oracle should not revert `price`.
/// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
/// `assetsRepaid` in `liquidate` can overflow).
/// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
/// the point where `totalBorrowShares` is very large and borrowing overflows.
function createMarket(MarketParams memory marketParams) external;
/// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupply` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
/// amount of shares is given for full compatibility and precision.
/// @dev Supplying a large amount can revert for overflow.
/// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to supply assets to.
/// @param assets The amount of assets to supply.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased supply position.
/// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
/// @return assetsSupplied The amount of assets supplied.
/// @return sharesSupplied The amount of shares minted.
function supply(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsSupplied, uint256 sharesSupplied);
/// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
/// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
/// conversion roundings between shares and assets.
/// @param marketParams The market to withdraw assets from.
/// @param assets The amount of assets to withdraw.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the supply position.
/// @param receiver The address that will receive the withdrawn assets.
/// @return assetsWithdrawn The amount of assets withdrawn.
/// @return sharesWithdrawn The amount of shares burned.
function withdraw(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);
/// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
/// given for full compatibility and precision.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Borrowing a large amount can revert for overflow.
/// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to borrow assets from.
/// @param assets The amount of assets to borrow.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased borrow position.
/// @param receiver The address that will receive the borrowed assets.
/// @return assetsBorrowed The amount of assets borrowed.
/// @return sharesBorrowed The amount of shares minted.
function borrow(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);
/// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoRepay` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
/// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
/// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
/// roundings between shares and assets.
/// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
/// @param marketParams The market to repay assets to.
/// @param assets The amount of assets to repay.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the debt position.
/// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
/// @return assetsRepaid The amount of assets repaid.
/// @return sharesRepaid The amount of shares burned.
function repay(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
/// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupplyCollateral` function with the given `data`.
/// @dev Interest are not accrued since it's not required and it saves gas.
/// @dev Supplying a large amount can revert for overflow.
/// @param marketParams The market to supply collateral to.
/// @param assets The amount of collateral to supply.
/// @param onBehalf The address that will own the increased collateral position.
/// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
function supplyCollateral(
MarketParams memory marketParams,
uint256 assets,
address onBehalf,
bytes memory data
) external;
/// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
/// @param marketParams The market to withdraw collateral from.
/// @param assets The amount of collateral to withdraw.
/// @param onBehalf The address of the owner of the collateral position.
/// @param receiver The address that will receive the collateral assets.
function withdrawCollateral(
MarketParams memory marketParams,
uint256 assets,
address onBehalf,
address receiver
) external;
/// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
/// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
/// `onMorphoLiquidate` function with the given `data`.
/// @dev Either `seizedAssets` or `repaidShares` should be zero.
/// @dev Seizing more than the collateral balance will underflow and revert without any error message.
/// @dev Repaying more than the borrow balance will underflow and revert without any error message.
/// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
/// @param marketParams The market of the position.
/// @param borrower The owner of the position.
/// @param seizedAssets The amount of collateral to seize.
/// @param repaidShares The amount of shares to repay.
/// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
/// @return The amount of assets seized.
/// @return The amount of assets repaid.
function liquidate(
MarketParams memory marketParams,
address borrower,
uint256 seizedAssets,
uint256 repaidShares,
bytes memory data
) external returns (uint256, uint256);
/// @notice Executes a flash loan.
/// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
/// markets combined, plus donations).
/// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
/// - `flashFee` is zero.
/// - `maxFlashLoan` is the token's balance of this contract.
/// - The receiver of `assets` is the caller.
/// @param token The token to flash loan.
/// @param assets The amount of assets to flash loan.
/// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
function flashLoan(
address token,
uint256 assets,
bytes calldata data
) external;
/// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
/// @param authorized The authorized address.
/// @param newIsAuthorized The new authorization status.
function setAuthorization(
address authorized,
bool newIsAuthorized
) external;
/// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
/// @dev Warning: Reverts if the signature has already been submitted.
/// @dev The signature is malleable, but it has no impact on the security here.
/// @dev The nonce is passed as argument to be able to revert with a different error message.
/// @param authorization The `Authorization` struct.
/// @param signature The signature.
function setAuthorizationWithSig(
Authorization calldata authorization,
Signature calldata signature
) external;
/// @notice Accrues interest for the given market `marketParams`.
function accrueInterest(MarketParams memory marketParams) external;
/// @notice Returns the data stored on the different `slots`.
function extSloads(
bytes32[] memory slots
) external view returns (bytes32[] memory);
}
/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(
Id id,
address user
)
external
view
returns (
uint256 supplyShares,
uint128 borrowShares,
uint128 collateral
);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
/// accrual.
function market(
Id id
)
external
view
returns (
uint128 totalSupplyAssets,
uint128 totalSupplyShares,
uint128 totalBorrowAssets,
uint128 totalBorrowShares,
uint128 lastUpdate,
uint128 fee
);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(
Id id
)
external
view
returns (
address loanToken,
address collateralToken,
address oracle,
address irm,
uint256 lltv
);
}
/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(
Id id,
address user
) external view returns (Position memory p);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
/// interest accrual.
function market(Id id) external view returns (Market memory m);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(
Id id
) external view returns (MarketParams memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BaseHealthCheck, ERC20} from "@periphery/Bases/HealthCheck/BaseHealthCheck.sol";
/**
* @title Base Lender Borrower
*/
abstract contract BaseLenderBorrower is BaseHealthCheck {
using SafeERC20 for ERC20;
uint256 internal constant WAD = 1e18;
/// The token we will be borrowing/supplying.
address public immutable borrowToken;
/// If set to true, the strategy will not try to repay debt by selling rewards or asset.
bool public leaveDebtBehind;
/// @notice Target Loan-To-Value (LTV) multiplier in Basis Points
/// @dev Represents the ratio up to which we will borrow, relative to the liquidation threshold.
/// LTV is the debt-to-collateral ratio. Default is set to 70% of the liquidation LTV.
uint16 public targetLTVMultiplier;
/// @notice Warning Loan-To-Value (LTV) multiplier in Basis Points
/// @dev Represents the ratio at which we will start repaying the debt to avoid liquidation
/// Default is set to 80% of the liquidation LTV
uint16 public warningLTVMultiplier; // 80% of liquidation LTV
/// @notice Slippage tolerance (in basis points) for swaps
uint64 public slippage;
/// @notice Deposit limit for the strategy.
uint256 public depositLimit;
/// The max the base fee (in gwei) will be for a tend
uint256 public maxGasPriceToTend;
/// Thresholds: lower limit on how much base token can be borrowed at a time.
uint256 internal minAmountToBorrow;
/// The lender vault that will be used to lend and borrow.
IERC4626 public immutable lenderVault;
/**
* @param _asset The address of the asset we are lending/borrowing.
* @param _name The name of the strategy.
* @param _borrowToken The address of the borrow token.
*/
constructor(
address _asset,
string memory _name,
address _borrowToken,
address _lenderVault
) BaseHealthCheck(_asset, _name) {
borrowToken = _borrowToken;
// Set default variables
depositLimit = type(uint256).max;
targetLTVMultiplier = 7_000;
warningLTVMultiplier = 8_000;
leaveDebtBehind = false;
maxGasPriceToTend = 200 * 1e9;
slippage = 500;
// Allow for address(0) for versions that don't use 4626 vault.
if (_lenderVault != address(0)) {
lenderVault = IERC4626(_lenderVault);
require(lenderVault.asset() == _borrowToken, "!lenderVault");
ERC20(_borrowToken).forceApprove(_lenderVault, type(uint256).max);
}
}
/// ----------------- SETTERS -----------------
/**
* @notice Set the deposit limit for the strategy
* @param _depositLimit New deposit limit
*/
function setDepositLimit(uint256 _depositLimit) external onlyManagement {
depositLimit = _depositLimit;
}
/**
* @notice Set the target and warning LTV multipliers
* @param _targetLTVMultiplier New target LTV multiplier
* @param _warningLTVMultiplier New warning LTV multiplier
* @dev Target must be less than warning, warning must be <= 9000, target cannot be 0
*/
function setLtvMultipliers(
uint16 _targetLTVMultiplier,
uint16 _warningLTVMultiplier
) external onlyManagement {
require(
_warningLTVMultiplier <= 9_000 &&
_targetLTVMultiplier < _warningLTVMultiplier &&
_targetLTVMultiplier != 0,
"invalid LTV"
);
targetLTVMultiplier = _targetLTVMultiplier;
warningLTVMultiplier = _warningLTVMultiplier;
}
/**
* @notice Set whether to leave debt behind
* @param _leaveDebtBehind New leave debt behind setting
*/
function setLeaveDebtBehind(bool _leaveDebtBehind) external onlyManagement {
leaveDebtBehind = _leaveDebtBehind;
}
/**
* @notice Set the maximum gas price for tending
* @param _maxGasPriceToTend New maximum gas price
*/
function setMaxGasPriceToTend(
uint256 _maxGasPriceToTend
) external onlyManagement {
maxGasPriceToTend = _maxGasPriceToTend;
}
/**
* @notice Set the slippage tolerance
* @param _slippage New slippage tolerance in basis points
*/
function setSlippage(uint256 _slippage) external onlyManagement {
require(_slippage < MAX_BPS, "slippage");
slippage = uint64(_slippage);
}
/*//////////////////////////////////////////////////////////////
NEEDED TO BE OVERRIDDEN BY STRATEGIST
//////////////////////////////////////////////////////////////*/
/**
* @dev Should deploy up to '_amount' of 'asset' in the yield source.
*
* This function is called at the end of a {deposit} or {mint}
* call. Meaning that unless a whitelist is implemented it will
* be entirely permissionless and thus can be sandwiched or otherwise
* manipulated.
*
* @param _amount The amount of 'asset' that the strategy should attempt
* to deposit in the yield source.
*/
function _deployFunds(uint256 _amount) internal virtual override {
_leveragePosition(_amount);
}
/**
* @dev Will attempt to free the '_amount' of 'asset'.
*
* The amount of 'asset' that is already loose has already
* been accounted for.
*
* This function is called during {withdraw} and {redeem} calls.
* Meaning that unless a whitelist is implemented it will be
* entirely permissionless and thus can be sandwiched or otherwise
* manipulated.
*
* Should not rely on asset.balanceOf(address(this)) calls other than
* for diff accounting purposes.
*
* Any difference between `_amount` and what is actually freed will be
* counted as a loss and passed on to the withdrawer. This means
* care should be taken in times of illiquidity. It may be better to revert
* if withdraws are simply illiquid so not to realize incorrect losses.
*
* @param _amount, The amount of 'asset' to be freed.
*/
function _freeFunds(uint256 _amount) internal virtual override {
_liquidatePosition(_amount);
}
/**
* @dev Internal function to harvest all rewards, redeploy any idle
* funds and return an accurate accounting of all funds currently
* held by the Strategy.
*
* This should do any needed harvesting, rewards selling, accrual,
* redepositing etc. to get the most accurate view of current assets.
*
* NOTE: All applicable assets including loose assets should be
* accounted for in this function.
*
* Care should be taken when relying on oracles or swap values rather
* than actual amounts as all Strategy profit/loss accounting will
* be done based on this returned value.
*
* This can still be called post a shutdown, a strategist can check
* `TokenizedStrategy.isShutdown()` to decide if funds should be
* redeployed or simply realize any profits/losses.
*
* @return _totalAssets A trusted and accurate account for the total
* amount of 'asset' the strategy currently holds including idle funds.
*/
function _harvestAndReport()
internal
virtual
override
returns (uint256 _totalAssets)
{
/// 1. claim rewards, 2. even borrowToken deposits and borrows 3. sell remainder of rewards to asset.
_claimAndSellRewards();
/// Leverage all the asset we have or up to the supply cap.
/// We want check our leverage even if balance of asset is 0.
_leveragePosition(
Math.min(balanceOfAsset(), availableDepositLimit(address(this)))
);
/// Base token owed should be 0 here but we count it just in case
_totalAssets =
balanceOfAsset() +
balanceOfCollateral() -
_borrowTokenOwedInAsset();
}
/*//////////////////////////////////////////////////////////////
OPTIONAL TO OVERRIDE BY STRATEGIST
//////////////////////////////////////////////////////////////*/
/**
* @dev Optional function for strategist to override that can
* be called in between reports.
*
* If '_tend' is used tendTrigger() will also need to be overridden.
*
* This call can only be called by a permissioned role so may be
* through protected relays.
*
* This can be used to harvest and compound rewards, deposit idle funds,
* perform needed position maintenance or anything else that doesn't need
* a full report for.
*
* EX: A strategy that can not deposit funds without getting
* sandwiched can use the tend when a certain threshold
* of idle to totalAssets has been reached.
*
* The TokenizedStrategy contract will do all needed debt and idle updates
* after this has finished and will have no effect on PPS of the strategy
* till report() is called.
*
* @param _totalIdle The current amount of idle funds that are available to deploy.
*/
function _tend(uint256 _totalIdle) internal virtual override {
/// If the cost to borrow > rewards rate we will pull out all funds to not report a loss
if (getNetBorrowApr(0) > getNetRewardApr(0)) {
/// Liquidate everything so not to report a loss
_liquidatePosition(balanceOfCollateral());
/// Return since we don't asset to do anything else
return;
}
/// Else we need to either adjust LTV up or down.
_leveragePosition(
Math.min(_totalIdle, availableDepositLimit(address(this)))
);
}
/**
* @dev Optional trigger to override if tend() will be used by the strategy.
* This must be implemented if the strategy hopes to invoke _tend().
*
* @return . Should return true if tend() should be called by keeper or false if not.
*/
function _tendTrigger() internal view virtual override returns (bool) {
/// If we are in danger of being liquidated tend no matter what
if (_isLiquidatable()) return true;
if (TokenizedStrategy.totalAssets() == 0) return false;
/// We adjust position if:
/// 1. LTV ratios are not in the HEALTHY range (either we take on more debt or repay debt)
/// 2. costs are acceptable
uint256 collateralInUsd = _toUsd(balanceOfCollateral(), address(asset));
uint256 debtInUsd = _toUsd(balanceOfDebt(), borrowToken);
uint256 currentLTV = collateralInUsd > 0
? (debtInUsd * WAD) / collateralInUsd
: 0;
/// Check if we are over our warning LTV
if (currentLTV > _getWarningLTV()) {
return true;
}
if (_isSupplyPaused() || _isBorrowPaused()) return false;
uint256 targetLTV = _getTargetLTV();
/// If we are still levered and Borrowing costs are too high.
if (currentLTV != 0 && getNetBorrowApr(0) > getNetRewardApr(0)) {
/// Tend if base fee is acceptable.
return _isBaseFeeAcceptable();
/// IF we are lower than our target. (we need a 10% (1000bps) difference)
} else if ((currentLTV < targetLTV && targetLTV - currentLTV > 1e17)) {
/// Make sure the increase in debt would keep borrowing costs healthy.
uint256 targetDebtUsd = (collateralInUsd * targetLTV) / WAD;
uint256 amountToBorrowUsd;
unchecked {
amountToBorrowUsd = targetDebtUsd - debtInUsd; // safe bc we checked ratios
}
/// Convert to borrowToken
uint256 amountToBorrowBT = Math.min(
_fromUsd(amountToBorrowUsd, borrowToken),
Math.min(_lenderMaxDeposit(), _maxBorrowAmount())
);
if (amountToBorrowBT == 0) return false;
/// We want to make sure that the reward apr > borrow apr so we don't report a loss
/// Borrowing will cause the borrow apr to go up and the rewards apr to go down
if (
getNetBorrowApr(amountToBorrowBT) <
getNetRewardApr(amountToBorrowBT)
) {
/// Borrowing costs are healthy and WE NEED TO TAKE ON MORE DEBT
return _isBaseFeeAcceptable();
}
}
return false;
}
/**
* @notice Gets the max amount of `asset` that an address can deposit.
* @dev Defaults to an unlimited amount for any address. But can
* be overridden by strategists.
*
* This function will be called before any deposit or mints to enforce
* any limits desired by the strategist. This can be used for either a
* traditional deposit limit or for implementing a whitelist etc.
*
* EX:
* if(isAllowed[_owner]) return super.availableDepositLimit(_owner);
*
* This does not need to take into account any conversion rates
* from shares to assets. But should know that any non max uint256
* amounts may be converted to shares. So it is recommended to keep
* custom amounts low enough as not to cause overflow when multiplied
* by `totalSupply`.
*
* @param . The address that is depositing into the strategy.
* @return . The available amount the `_owner` can deposit in terms of `asset`
*/
function availableDepositLimit(
address /*_owner*/
) public view virtual override returns (uint256) {
/// We need to be able to both supply and withdraw on deposits.
if (_isSupplyPaused() || _isBorrowPaused()) return 0;
uint256 currentAssets = TokenizedStrategy.totalAssets();
uint256 limit = depositLimit > currentAssets
? depositLimit - currentAssets
: 0;
uint256 maxDeposit = Math.min(_maxCollateralDeposit(), limit);
uint256 maxBorrow = Math.min(_lenderMaxDeposit(), _maxBorrowAmount());
// Either the max supply or the max we could borrow / targetLTV.
return
Math.min(
maxDeposit,
_fromUsd(
(_toUsd(maxBorrow, borrowToken) * WAD) / _getTargetLTV(),
address(asset)
)
);
}
/**
* @notice Gets the max amount of `asset` that can be withdrawn.
* @dev Defaults to an unlimited amount for any address. But can
* be overridden by strategists.
*
* This function will be called before any withdraw or redeem to enforce
* any limits desired by the strategist. This can be used for illiquid
* or sandwichable strategies. It should never be lower than `totalIdle`.
*
* EX:
* return TokenIzedStrategy.totalIdle();
*
* This does not need to take into account the `_owner`'s share balance
* or conversion rates from shares to assets.
*
* @param . The address that is withdrawing from the strategy.
* @return . The available amount that can be withdrawn in terms of `asset`
*/
function availableWithdrawLimit(
address /*_owner*/
) public view virtual override returns (uint256) {
/// Default liquidity is the balance of collateral + 1 for rounding.
uint256 liquidity = balanceOfCollateral() + 1;
uint256 lenderLiquidity = _lenderMaxWithdraw();
/// If we can't withdraw or supply, set liquidity = 0.
if (lenderLiquidity < balanceOfLentAssets()) {
/// Adjust liquidity based on withdrawing the full amount of debt.
unchecked {
liquidity = ((_fromUsd(
_toUsd(lenderLiquidity, borrowToken),
address(asset)
) * WAD) / _getTargetLTV());
}
}
return balanceOfAsset() + liquidity;
}
/// ----------------- INTERNAL FUNCTIONS SUPPORT ----------------- \\
/**
* @notice Adjusts the leverage position of the strategy based on current and target Loan-to-Value (LTV) ratios.
* @dev All debt and collateral calculations are done in USD terms. LTV values are represented in 1e18 format.
* @param _amount The amount to be supplied to adjust the leverage position,
*/
function _leveragePosition(uint256 _amount) internal virtual {
/// Supply the given amount to the strategy.
// This function internally checks for zero amounts.
_supplyCollateral(_amount);
uint256 collateralInUsd = _toUsd(balanceOfCollateral(), address(asset));
/// Convert debt to USD
uint256 debtInUsd = _toUsd(balanceOfDebt(), borrowToken);
/// LTV numbers are always in WAD
uint256 currentLTV = collateralInUsd > 0
? (debtInUsd * WAD) / collateralInUsd
: 0;
uint256 targetLTV = _getTargetLTV(); // 70% under default liquidation Threshold
/// decide in which range we are and act accordingly:
/// SUBOPTIMAL(borrow) (e.g. from 0 to 70% liqLTV)
/// HEALTHY(do nothing) (e.g. from 70% to 80% liqLTV)
/// UNHEALTHY(repay) (e.g. from 80% to 100% liqLTV)
if (targetLTV > currentLTV) {
/// SUBOPTIMAL RATIO: our current Loan-to-Value is lower than what we want
/// we need to take on more debt
uint256 targetDebtUsd = (collateralInUsd * targetLTV) / WAD;
uint256 amountToBorrowUsd;
unchecked {
amountToBorrowUsd = targetDebtUsd - debtInUsd; // safe bc we checked ratios
}
/// convert to borrowToken
uint256 amountToBorrowBT = Math.min(
_fromUsd(amountToBorrowUsd, borrowToken),
Math.min(_lenderMaxDeposit(), _maxBorrowAmount())
);
/// We want to make sure that the reward apr > borrow apr so we don't report a loss
/// Borrowing will cause the borrow apr to go up and the rewards apr to go down
if (
getNetBorrowApr(amountToBorrowBT) >
getNetRewardApr(amountToBorrowBT)
) {
/// If we would push it over the limit don't borrow anything
amountToBorrowBT = 0;
}
/// Need to have at least the min threshold
if (amountToBorrowBT > minAmountToBorrow) {
_borrow(amountToBorrowBT);
}
} else if (currentLTV > _getWarningLTV()) {
/// UNHEALTHY RATIO
/// we repay debt to set it to targetLTV
uint256 targetDebtUsd = (targetLTV * collateralInUsd) / WAD;
/// Withdraw the difference from the Depositor
_withdrawFromLender(
_fromUsd(debtInUsd - targetDebtUsd, borrowToken)
);
/// Repay the borrowToken debt.
_repayTokenDebt();
}
// Deposit any loose base token that was borrowed.
uint256 borrowTokenBalance = balanceOfBorrowToken();
if (borrowTokenBalance > 0) {
_lendBorrowToken(borrowTokenBalance);
}
}
/**
* @notice Liquidates the position to ensure the needed amount while maintaining healthy ratios.
* @dev All debt, collateral, and needed amounts are calculated in USD. The needed amount is represented in the asset.
* @param _needed The amount required in the asset.
*/
function _liquidatePosition(uint256 _needed) internal virtual {
/// Cache balance for withdraw checks
uint256 balance = balanceOfAsset();
/// We first repay whatever we need to repay to keep healthy ratios
_withdrawFromLender(_calculateAmountToRepay(_needed));
/// we repay the borrowToken debt with the amount withdrawn from the vault
_repayTokenDebt();
// Withdraw as much as we can up to the amount needed while maintaining a health ltv
_withdrawCollateral(Math.min(_needed, _maxWithdrawal()));
/// We check if we withdrew less than expected, and we do have not more borrowToken
/// left AND should harvest or buy borrowToken with asset (potentially realising losses)
if (
/// if we didn't get enough
_needed > balanceOfAsset() - balance &&
/// still some debt remaining
balanceOfDebt() > 0 &&
/// but no capital to repay
balanceOfLentAssets() == 0 &&
/// And the leave debt flag is false.
!leaveDebtBehind
) {
/// using this part of code may result in losses but it is necessary to unlock full collateral
/// in case of wind down. This should only occur when depleting the strategy so we buy the full
/// amount of our remaining debt. We buy borrowToken first with available rewards then with asset.
_buyBorrowToken();
/// we repay debt to actually unlock collateral
/// after this, balanceOfDebt should be 0
_repayTokenDebt();
/// then we try withdraw once more
/// still withdraw with target LTV since management can potentially save any left over manually
_withdrawCollateral(_maxWithdrawal());
}
}
/**
* @notice Calculates max amount that can be withdrawn while maintaining healthy LTV ratio
* @dev Considers current collateral and debt amounts
* @return The max amount of collateral available for withdrawal
*/
function _maxWithdrawal() internal view virtual returns (uint256) {
uint256 collateral = balanceOfCollateral();
uint256 debt = balanceOfDebt();
/// If there is no debt we can withdraw everything
if (debt == 0) return collateral;
uint256 debtInUsd = _toUsd(debt, borrowToken);
/// What we need to maintain a health LTV
uint256 neededCollateral = _fromUsd(
(debtInUsd * WAD) / _getTargetLTV(),
address(asset)
);
/// We need more collateral so we cant withdraw anything
if (neededCollateral > collateral) {
return 0;
}
/// Return the difference in terms of asset
unchecked {
return collateral - neededCollateral;
}
}
/**
* @notice Calculates amount of debt to repay to maintain healthy LTV ratio
* @dev Considers target LTV, amount being withdrawn, and current collateral/debt
* @param amount The withdrawal amount
* @return The amount of debt to repay
*/
function _calculateAmountToRepay(
uint256 amount
) internal view virtual returns (uint256) {
if (amount == 0) return 0;
uint256 collateral = balanceOfCollateral();
/// To unlock all collateral we must repay all the debt
if (amount >= collateral) return balanceOfDebt();
/// We check if the collateral that we are withdrawing leaves us in a risky range, we then take action
uint256 newCollateralUsd = _toUsd(collateral - amount, address(asset));
uint256 targetDebtUsd = (newCollateralUsd * _getTargetLTV()) / WAD;
uint256 targetDebt = _fromUsd(targetDebtUsd, borrowToken);
uint256 currentDebt = balanceOfDebt();
/// Repay only if our target debt is lower than our current debt
return targetDebt < currentDebt ? currentDebt - targetDebt : 0;
}
/**
* @notice Repays outstanding debt with available base tokens
* @dev Repays debt by supplying base tokens up to the min of available balance and debt amount
*/
function _repayTokenDebt() internal virtual {
/// We cannot pay more than loose balance or more than we owe
_repay(Math.min(balanceOfBorrowToken(), balanceOfDebt()));
}
/**
* @notice Withdraws a specified amount of `borrowToken` from the lender.
* @param amount The amount of the borrowToken to withdraw.
*/
function _withdrawFromLender(uint256 amount) internal virtual {
uint256 balancePrior = balanceOfBorrowToken();
/// Only withdraw what we don't already have free
amount = balancePrior >= amount ? 0 : amount - balancePrior;
/// Make sure we have enough balance.
amount = Math.min(amount, _lenderMaxWithdraw());
if (amount == 0) return;
_withdrawBorrowToken(amount);
}
// ----------------- INTERNAL WRITE FUNCTIONS ----------------- \\
/**
* @notice Supplies a specified amount of `asset` as collateral.
* @param amount The amount of the asset to supply.
*/
function _supplyCollateral(uint256 amount) internal virtual;
/**
* @notice Withdraws a specified amount of collateral.
* @param amount The amount of the collateral to withdraw.
*/
function _withdrawCollateral(uint256 amount) internal virtual;
/**
* @notice Borrows a specified amount of `borrowToken`.
* @param amount The amount of the borrowToken to borrow.
*/
function _borrow(uint256 amount) internal virtual;
/**
* @notice Repays a specified amount of `borrowToken`.
* @param amount The amount of the borrowToken to repay.
*/
function _repay(uint256 amount) internal virtual;
/**
* @notice Lends a specified amount of `borrowToken`.
* @param amount The amount of the borrowToken to lend.
*/
function _lendBorrowToken(uint256 amount) internal virtual {
lenderVault.deposit(amount, address(this));
}
/**
* @notice Withdraws a specified amount of `borrowToken`.
* @param amount The amount of the borrowToken to withdraw.
*/
function _withdrawBorrowToken(uint256 amount) internal virtual {
// Use previewWithdraw to round up.
uint256 shares = Math.min(
lenderVault.previewWithdraw(amount),
lenderVault.balanceOf(address(this))
);
lenderVault.redeem(shares, address(this), address(this));
}
// ----------------- INTERNAL VIEW FUNCTIONS ----------------- \\
/**
* @notice Gets asset price returned 1e8
* @param _asset The asset address
* @return price asset price
*/
function _getPrice(
address _asset
) internal view virtual returns (uint256 price);
/**
* @notice Checks if lending or borrowing is paused
* @return True if paused, false otherwise
*/
function _isSupplyPaused() internal view virtual returns (bool);
/**
* @notice Checks if borrowing is paused
* @return True if paused, false otherwise
*/
function _isBorrowPaused() internal view virtual returns (bool);
/**
* @notice Checks if the strategy is liquidatable
* @return True if liquidatable, false otherwise
*/
function _isLiquidatable() internal view virtual returns (bool);
/**
* @notice Gets the supply cap for the collateral asset if any
* @return The supply cap
*/
function _maxCollateralDeposit() internal view virtual returns (uint256);
/**
* @notice Gets the max amount of `borrowToken` that could be borrowed
* @return The max borrow amount
*/
function _maxBorrowAmount() internal view virtual returns (uint256);
/**
* @notice Gets the max amount of `borrowToken` that could be deposited to the lender
* @return The max deposit amount
*/
function _lenderMaxDeposit() internal view virtual returns (uint256) {
return lenderVault.maxDeposit(address(this));
}
/**
* @notice Gets the amount of borrowToken that could be withdrawn from the lender
* @return The lender liquidity
*/
function _lenderMaxWithdraw() internal view virtual returns (uint256) {
return
lenderVault.convertToAssets(lenderVault.maxRedeem(address(this)));
}
/**
* @notice Gets net borrow APR from depositor
* @param newAmount Simulated supply amount
* @return Net borrow APR
*/
function getNetBorrowApr(
uint256 newAmount
) public view virtual returns (uint256);
/**
* @notice Gets net reward APR from depositor
* @param newAmount Simulated supply amount
* @return Net reward APR
*/
function getNetRewardApr(
uint256 newAmount
) public view virtual returns (uint256);
/**
* @notice Gets liquidation collateral factor for asset
* @return Liquidation collateral factor
*/
function getLiquidateCollateralFactor()
public
view
virtual
returns (uint256);
/**
* @notice Gets supplied collateral balance
* @return Collateral balance
*/
function balanceOfCollateral() public view virtual returns (uint256);
/**
* @notice Gets current borrow balance
* @return Borrow balance
*/
function balanceOfDebt() public view virtual returns (uint256);
/**
* @notice Gets full depositor balance
* @return Depositor balance
*/
function balanceOfLentAssets() public view virtual returns (uint256) {
return
lenderVault.convertToAssets(lenderVault.balanceOf(address(this)));
}
/**
* @notice Gets available balance of asset token
* @return The asset token balance
*/
function balanceOfAsset() public view virtual returns (uint256) {
return asset.balanceOf(address(this));
}
/**
* @notice Gets available base token balance
* @return Base token balance
*/
function balanceOfBorrowToken() public view virtual returns (uint256) {
return ERC20(borrowToken).balanceOf(address(this));
}
/**
* @notice Gets net owed base tokens (borrowed - supplied)
* @return Net base tokens owed
*/
function borrowTokenOwedBalance() public view virtual returns (uint256) {
uint256 have = balanceOfLentAssets() + balanceOfBorrowToken();
uint256 owe = balanceOfDebt();
/// If they are the same or supply > debt return 0
if (have >= owe) return 0;
unchecked {
return owe - have;
}
}
/**
* @notice Gets base tokens owed in asset terms
* @return owed tokens owed in asset value
*/
function _borrowTokenOwedInAsset()
internal
view
virtual
returns (uint256 owed)
{
/// Don't do conversions unless it's a non-zero false.
uint256 owedInBase = borrowTokenOwedBalance();
if (owedInBase != 0) {
owed = _fromUsd(_toUsd(owedInBase, borrowToken), address(asset));
}
}
/**
* @notice Calculates current loan-to-value ratio
* @dev Converts collateral and debt values to USD
* @return Current LTV in 1e18 format
*/
function getCurrentLTV() external view virtual returns (uint256) {
uint256 collateral = balanceOfCollateral();
if (collateral == 0) return 0;
unchecked {
return
(_toUsd(balanceOfDebt(), borrowToken) * WAD) /
_toUsd(collateral, address(asset));
}
}
/**
* @notice Gets target loan-to-value ratio
* @dev Calculates based on liquidation threshold and multiplier
* @return Target LTV in 1e18 format
*/
function _getTargetLTV() internal view virtual returns (uint256) {
unchecked {
return
(getLiquidateCollateralFactor() * targetLTVMultiplier) /
MAX_BPS;
}
}
/**
* @notice Gets warning loan-to-value ratio
* @dev Calculates based on liquidation threshold and multiplier
* @return Warning LTV in 1e18 format
*/
function _getWarningLTV() internal view virtual returns (uint256) {
unchecked {
return
(getLiquidateCollateralFactor() * warningLTVMultiplier) /
MAX_BPS;
}
}
/**
* @notice Converts a token amount to USD value
* @dev This assumes _getPrice returns constants 1e8 price
* @param _amount The token amount
* @param _token The token address
* @return The USD value scaled by 1e8
*/
function _toUsd(
uint256 _amount,
address _token
) internal view virtual returns (uint256) {
if (_amount == 0) return 0;
unchecked {
return
(_amount * _getPrice(_token)) /
(10 ** ERC20(_token).decimals());
}
}
/**
* @notice Converts a USD amount to token value
* @dev This assumes _getPrice returns constants 1e8 price
* @param _amount The USD amount (scaled by 1e8)
* @param _token The token address
* @return The token amount
*/
function _fromUsd(
uint256 _amount,
address _token
) internal view virtual returns (uint256) {
if (_amount == 0) return 0;
unchecked {
return
(_amount * (10 ** ERC20(_token).decimals())) /
_getPrice(_token);
}
}
/// ----------------- HARVEST / TOKEN CONVERSIONS -----------------
/**
* @notice Claims reward tokens.
*/
function _claimRewards() internal virtual;
/**
* @notice Claims and sells available reward tokens
* @dev Handles claiming, selling rewards for borrow tokens if needed, and selling remaining rewards for asset
*/
function _claimAndSellRewards() internal virtual;
/**
* @dev Buys the borrow token using the strategy's assets.
* This function should only ever be called when withdrawing all funds from the strategy if there is debt left over.
* Initially, it tries to sell rewards for the needed amount of base token, then it will swap assets.
* Using this function in a standard withdrawal can cause it to be sandwiched, which is why rewards are used first.
*/
function _buyBorrowToken() internal virtual;
/**
* @dev Will swap from the base token => underlying asset.
*/
function _sellBorrowToken(uint256 _amount) internal virtual;
/**
* @notice Estimates swap output accounting for slippage
* @param _amount Input amount
* @param _from Input token
* @param _to Output token
* @return Estimated output amount
*/
function _getAmountOut(
uint256 _amount,
address _from,
address _to
) internal view virtual returns (uint256) {
if (_amount == 0) return 0;
return
(_fromUsd(_toUsd(_amount, _from), _to) * (MAX_BPS - slippage)) /
MAX_BPS;
}
/**
* @notice Checks if base fee is acceptable
* @return True if base fee is below threshold
*/
function _isBaseFeeAcceptable() internal view virtual returns (bool) {
return block.basefee <= maxGasPriceToTend;
}
/**
* @dev Optional function for a strategist to override that will
* allow management to manually withdraw deployed funds from the
* yield source if a strategy is shutdown.
*
* This should attempt to free `_amount`, noting that `_amount` may
* be more than is currently deployed.
*
* NOTE: This will not realize any profits or losses. A separate
* {report} will be needed in order to record any profit/loss. If
* a report may need to be called after a shutdown it is important
* to check if the strategy is shutdown during {_harvestAndReport}
* so that it does not simply re-deploy all funds that had been freed.
*
* EX:
* if(freeAsset > 0 && !TokenizedStrategy.isShutdown()) {
* depositFunds...
* }
*
* @param _amount The amount of asset to attempt to free.
*/
function _emergencyWithdraw(uint256 _amount) internal virtual override {
_amount = Math.min(_amount, _lenderMaxWithdraw());
if (_amount > 0) _withdrawBorrowToken(_amount);
// Repay everything we can.
_repayTokenDebt();
// Withdraw all that makes sense.
_withdrawCollateral(_maxWithdrawal());
}
// Manually Sell rewards
function claimAndSellRewards() external virtual onlyEmergencyAuthorized {
_claimAndSellRewards();
}
/// @notice Sell a specific amount of `borrowToken` -> asset.
/// The amount of borrowToken should be loose in the strategy before this is called
/// max uint input will sell any excess borrowToken we have.
function sellBorrowToken(
uint256 _amount
) external virtual onlyEmergencyAuthorized {
if (_amount == type(uint256).max) {
uint256 _balanceOfBorrowToken = balanceOfBorrowToken();
uint256 have = balanceOfLentAssets() + _balanceOfBorrowToken;
uint256 owe = balanceOfDebt();
// Only sell excess if we have more than we owe
_amount = have > owe
? Math.min(have - owe, _balanceOfBorrowToken)
: 0;
}
_sellBorrowToken(_amount);
}
/// @notice Withdraw a specific amount of `_token`
function manualWithdraw(
address _token,
uint256 _amount
) external virtual onlyEmergencyAuthorized {
if (_token == borrowToken) {
_withdrawBorrowToken(_amount);
} else {
_withdrawCollateral(_amount);
}
}
// Manually repay debt with loose borrowToken already in the strategy.
function manualRepayDebt() external virtual onlyEmergencyAuthorized {
_repayTokenDebt();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IChainlinkAggregator {
function latestAnswer() external view returns (int256);
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title IOracle
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Interface that oracles used by Morpho must implement.
/// @dev It is the user's responsibility to select markets with safe oracles.
interface IOracle {
/// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36.
/// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in
/// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals`
/// decimals of precision.
function price() external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id, MarketParams, Market, IMorpho} from "../../../interfaces/morpho/IMorpho.sol";
import {IIrm} from "../../../interfaces/morpho/IIrm.sol";
import {MathLib} from "../MathLib.sol";
import {UtilsLib} from "../UtilsLib.sol";
import {MorphoLib} from "./MorphoLib.sol";
import {SharesMathLib} from "../SharesMathLib.sol";
import {MarketParamsLib} from "../MarketParamsLib.sol";
/// @title MorphoBalancesLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library exposing getters with the expected value after interest accrual.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply
/// to it. The value can be queried directly on Morpho using `totalBorrowShares`.
library MorphoBalancesLib {
using MathLib for uint256;
using MathLib for uint128;
using UtilsLib for uint256;
using MorphoLib for IMorpho;
using SharesMathLib for uint256;
using MarketParamsLib for MarketParams;
/// @notice Returns the expected market balances of a market after having accrued interest.
/// @return The expected total supply assets.
/// @return The expected total supply shares.
/// @return The expected total borrow assets.
/// @return The expected total borrow shares.
function expectedMarketBalances(
IMorpho morpho,
MarketParams memory marketParams
) internal view returns (uint256, uint256, uint256, uint256) {
Id id = marketParams.id();
Market memory market = morpho.market(id);
uint256 elapsed = block.timestamp - market.lastUpdate;
// Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0).
if (
elapsed != 0 &&
market.totalBorrowAssets != 0 &&
marketParams.irm != address(0)
) {
uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(
marketParams,
market
);
uint256 interest = market.totalBorrowAssets.wMulDown(
borrowRate.wTaylorCompounded(elapsed)
);
market.totalBorrowAssets += interest.toUint128();
market.totalSupplyAssets += interest.toUint128();
if (market.fee != 0) {
uint256 feeAmount = interest.wMulDown(market.fee);
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact
// that total supply is already updated.
uint256 feeShares = feeAmount.toSharesDown(
market.totalSupplyAssets - feeAmount,
market.totalSupplyShares
);
market.totalSupplyShares += feeShares.toUint128();
}
}
return (
market.totalSupplyAssets,
market.totalSupplyShares,
market.totalBorrowAssets,
market.totalBorrowShares
);
}
/// @notice Returns the expected total supply assets of a market after having accrued interest.
function expectedTotalSupplyAssets(
IMorpho morpho,
MarketParams memory marketParams
) internal view returns (uint256 totalSupplyAssets) {
(totalSupplyAssets, , , ) = expectedMarketBalances(
morpho,
marketParams
);
}
/// @notice Returns the expected total borrow assets of a market after having accrued interest.
function expectedTotalBorrowAssets(
IMorpho morpho,
MarketParams memory marketParams
) internal view returns (uint256 totalBorrowAssets) {
(, , totalBorrowAssets, ) = expectedMarketBalances(
morpho,
marketParams
);
}
/// @notice Returns the expected total supply shares of a market after having accrued interest.
function expectedTotalSupplyShares(
IMorpho morpho,
MarketParams memory marketParams
) internal view returns (uint256 totalSupplyShares) {
(, totalSupplyShares, , ) = expectedMarketBalances(
morpho,
marketParams
);
}
/// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest.
/// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account.
function expectedSupplyAssets(
IMorpho morpho,
MarketParams memory marketParams,
address user
) internal view returns (uint256) {
Id id = marketParams.id();
uint256 supplyShares = morpho.supplyShares(id, user);
(
uint256 totalSupplyAssets,
uint256 totalSupplyShares,
,
) = expectedMarketBalances(morpho, marketParams);
return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares);
}
/// @notice Returns the expected borrow assets balance of `user` on a market after having accrued interest.
/// @dev Warning: The expected balance is rounded up, so it may be greater than the market's expected total borrow
/// assets.
function expectedBorrowAssets(
IMorpho morpho,
MarketParams memory marketParams,
address user
) internal view returns (uint256) {
Id id = marketParams.id();
uint256 borrowShares = morpho.borrowShares(id, user);
(
,
,
uint256 totalBorrowAssets,
uint256 totalBorrowShares
) = expectedMarketBalances(morpho, marketParams);
return borrowShares.toAssetsUp(totalBorrowAssets, totalBorrowShares);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {MathLib} from "./MathLib.sol";
/// @title SharesMathLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Shares management library.
/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
library SharesMathLib {
using MathLib for uint256;
/// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
/// high precision computations.
/// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
/// stays low enough not to inflate these assets to a significant value.
/// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
uint256 internal constant VIRTUAL_SHARES = 1e6;
/// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
/// empty.
uint256 internal constant VIRTUAL_ASSETS = 1;
/// @dev Calculates the value of `assets` quoted in shares, rounding down.
function toSharesDown(
uint256 assets,
uint256 totalAssets,
uint256 totalShares
) internal pure returns (uint256) {
return
assets.mulDivDown(
totalShares + VIRTUAL_SHARES,
totalAssets + VIRTUAL_ASSETS
);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding down.
function toAssetsDown(
uint256 shares,
uint256 totalAssets,
uint256 totalShares
) internal pure returns (uint256) {
return
shares.mulDivDown(
totalAssets + VIRTUAL_ASSETS,
totalShares + VIRTUAL_SHARES
);
}
/// @dev Calculates the value of `assets` quoted in shares, rounding up.
function toSharesUp(
uint256 assets,
uint256 totalAssets,
uint256 totalShares
) internal pure returns (uint256) {
return
assets.mulDivUp(
totalShares + VIRTUAL_SHARES,
totalAssets + VIRTUAL_ASSETS
);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding up.
function toAssetsUp(
uint256 shares,
uint256 totalAssets,
uint256 totalShares
) internal pure returns (uint256) {
return
shares.mulDivUp(
totalAssets + VIRTUAL_ASSETS,
totalShares + VIRTUAL_SHARES
);
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ISwapRouter} from "../interfaces/Uniswap/V3/ISwapRouter.sol";
import {BaseSwapper} from "./BaseSwapper.sol";
/**
* @title UniswapV3Swapper
* @author Yearn.finance
* @dev This is a simple contract that can be inherited by any tokenized
* strategy that would like to use Uniswap V3 for swaps. It hold all needed
* logic to perform both exact input and exact output swaps.
*
* The global address variables default to the ETH mainnet addresses but
* remain settable by the inheriting contract to allow for customization
* based on needs or chain its used on.
*
* The only variables that are required to be set are the specific fees
* for each token pair. The inheriting contract can use the {_setUniFees}
* function to easily set this for any token pairs needed.
*/
contract UniswapV3Swapper is BaseSwapper {
using SafeERC20 for ERC20;
// Defaults to WETH on mainnet.
address public base = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
// Defaults to Uniswap V3 router on mainnet.
address public router = 0xE592427A0AEce92De3Edee1F18E0157C05861564;
// Fees for the Uni V3 pools. Each fee should get set each way in
// the mapping so no matter the direction the correct fee will get
// returned for any two tokens.
mapping(address => mapping(address => uint24)) public uniFees;
/**
* @dev All fess will default to 0 on creation. A strategist will need
* To set the mapping for the tokens expected to swap. This function
* is to help set the mapping. It can be called internally during
* initialization, through permissioned functions etc.
*/
function _setUniFees(
address _token0,
address _token1,
uint24 _fee
) internal virtual {
uniFees[_token0][_token1] = _fee;
uniFees[_token1][_token0] = _fee;
}
/**
* @dev Used to swap a specific amount of `_from` to `_to`.
* This will check and handle all allowances as well as not swapping
* unless `_amountIn` is greater than the set `_minAmountOut`
*
* If one of the tokens matches with the `base` token it will do only
* one jump, otherwise will do two jumps.
*
* The corresponding uniFees for each token pair will need to be set
* other wise this function will revert.
*
* @param _from The token we are swapping from.
* @param _to The token we are swapping to.
* @param _amountIn The amount of `_from` we will swap.
* @param _minAmountOut The min of `_to` to get out.
* @return _amountOut The actual amount of `_to` that was swapped to
*/
function _swapFrom(
address _from,
address _to,
uint256 _amountIn,
uint256 _minAmountOut
) internal virtual returns (uint256 _amountOut) {
if (_amountIn != 0 && _amountIn >= minAmountToSell) {
_checkAllowance(router, _from, _amountIn);
if (_from == base || _to == base) {
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter
.ExactInputSingleParams(
_from, // tokenIn
_to, // tokenOut
uniFees[_from][_to], // from-to fee
address(this), // recipient
block.timestamp, // deadline
_amountIn, // amountIn
_minAmountOut, // amountOut
0 // sqrtPriceLimitX96
);
_amountOut = ISwapRouter(router).exactInputSingle(params);
} else {
bytes memory path = abi.encodePacked(
_from, // tokenIn
uniFees[_from][base], // from-base fee
base, // base token
uniFees[base][_to], // base-to fee
_to // tokenOut
);
_amountOut = ISwapRouter(router).exactInput(
ISwapRouter.ExactInputParams(
path,
address(this),
block.timestamp,
_amountIn,
_minAmountOut
)
);
}
}
}
/**
* @dev Used to swap a specific amount of `_to` from `_from` unless
* it takes more than `_maxAmountFrom`.
*
* This will check and handle all allowances as well as not swapping
* unless `_maxAmountFrom` is greater than the set `minAmountToSell`
*
* If one of the tokens matches with the `base` token it will do only
* one jump, otherwise will do two jumps.
*
* The corresponding uniFees for each token pair will need to be set
* other wise this function will revert.
*
* @param _from The token we are swapping from.
* @param _to The token we are swapping to.
* @param _amountTo The amount of `_to` we need out.
* @param _maxAmountFrom The max of `_from` we will swap.
* @return _amountIn The actual amount of `_from` swapped.
*/
function _swapTo(
address _from,
address _to,
uint256 _amountTo,
uint256 _maxAmountFrom
) internal virtual returns (uint256 _amountIn) {
if (_maxAmountFrom != 0 && _maxAmountFrom >= minAmountToSell) {
_checkAllowance(router, _from, _maxAmountFrom);
if (_from == base || _to == base) {
ISwapRouter.ExactOutputSingleParams memory params = ISwapRouter
.ExactOutputSingleParams(
_from, // tokenIn
_to, // tokenOut
uniFees[_from][_to], // from-to fee
address(this), // recipient
block.timestamp, // deadline
_amountTo, // amountOut
_maxAmountFrom, // maxAmountIn
0 // sqrtPriceLimitX96
);
_amountIn = ISwapRouter(router).exactOutputSingle(params);
} else {
bytes memory path = abi.encodePacked(
_to,
uniFees[base][_to], // base-to fee
base,
uniFees[_from][base], // from-base fee
_from
);
_amountIn = ISwapRouter(router).exactOutput(
ISwapRouter.ExactOutputParams(
path,
address(this),
block.timestamp,
_amountTo, // How much we want out
_maxAmountFrom
)
);
}
}
}
/**
* @dev Internal safe function to make sure the contract you want to
* interact with has enough allowance to pull the desired tokens.
*
* @param _contract The address of the contract that will move the token.
* @param _token The ERC-20 token that will be getting spent.
* @param _amount The amount of `_token` to be spent.
*/
function _checkAllowance(
address _contract,
address _token,
uint256 _amount
) internal virtual {
if (ERC20(_token).allowance(address(this), _contract) < _amount) {
ERC20(_token).forceApprove(_contract, 0);
ERC20(_token).forceApprove(_contract, _amount);
}
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {ITokenizedStrategy} from "./ITokenizedStrategy.sol";
import {IBaseStrategy} from "./IBaseStrategy.sol";
interface IStrategy is IBaseStrategy, ITokenizedStrategy {}// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.18;
import {IBaseHealthCheck} from "@periphery/Bases/HealthCheck/IBaseHealthCheck.sol";
interface ILenderBorrower is IBaseHealthCheck {
// Public Variables
function borrowToken() external view returns (address);
function leaveDebtBehind() external view returns (bool);
function depositLimit() external view returns (uint256);
function targetLTVMultiplier() external view returns (uint16);
function warningLTVMultiplier() external view returns (uint16);
function maxGasPriceToTend() external view returns (uint256);
function slippage() external view returns (uint256);
// External Functions
function setDepositLimit(uint256 _depositLimit) external;
function setLtvMultipliers(
uint16 _targetLTVMultiplier,
uint16 _warningLTVMultiplier
) external;
function setLeaveDebtBehind(bool _leaveDebtBehind) external;
function setMaxGasPriceToTend(uint256 _maxGasPriceToTend) external;
function setSlippage(uint256 _slippage) external;
// Public View Functions
function getCurrentLTV() external view returns (uint256);
function getNetBorrowApr(uint256 newAmount) external view returns (uint256);
function getNetRewardApr(uint256 newAmount) external view returns (uint256);
function getLiquidateCollateralFactor() external view returns (uint256);
function balanceOfCollateral() external view returns (uint256);
function balanceOfDebt() external view returns (uint256);
function balanceOfLentAssets() external view returns (uint256);
function balanceOfAsset() external view returns (uint256);
function balanceOfBorrowToken() external view returns (uint256);
function borrowTokenOwedBalance() external view returns (uint256);
// Emergency Functions
function claimAndSellRewards() external;
function sellBorrowToken(uint256 _amount) external;
function manualWithdraw(address _token, uint256 _amount) external;
function manualRepayDebt() external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*
* _Available since v4.7._
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {BaseStrategy, ERC20} from "@tokenized-strategy/BaseStrategy.sol";
/**
* @title Base Health Check
* @author Yearn.finance
* @notice This contract can be inherited by any Yearn
* V3 strategy wishing to implement a health check during
* the `report` function in order to prevent any unexpected
* behavior from being permanently recorded as well as the
* `checkHealth` modifier.
*
* A strategist simply needs to inherit this contract. Set
* the limit ratios to the desired amounts and then
* override `_harvestAndReport()` just as they otherwise
* would. If the profit or loss that would be recorded is
* outside the acceptable bounds the tx will revert.
*
* The healthcheck does not prevent a strategy from reporting
* losses, but rather can make sure manual intervention is
* needed before reporting an unexpected loss or profit.
*/
abstract contract BaseHealthCheck is BaseStrategy {
// Can be used to determine if a healthcheck should be called.
// Defaults to true;
bool public doHealthCheck = true;
uint256 internal constant MAX_BPS = 10_000;
// Default profit limit to 100%.
uint16 private _profitLimitRatio = uint16(MAX_BPS);
// Defaults loss limit to 0.
uint16 private _lossLimitRatio;
constructor(
address _asset,
string memory _name
) BaseStrategy(_asset, _name) {}
/**
* @notice Returns the current profit limit ratio.
* @dev Use a getter function to keep the variable private.
* @return . The current profit limit ratio.
*/
function profitLimitRatio() public view returns (uint256) {
return _profitLimitRatio;
}
/**
* @notice Returns the current loss limit ratio.
* @dev Use a getter function to keep the variable private.
* @return . The current loss limit ratio.
*/
function lossLimitRatio() public view returns (uint256) {
return _lossLimitRatio;
}
/**
* @notice Set the `profitLimitRatio`.
* @dev Denominated in basis points. I.E. 1_000 == 10%.
* @param _newProfitLimitRatio The mew profit limit ratio.
*/
function setProfitLimitRatio(
uint256 _newProfitLimitRatio
) external onlyManagement {
_setProfitLimitRatio(_newProfitLimitRatio);
}
/**
* @dev Internally set the profit limit ratio. Denominated
* in basis points. I.E. 1_000 == 10%.
* @param _newProfitLimitRatio The mew profit limit ratio.
*/
function _setProfitLimitRatio(uint256 _newProfitLimitRatio) internal {
require(_newProfitLimitRatio > 0, "!zero profit");
require(_newProfitLimitRatio <= type(uint16).max, "!too high");
_profitLimitRatio = uint16(_newProfitLimitRatio);
}
/**
* @notice Set the `lossLimitRatio`.
* @dev Denominated in basis points. I.E. 1_000 == 10%.
* @param _newLossLimitRatio The new loss limit ratio.
*/
function setLossLimitRatio(
uint256 _newLossLimitRatio
) external onlyManagement {
_setLossLimitRatio(_newLossLimitRatio);
}
/**
* @dev Internally set the loss limit ratio. Denominated
* in basis points. I.E. 1_000 == 10%.
* @param _newLossLimitRatio The new loss limit ratio.
*/
function _setLossLimitRatio(uint256 _newLossLimitRatio) internal {
require(_newLossLimitRatio < MAX_BPS, "!loss limit");
_lossLimitRatio = uint16(_newLossLimitRatio);
}
/**
* @notice Turns the healthcheck on and off.
* @dev If turned off the next report will auto turn it back on.
* @param _doHealthCheck Bool if healthCheck should be done.
*/
function setDoHealthCheck(bool _doHealthCheck) public onlyManagement {
doHealthCheck = _doHealthCheck;
}
/**
* @notice OVerrides the default {harvestAndReport} to include a healthcheck.
* @return _totalAssets New totalAssets post report.
*/
function harvestAndReport()
external
override
onlySelf
returns (uint256 _totalAssets)
{
// Let the strategy report.
_totalAssets = _harvestAndReport();
// Run the healthcheck on the amount returned.
_executeHealthCheck(_totalAssets);
}
/**
* @dev To be called during a report to make sure the profit
* or loss being recorded is within the acceptable bound.
*
* @param _newTotalAssets The amount that will be reported.
*/
function _executeHealthCheck(uint256 _newTotalAssets) internal virtual {
if (!doHealthCheck) {
doHealthCheck = true;
return;
}
// Get the current total assets from the implementation.
uint256 currentTotalAssets = TokenizedStrategy.totalAssets();
if (_newTotalAssets > currentTotalAssets) {
require(
((_newTotalAssets - currentTotalAssets) <=
(currentTotalAssets * uint256(_profitLimitRatio)) /
MAX_BPS),
"healthCheck"
);
} else if (currentTotalAssets > _newTotalAssets) {
require(
(currentTotalAssets - _newTotalAssets <=
((currentTotalAssets * uint256(_lossLimitRatio)) /
MAX_BPS)),
"healthCheck"
);
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import {MarketParams, Market} from "./IMorpho.sol";
/// @title IIrm
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
/// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRate(
MarketParams memory marketParams,
Market memory market
) external returns (uint256);
/// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
/// storage.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRateView(
MarketParams memory marketParams,
Market memory market
) external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
uint256 constant WAD = 1e18;
/// @title MathLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to manage fixed-point arithmetic.
library MathLib {
/// @dev Returns (`x` * `y`) / `WAD` rounded down.
function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD);
}
/// @dev Returns (`x` * `WAD`) / `y` rounded down.
function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y);
}
/// @dev Returns (`x` * `WAD`) / `y` rounded up.
function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y);
}
/// @dev Returns (`x` * `y`) / `d` rounded down.
function mulDivDown(
uint256 x,
uint256 y,
uint256 d
) internal pure returns (uint256) {
return (x * y) / d;
}
/// @dev Returns (`x` * `y`) / `d` rounded up.
function mulDivUp(
uint256 x,
uint256 y,
uint256 d
) internal pure returns (uint256) {
return (x * y + (d - 1)) / d;
}
/// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
/// continuous compound interest rate.
function wTaylorCompounded(
uint256 x,
uint256 n
) internal pure returns (uint256) {
uint256 firstTerm = x * n;
uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);
return firstTerm + secondTerm + thirdTerm;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {ErrorsLib} from "./ErrorsLib.sol";
/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library exposing helpers.
/// @dev Inspired by https://github.com/morpho-org/morpho-utils.
library UtilsLib {
/// @dev Returns true if there is exactly one zero among `x` and `y`.
function exactlyOneZero(
uint256 x,
uint256 y
) internal pure returns (bool z) {
assembly {
z := xor(iszero(x), iszero(y))
}
}
/// @dev Returns the min of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns `x` safely cast to uint128.
function toUint128(uint256 x) internal pure returns (uint128) {
require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED);
return uint128(x);
}
/// @dev Returns max(0, x - y).
function zeroFloorSub(
uint256 x,
uint256 y
) internal pure returns (uint256 z) {
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {IMorpho, Id} from "../../../interfaces/morpho/IMorpho.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";
/// @title MorphoLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library to access Morpho storage variables.
/// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest.
library MorphoLib {
function supplyShares(
IMorpho morpho,
Id id,
address user
) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.positionSupplySharesSlot(id, user)
);
return uint256(morpho.extSloads(slot)[0]);
}
function borrowShares(
IMorpho morpho,
Id id,
address user
) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user)
);
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function collateral(
IMorpho morpho,
Id id,
address user
) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user)
);
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function totalSupplyAssets(
IMorpho morpho,
Id id
) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id)
);
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function totalSupplyShares(
IMorpho morpho,
Id id
) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id)
);
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function totalBorrowAssets(
IMorpho morpho,
Id id
) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id)
);
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function totalBorrowShares(
IMorpho morpho,
Id id
) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id)
);
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.marketLastUpdateAndFeeSlot(id)
);
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function fee(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(
MorphoStorageLib.marketLastUpdateAndFeeSlot(id)
);
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function _array(bytes32 x) private pure returns (bytes32[] memory) {
bytes32[] memory res = new bytes32[](1);
res[0] = x;
return res;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id, MarketParams} from "../../interfaces/morpho/IMorpho.sol";
/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to convert a market to its id.
library MarketParamsLib {
/// @notice The length of the data used to compute the id of a market.
/// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;
/// @notice Returns the id of the market `marketParams`.
function id(
MarketParams memory marketParams
) internal pure returns (Id marketParamsId) {
assembly ("memory-safe") {
marketParamsId := keccak256(
marketParams,
MARKET_PARAMS_BYTES_LENGTH
)
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.12;
pragma experimental ABIEncoderV2;
import "./IUniswapV3SwapCallback.sol";
/// @title Router token swapping functionality
/// @notice Functions for swapping tokens via Uniswap V3
interface ISwapRouter is IUniswapV3SwapCallback {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another token
/// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
/// @return amountOut The amount of the received token
function exactInputSingle(
ExactInputSingleParams calldata params
) external payable returns (uint256 amountOut);
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
/// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata
/// @return amountOut The amount of the received token
function exactInput(
ExactInputParams calldata params
) external payable returns (uint256 amountOut);
struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another token
/// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
/// @return amountIn The amount of the input token
function exactOutputSingle(
ExactOutputSingleParams calldata params
) external payable returns (uint256 amountIn);
struct ExactOutputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
}
/// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed)
/// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata
/// @return amountIn The amount of the input token
function exactOutput(
ExactOutputParams calldata params
) external payable returns (uint256 amountIn);
// Taken from https://soliditydeveloper.com/uniswap3
// Manually added to the interface
function refundETH() external payable;
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
/**
* @title BaseSwapper
* @author yearn.fi
* @dev Base contract for all swapper contracts except TradeFactorySwapper.
* Contains the common minAmountToSell variable that most swappers need.
*/
contract BaseSwapper {
/// @notice Minimum amount of tokens to sell in a swap.
uint256 public minAmountToSell;
/**
* @dev Set the minimum amount to sell in a swap.
* @param _minAmountToSell Minimum amount of tokens needed to execute a swap.
*/
function _setMinAmountToSell(uint256 _minAmountToSell) internal virtual {
minAmountToSell = _minAmountToSell;
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
// Interface that implements the 4626 standard and the implementation functions
interface ITokenizedStrategy is IERC4626, IERC20Permit {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event StrategyShutdown();
event NewTokenizedStrategy(
address indexed strategy,
address indexed asset,
string apiVersion
);
event Reported(
uint256 profit,
uint256 loss,
uint256 protocolFees,
uint256 performanceFees
);
event UpdatePerformanceFeeRecipient(
address indexed newPerformanceFeeRecipient
);
event UpdateKeeper(address indexed newKeeper);
event UpdatePerformanceFee(uint16 newPerformanceFee);
event UpdateManagement(address indexed newManagement);
event UpdateEmergencyAdmin(address indexed newEmergencyAdmin);
event UpdateProfitMaxUnlockTime(uint256 newProfitMaxUnlockTime);
event UpdatePendingManagement(address indexed newPendingManagement);
/*//////////////////////////////////////////////////////////////
INITIALIZATION
//////////////////////////////////////////////////////////////*/
function initialize(
address _asset,
string memory _name,
address _management,
address _performanceFeeRecipient,
address _keeper
) external;
/*//////////////////////////////////////////////////////////////
NON-STANDARD 4626 OPTIONS
//////////////////////////////////////////////////////////////*/
function withdraw(
uint256 assets,
address receiver,
address owner,
uint256 maxLoss
) external returns (uint256);
function redeem(
uint256 shares,
address receiver,
address owner,
uint256 maxLoss
) external returns (uint256);
function maxWithdraw(
address owner,
uint256 /*maxLoss*/
) external view returns (uint256);
function maxRedeem(
address owner,
uint256 /*maxLoss*/
) external view returns (uint256);
/*//////////////////////////////////////////////////////////////
MODIFIER HELPERS
//////////////////////////////////////////////////////////////*/
function requireManagement(address _sender) external view;
function requireKeeperOrManagement(address _sender) external view;
function requireEmergencyAuthorized(address _sender) external view;
/*//////////////////////////////////////////////////////////////
KEEPERS FUNCTIONS
//////////////////////////////////////////////////////////////*/
function tend() external;
function report() external returns (uint256 _profit, uint256 _loss);
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
function MAX_FEE() external view returns (uint16);
function FACTORY() external view returns (address);
/*//////////////////////////////////////////////////////////////
GETTERS
//////////////////////////////////////////////////////////////*/
function apiVersion() external view returns (string memory);
function pricePerShare() external view returns (uint256);
function management() external view returns (address);
function pendingManagement() external view returns (address);
function keeper() external view returns (address);
function emergencyAdmin() external view returns (address);
function performanceFee() external view returns (uint16);
function performanceFeeRecipient() external view returns (address);
function fullProfitUnlockDate() external view returns (uint256);
function profitUnlockingRate() external view returns (uint256);
function profitMaxUnlockTime() external view returns (uint256);
function lastReport() external view returns (uint256);
function isShutdown() external view returns (bool);
function unlockedShares() external view returns (uint256);
/*//////////////////////////////////////////////////////////////
SETTERS
//////////////////////////////////////////////////////////////*/
function setPendingManagement(address) external;
function acceptManagement() external;
function setKeeper(address _keeper) external;
function setEmergencyAdmin(address _emergencyAdmin) external;
function setPerformanceFee(uint16 _performanceFee) external;
function setPerformanceFeeRecipient(
address _performanceFeeRecipient
) external;
function setProfitMaxUnlockTime(uint256 _profitMaxUnlockTime) external;
function setName(string calldata _newName) external;
function shutdownStrategy() external;
function emergencyWithdraw(uint256 _amount) external;
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
interface IBaseStrategy {
function tokenizedStrategyAddress() external view returns (address);
/*//////////////////////////////////////////////////////////////
IMMUTABLE FUNCTIONS
//////////////////////////////////////////////////////////////*/
function availableDepositLimit(
address _owner
) external view returns (uint256);
function availableWithdrawLimit(
address _owner
) external view returns (uint256);
function deployFunds(uint256 _assets) external;
function freeFunds(uint256 _amount) external;
function harvestAndReport() external returns (uint256);
function tendThis(uint256 _totalIdle) external;
function shutdownWithdraw(uint256 _amount) external;
function tendTrigger() external view returns (bool, bytes memory);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {IStrategy} from "@tokenized-strategy/interfaces/IStrategy.sol";
interface IBaseHealthCheck is IStrategy {
function doHealthCheck() external view returns (bool);
function profitLimitRatio() external view returns (uint256);
function lossLimitRatio() external view returns (uint256);
function setProfitLimitRatio(uint256 _newProfitLimitRatio) external;
function setLossLimitRatio(uint256 _newLossLimitRatio) external;
function setDoHealthCheck(bool _doHealthCheck) external;
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// TokenizedStrategy interface used for internal view delegateCalls.
import {ITokenizedStrategy} from "./interfaces/ITokenizedStrategy.sol";
/**
* @title YearnV3 Base Strategy
* @author yearn.finance
* @notice
* BaseStrategy implements all of the required functionality to
* seamlessly integrate with the `TokenizedStrategy` implementation contract
* allowing anyone to easily build a fully permissionless ERC-4626 compliant
* Vault by inheriting this contract and overriding three simple functions.
* It utilizes an immutable proxy pattern that allows the BaseStrategy
* to remain simple and small. All standard logic is held within the
* `TokenizedStrategy` and is reused over any n strategies all using the
* `fallback` function to delegatecall the implementation so that strategists
* can only be concerned with writing their strategy specific code.
*
* This contract should be inherited and the three main abstract methods
* `_deployFunds`, `_freeFunds` and `_harvestAndReport` implemented to adapt
* the Strategy to the particular needs it has to generate yield. There are
* other optional methods that can be implemented to further customize
* the strategy if desired.
*
* All default storage for the strategy is controlled and updated by the
* `TokenizedStrategy`. The implementation holds a storage struct that
* contains all needed global variables in a manual storage slot. This
* means strategists can feel free to implement their own custom storage
* variables as they need with no concern of collisions. All global variables
* can be viewed within the Strategy by a simple call using the
* `TokenizedStrategy` variable. IE: TokenizedStrategy.globalVariable();.
*/
abstract contract BaseStrategy {
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
/**
* @dev Used on TokenizedStrategy callback functions to make sure it is post
* a delegateCall from this address to the TokenizedStrategy.
*/
modifier onlySelf() {
_onlySelf();
_;
}
/**
* @dev Use to assure that the call is coming from the strategies management.
*/
modifier onlyManagement() {
TokenizedStrategy.requireManagement(msg.sender);
_;
}
/**
* @dev Use to assure that the call is coming from either the strategies
* management or the keeper.
*/
modifier onlyKeepers() {
TokenizedStrategy.requireKeeperOrManagement(msg.sender);
_;
}
/**
* @dev Use to assure that the call is coming from either the strategies
* management or the emergency admin.
*/
modifier onlyEmergencyAuthorized() {
TokenizedStrategy.requireEmergencyAuthorized(msg.sender);
_;
}
/**
* @dev Require that the msg.sender is this address.
*/
function _onlySelf() internal view {
require(msg.sender == address(this), "!self");
}
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @dev This is the address of the TokenizedStrategy implementation
* contract that will be used by all strategies to handle the
* accounting, logic, storage etc.
*
* Any external calls to the that don't hit one of the functions
* defined in this base or the strategy will end up being forwarded
* through the fallback function, which will delegateCall this address.
*
* This address should be the same for every strategy, never be adjusted
* and always be checked before any integration with the Strategy.
*/
address public constant tokenizedStrategyAddress =
0xD377919FA87120584B21279a491F82D5265A139c;
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
/**
* @dev Underlying asset the Strategy is earning yield on.
* Stored here for cheap retrievals within the strategy.
*/
ERC20 internal immutable asset;
/**
* @dev This variable is set to address(this) during initialization of each strategy.
*
* This can be used to retrieve storage data within the strategy
* contract as if it were a linked library.
*
* i.e. uint256 totalAssets = TokenizedStrategy.totalAssets()
*
* Using address(this) will mean any calls using this variable will lead
* to a call to itself. Which will hit the fallback function and
* delegateCall that to the actual TokenizedStrategy.
*/
ITokenizedStrategy internal immutable TokenizedStrategy;
/**
* @notice Used to initialize the strategy on deployment.
*
* This will set the `TokenizedStrategy` variable for easy
* internal view calls to the implementation. As well as
* initializing the default storage variables based on the
* parameters and using the deployer for the permissioned roles.
*
* @param _asset Address of the underlying asset.
* @param _name Name the strategy will use.
*/
constructor(address _asset, string memory _name) {
asset = ERC20(_asset);
// Set instance of the implementation for internal use.
TokenizedStrategy = ITokenizedStrategy(address(this));
// Initialize the strategy's storage variables.
_delegateCall(
abi.encodeCall(
ITokenizedStrategy.initialize,
(_asset, _name, msg.sender, msg.sender, msg.sender)
)
);
// Store the tokenizedStrategyAddress at the standard implementation
// address storage slot so etherscan picks up the interface. This gets
// stored on initialization and never updated.
assembly {
sstore(
// keccak256('eip1967.proxy.implementation' - 1)
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
tokenizedStrategyAddress
)
}
}
/*//////////////////////////////////////////////////////////////
NEEDED TO BE OVERRIDDEN BY STRATEGIST
//////////////////////////////////////////////////////////////*/
/**
* @dev Can deploy up to '_amount' of 'asset' in the yield source.
*
* This function is called at the end of a {deposit} or {mint}
* call. Meaning that unless a whitelist is implemented it will
* be entirely permissionless and thus can be sandwiched or otherwise
* manipulated.
*
* @param _amount The amount of 'asset' that the strategy can attempt
* to deposit in the yield source.
*/
function _deployFunds(uint256 _amount) internal virtual;
/**
* @dev Should attempt to free the '_amount' of 'asset'.
*
* NOTE: The amount of 'asset' that is already loose has already
* been accounted for.
*
* This function is called during {withdraw} and {redeem} calls.
* Meaning that unless a whitelist is implemented it will be
* entirely permissionless and thus can be sandwiched or otherwise
* manipulated.
*
* Should not rely on asset.balanceOf(address(this)) calls other than
* for diff accounting purposes.
*
* Any difference between `_amount` and what is actually freed will be
* counted as a loss and passed on to the withdrawer. This means
* care should be taken in times of illiquidity. It may be better to revert
* if withdraws are simply illiquid so not to realize incorrect losses.
*
* @param _amount, The amount of 'asset' to be freed.
*/
function _freeFunds(uint256 _amount) internal virtual;
/**
* @dev Internal function to harvest all rewards, redeploy any idle
* funds and return an accurate accounting of all funds currently
* held by the Strategy.
*
* This should do any needed harvesting, rewards selling, accrual,
* redepositing etc. to get the most accurate view of current assets.
*
* NOTE: All applicable assets including loose assets should be
* accounted for in this function.
*
* Care should be taken when relying on oracles or swap values rather
* than actual amounts as all Strategy profit/loss accounting will
* be done based on this returned value.
*
* This can still be called post a shutdown, a strategist can check
* `TokenizedStrategy.isShutdown()` to decide if funds should be
* redeployed or simply realize any profits/losses.
*
* @return _totalAssets A trusted and accurate account for the total
* amount of 'asset' the strategy currently holds including idle funds.
*/
function _harvestAndReport()
internal
virtual
returns (uint256 _totalAssets);
/*//////////////////////////////////////////////////////////////
OPTIONAL TO OVERRIDE BY STRATEGIST
//////////////////////////////////////////////////////////////*/
/**
* @dev Optional function for strategist to override that can
* be called in between reports.
*
* If '_tend' is used tendTrigger() will also need to be overridden.
*
* This call can only be called by a permissioned role so may be
* through protected relays.
*
* This can be used to harvest and compound rewards, deposit idle funds,
* perform needed position maintenance or anything else that doesn't need
* a full report for.
*
* EX: A strategy that can not deposit funds without getting
* sandwiched can use the tend when a certain threshold
* of idle to totalAssets has been reached.
*
* This will have no effect on PPS of the strategy till report() is called.
*
* @param _totalIdle The current amount of idle funds that are available to deploy.
*/
function _tend(uint256 _totalIdle) internal virtual {}
/**
* @dev Optional trigger to override if tend() will be used by the strategy.
* This must be implemented if the strategy hopes to invoke _tend().
*
* @return . Should return true if tend() should be called by keeper or false if not.
*/
function _tendTrigger() internal view virtual returns (bool) {
return false;
}
/**
* @notice Returns if tend() should be called by a keeper.
*
* @return . Should return true if tend() should be called by keeper or false if not.
* @return . Calldata for the tend call.
*/
function tendTrigger() external view virtual returns (bool, bytes memory) {
return (
// Return the status of the tend trigger.
_tendTrigger(),
// And the needed calldata either way.
abi.encodeWithSelector(ITokenizedStrategy.tend.selector)
);
}
/**
* @notice Gets the max amount of `asset` that an address can deposit.
* @dev Defaults to an unlimited amount for any address. But can
* be overridden by strategists.
*
* This function will be called before any deposit or mints to enforce
* any limits desired by the strategist. This can be used for either a
* traditional deposit limit or for implementing a whitelist etc.
*
* EX:
* if(isAllowed[_owner]) return super.availableDepositLimit(_owner);
*
* This does not need to take into account any conversion rates
* from shares to assets. But should know that any non max uint256
* amounts may be converted to shares. So it is recommended to keep
* custom amounts low enough as not to cause overflow when multiplied
* by `totalSupply`.
*
* @param . The address that is depositing into the strategy.
* @return . The available amount the `_owner` can deposit in terms of `asset`
*/
function availableDepositLimit(
address /*_owner*/
) public view virtual returns (uint256) {
return type(uint256).max;
}
/**
* @notice Gets the max amount of `asset` that can be withdrawn.
* @dev Defaults to an unlimited amount for any address. But can
* be overridden by strategists.
*
* This function will be called before any withdraw or redeem to enforce
* any limits desired by the strategist. This can be used for illiquid
* or sandwichable strategies. It should never be lower than `totalIdle`.
*
* EX:
* return TokenIzedStrategy.totalIdle();
*
* This does not need to take into account the `_owner`'s share balance
* or conversion rates from shares to assets.
*
* @param . The address that is withdrawing from the strategy.
* @return . The available amount that can be withdrawn in terms of `asset`
*/
function availableWithdrawLimit(
address /*_owner*/
) public view virtual returns (uint256) {
return type(uint256).max;
}
/**
* @dev Optional function for a strategist to override that will
* allow management to manually withdraw deployed funds from the
* yield source if a strategy is shutdown.
*
* This should attempt to free `_amount`, noting that `_amount` may
* be more than is currently deployed.
*
* NOTE: This will not realize any profits or losses. A separate
* {report} will be needed in order to record any profit/loss. If
* a report may need to be called after a shutdown it is important
* to check if the strategy is shutdown during {_harvestAndReport}
* so that it does not simply re-deploy all funds that had been freed.
*
* EX:
* if(freeAsset > 0 && !TokenizedStrategy.isShutdown()) {
* depositFunds...
* }
*
* @param _amount The amount of asset to attempt to free.
*/
function _emergencyWithdraw(uint256 _amount) internal virtual {}
/*//////////////////////////////////////////////////////////////
TokenizedStrategy HOOKS
//////////////////////////////////////////////////////////////*/
/**
* @notice Can deploy up to '_amount' of 'asset' in yield source.
* @dev Callback for the TokenizedStrategy to call during a {deposit}
* or {mint} to tell the strategy it can deploy funds.
*
* Since this can only be called after a {deposit} or {mint}
* delegateCall to the TokenizedStrategy msg.sender == address(this).
*
* Unless a whitelist is implemented this will be entirely permissionless
* and thus can be sandwiched or otherwise manipulated.
*
* @param _amount The amount of 'asset' that the strategy can
* attempt to deposit in the yield source.
*/
function deployFunds(uint256 _amount) external virtual onlySelf {
_deployFunds(_amount);
}
/**
* @notice Should attempt to free the '_amount' of 'asset'.
* @dev Callback for the TokenizedStrategy to call during a withdraw
* or redeem to free the needed funds to service the withdraw.
*
* This can only be called after a 'withdraw' or 'redeem' delegateCall
* to the TokenizedStrategy so msg.sender == address(this).
*
* @param _amount The amount of 'asset' that the strategy should attempt to free up.
*/
function freeFunds(uint256 _amount) external virtual onlySelf {
_freeFunds(_amount);
}
/**
* @notice Returns the accurate amount of all funds currently
* held by the Strategy.
* @dev Callback for the TokenizedStrategy to call during a report to
* get an accurate accounting of assets the strategy controls.
*
* This can only be called after a report() delegateCall to the
* TokenizedStrategy so msg.sender == address(this).
*
* @return . A trusted and accurate account for the total amount
* of 'asset' the strategy currently holds including idle funds.
*/
function harvestAndReport() external virtual onlySelf returns (uint256) {
return _harvestAndReport();
}
/**
* @notice Will call the internal '_tend' when a keeper tends the strategy.
* @dev Callback for the TokenizedStrategy to initiate a _tend call in the strategy.
*
* This can only be called after a tend() delegateCall to the TokenizedStrategy
* so msg.sender == address(this).
*
* We name the function `tendThis` so that `tend` calls are forwarded to
* the TokenizedStrategy.
* @param _totalIdle The amount of current idle funds that can be
* deployed during the tend
*/
function tendThis(uint256 _totalIdle) external virtual onlySelf {
_tend(_totalIdle);
}
/**
* @notice Will call the internal '_emergencyWithdraw' function.
* @dev Callback for the TokenizedStrategy during an emergency withdraw.
*
* This can only be called after a emergencyWithdraw() delegateCall to
* the TokenizedStrategy so msg.sender == address(this).
*
* We name the function `shutdownWithdraw` so that `emergencyWithdraw`
* calls are forwarded to the TokenizedStrategy.
*
* @param _amount The amount of asset to attempt to free.
*/
function shutdownWithdraw(uint256 _amount) external virtual onlySelf {
_emergencyWithdraw(_amount);
}
/**
* @dev Function used to delegate call the TokenizedStrategy with
* certain `_calldata` and return any return values.
*
* This is used to setup the initial storage of the strategy, and
* can be used by strategist to forward any other call to the
* TokenizedStrategy implementation.
*
* @param _calldata The abi encoded calldata to use in delegatecall.
* @return . The return value if the call was successful in bytes.
*/
function _delegateCall(
bytes memory _calldata
) internal returns (bytes memory) {
// Delegate call the tokenized strategy with provided calldata.
(bool success, bytes memory result) = tokenizedStrategyAddress
.delegatecall(_calldata);
// If the call reverted. Return the error.
if (!success) {
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
// Return the result.
return result;
}
/**
* @dev Execute a function on the TokenizedStrategy and return any value.
*
* This fallback function will be executed when any of the standard functions
* defined in the TokenizedStrategy are called since they wont be defined in
* this contract.
*
* It will delegatecall the TokenizedStrategy implementation with the exact
* calldata and return any relevant values.
*
*/
fallback() external {
// load our target address
address _tokenizedStrategyAddress = tokenizedStrategyAddress;
// Execute external function using delegatecall and return any value.
assembly {
// Copy function selector and any arguments.
calldatacopy(0, 0, calldatasize())
// Execute function delegatecall.
let result := delegatecall(
gas(),
_tokenizedStrategyAddress,
0,
calldatasize(),
0,
0
)
// Get any return value
returndatacopy(0, 0, returndatasize())
// Return any return value or error back to the caller
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library exposing error messages.
library ErrorsLib {
/// @notice Thrown when the caller is not the owner.
string internal constant NOT_OWNER = "not owner";
/// @notice Thrown when the LLTV to enable exceeds the maximum LLTV.
string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded";
/// @notice Thrown when the fee to set exceeds the maximum fee.
string internal constant MAX_FEE_EXCEEDED = "max fee exceeded";
/// @notice Thrown when the value is already set.
string internal constant ALREADY_SET = "already set";
/// @notice Thrown when the IRM is not enabled at market creation.
string internal constant IRM_NOT_ENABLED = "IRM not enabled";
/// @notice Thrown when the LLTV is not enabled at market creation.
string internal constant LLTV_NOT_ENABLED = "LLTV not enabled";
/// @notice Thrown when the market is already created.
string internal constant MARKET_ALREADY_CREATED = "market already created";
/// @notice Thrown when a token to transfer doesn't have code.
string internal constant NO_CODE = "no code";
/// @notice Thrown when the market is not created.
string internal constant MARKET_NOT_CREATED = "market not created";
/// @notice Thrown when not exactly one of the input amount is zero.
string internal constant INCONSISTENT_INPUT = "inconsistent input";
/// @notice Thrown when zero assets is passed as input.
string internal constant ZERO_ASSETS = "zero assets";
/// @notice Thrown when a zero address is passed as input.
string internal constant ZERO_ADDRESS = "zero address";
/// @notice Thrown when the caller is not authorized to conduct an action.
string internal constant UNAUTHORIZED = "unauthorized";
/// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`.
string internal constant INSUFFICIENT_COLLATERAL =
"insufficient collateral";
/// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`.
string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity";
/// @notice Thrown when the position to liquidate is healthy.
string internal constant HEALTHY_POSITION = "position is healthy";
/// @notice Thrown when the authorization signature is invalid.
string internal constant INVALID_SIGNATURE = "invalid signature";
/// @notice Thrown when the authorization signature is expired.
string internal constant SIGNATURE_EXPIRED = "signature expired";
/// @notice Thrown when the nonce is invalid.
string internal constant INVALID_NONCE = "invalid nonce";
/// @notice Thrown when a token transfer reverted.
string internal constant TRANSFER_REVERTED = "transfer reverted";
/// @notice Thrown when a token transfer returned false.
string internal constant TRANSFER_RETURNED_FALSE =
"transfer returned false";
/// @notice Thrown when a token transferFrom reverted.
string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted";
/// @notice Thrown when a token transferFrom returned false
string internal constant TRANSFER_FROM_RETURNED_FALSE =
"transferFrom returned false";
/// @notice Thrown when the maximum uint128 is exceeded.
string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded";
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id} from "../../../interfaces/morpho/IMorpho.sol";
/// @title MorphoStorageLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library exposing getters to access Morpho storage variables' slot.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
library MorphoStorageLib {
/* SLOTS */
uint256 internal constant OWNER_SLOT = 0;
uint256 internal constant FEE_RECIPIENT_SLOT = 1;
uint256 internal constant POSITION_SLOT = 2;
uint256 internal constant MARKET_SLOT = 3;
uint256 internal constant IS_IRM_ENABLED_SLOT = 4;
uint256 internal constant IS_LLTV_ENABLED_SLOT = 5;
uint256 internal constant IS_AUTHORIZED_SLOT = 6;
uint256 internal constant NONCE_SLOT = 7;
uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 8;
/* SLOT OFFSETS */
uint256 internal constant LOAN_TOKEN_OFFSET = 0;
uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1;
uint256 internal constant ORACLE_OFFSET = 2;
uint256 internal constant IRM_OFFSET = 3;
uint256 internal constant LLTV_OFFSET = 4;
uint256 internal constant SUPPLY_SHARES_OFFSET = 0;
uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1;
uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0;
uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1;
uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2;
/* GETTERS */
function ownerSlot() internal pure returns (bytes32) {
return bytes32(OWNER_SLOT);
}
function feeRecipientSlot() internal pure returns (bytes32) {
return bytes32(FEE_RECIPIENT_SLOT);
}
function positionSupplySharesSlot(
Id id,
address user
) internal pure returns (bytes32) {
return
bytes32(
uint256(
keccak256(
abi.encode(
user,
keccak256(abi.encode(id, POSITION_SLOT))
)
)
) + SUPPLY_SHARES_OFFSET
);
}
function positionBorrowSharesAndCollateralSlot(
Id id,
address user
) internal pure returns (bytes32) {
return
bytes32(
uint256(
keccak256(
abi.encode(
user,
keccak256(abi.encode(id, POSITION_SLOT))
)
)
) + BORROW_SHARES_AND_COLLATERAL_OFFSET
);
}
function marketTotalSupplyAssetsAndSharesSlot(
Id id
) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(id, MARKET_SLOT))) +
TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET
);
}
function marketTotalBorrowAssetsAndSharesSlot(
Id id
) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(id, MARKET_SLOT))) +
TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET
);
}
function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(id, MARKET_SLOT))) +
LAST_UPDATE_AND_FEE_OFFSET
);
}
function isIrmEnabledSlot(address irm) internal pure returns (bytes32) {
return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT));
}
function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) {
return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT));
}
function isAuthorizedSlot(
address authorizer,
address authorizee
) internal pure returns (bytes32) {
return
keccak256(
abi.encode(
authorizee,
keccak256(abi.encode(authorizer, IS_AUTHORIZED_SLOT))
)
);
}
function nonceSlot(address authorizer) internal pure returns (bytes32) {
return keccak256(abi.encode(authorizer, NONCE_SLOT));
}
function idToLoanTokenSlot(Id id) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) +
LOAN_TOKEN_OFFSET
);
}
function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) +
COLLATERAL_TOKEN_OFFSET
);
}
function idToOracleSlot(Id id) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) +
ORACLE_OFFSET
);
}
function idToIrmSlot(Id id) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) +
IRM_OFFSET
);
}
function idToLltvSlot(Id id) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) +
LLTV_OFFSET
);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Callback for IUniswapV3PoolActions#swap
/// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface
interface IUniswapV3SwapCallback {
/// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap.
/// @dev In the implementation you must pay the pool tokens owed for the swap.
/// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory.
/// amount0Delta and amount1Delta can both be 0 if no tokens were swapped.
/// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token0 to the pool.
/// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by
/// the end of the swap. If positive, the callback must send that amount of token1 to the pool.
/// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata data
) external;
}{
"remappings": [
"@openzeppelin/=lib/openzeppelin-contracts/",
"forge-std/=lib/forge-std/src/",
"@tokenized-strategy/=lib/tokenized-strategy/src/",
"@periphery/=lib/tokenized-strategy-periphery/src/",
"@yearn-vaults/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/tokenized-strategy/lib/erc4626-tests/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/openzeppelin-contracts/contracts/",
"tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/",
"tokenized-strategy/=lib/tokenized-strategy/",
"yearn-vaults-v3/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_management","type":"address"},{"internalType":"address","name":"_performanceFeeRecipient","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_emergencyAdmin","type":"address"},{"internalType":"address","name":"_gov","type":"address"},{"internalType":"address","name":"_morpho","type":"address"},{"internalType":"address","name":"_router","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"strategy","type":"address"},{"indexed":true,"internalType":"Id","name":"marketId","type":"bytes32"}],"name":"NewStrategy","type":"event"},{"inputs":[],"name":"GOV","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Id","name":"","type":"bytes32"}],"name":"deployments","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"management","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morpho","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_borrowToken","type":"address"},{"internalType":"address","name":"_lenderVault","type":"address"},{"internalType":"Id","name":"_marketId","type":"bytes32"},{"internalType":"address","name":"_borrowUsdOracle","type":"address"}],"name":"newStrategy","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"performanceFeeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"router","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_management","type":"address"},{"internalType":"address","name":"_performanceFeeRecipient","type":"address"},{"internalType":"address","name":"_keeper","type":"address"},{"internalType":"address","name":"_emergencyAdmin","type":"address"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60e060405234801561000f575f80fd5b5060405161616238038061616283398101604081905261002e916100bf565b6001600160a01b038316610040575f80fd5b5f80546001600160a01b03199081166001600160a01b03998a16179091556001805482169789169790971790965560028054871695881695909517909455600380549095169286169290921790935591831660805290821660a0521660c052610140565b80516001600160a01b03811681146100ba575f80fd5b919050565b5f805f805f805f60e0888a0312156100d5575f80fd5b6100de886100a4565b96506100ec602089016100a4565b95506100fa604089016100a4565b9450610108606089016100a4565b9350610116608089016100a4565b925061012460a089016100a4565b915061013260c089016100a4565b905092959891949750929550565b60805160a05160c051615fe461017e5f395f81816101c8015261025701525f818161018c015261023401525f818160e501526102130152615fe45ff3fe608060405234801562000010575f80fd5b5060043610620000a8575f3560e01c806388a8d602116200006b57806388a8d602146200015f578063aced16611462000172578063d8fbc8331462000186578063ed27f7c914620001ae578063f887ea4014620001c2575f80fd5b80630b18f8a514620000ac578063180cb47f14620000df5780633740401714620001075780634a945f8d146200013257806370905dce146200014b575b5f80fd5b620000c3620000bd36600462000552565b620001ea565b6040516001600160a01b03909116815260200160405180910390f35b620000c37f000000000000000000000000000000000000000000000000000000000000000081565b620000c36200011836600462000615565b60046020525f90815260409020546001600160a01b031681565b62000149620001433660046200062d565b6200048d565b005b600354620000c3906001600160a01b031681565b5f54620000c3906001600160a01b031681565b600254620000c3906001600160a01b031681565b620000c37f000000000000000000000000000000000000000000000000000000000000000081565b600154620000c3906001600160a01b031681565b620000c37f000000000000000000000000000000000000000000000000000000000000000081565b5f828152600460205260408120546001600160a01b0316156200020b575f80fd5b5f88888888887f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008a8a7f0000000000000000000000000000000000000000000000000000000000000000604051620002849062000528565b620002999a9998979695949392919062000687565b604051809103905ff080158015620002b3573d5f803e3d5ffd5b5060015460405163352f8d5160e11b81526001600160a01b039182166004820152919250821690636a5f1aa2906024015f604051808303815f87803b158015620002fb575f80fd5b505af11580156200030e573d5f803e3d5ffd5b5050600254604051633a43a3f360e11b81526001600160a01b039182166004820152908416925063748747e691506024015f604051808303815f87803b15801562000357575f80fd5b505af11580156200036a573d5f803e3d5ffd5b50505f54604051630f629b7960e41b81526001600160a01b039182166004820152908416925063f629b79091506024015f604051808303815f87803b158015620003b2575f80fd5b505af1158015620003c5573d5f803e3d5ffd5b5050600354604051630d768ce560e21b81526001600160a01b03918216600482015290841692506335da339491506024015f604051808303815f87803b1580156200040e575f80fd5b505af115801562000421573d5f803e3d5ffd5b50506040518692506001600160a01b03841691507f0df9ebba034d173a99a9f19984c4f6b69c45f86fdf65be5234f4040127fb1b17905f90a35f9384526004602052604090932080546001600160a01b0319166001600160a01b03851617905550909695505050505050565b5f546001600160a01b03163314620004d95760405162461bcd60e51b815260206004820152600b60248201526a085b585b9859d95b595b9d60aa1b604482015260640160405180910390fd5b5f80546001600160a01b039586166001600160a01b0319918216179091556001805494861694821694909417909355600280549285169284169290921790915560038054919093169116179055565b6158a7806200070883390190565b80356001600160a01b03811681146200054d575f80fd5b919050565b5f805f805f805f60c0888a03121562000569575f80fd5b620005748862000536565b9650602088013567ffffffffffffffff8082111562000591575f80fd5b818a0191508a601f830112620005a5575f80fd5b813581811115620005b4575f80fd5b8b6020828501011115620005c6575f80fd5b602083019850809750505050620005e06040890162000536565b9350620005f06060890162000536565b9250608088013591506200060760a0890162000536565b905092959891949750929550565b5f6020828403121562000626575f80fd5b5035919050565b5f805f806080858703121562000641575f80fd5b6200064c8562000536565b93506200065c6020860162000536565b92506200066c6040860162000536565b91506200067c6060860162000536565b905092959194509250565b6001600160a01b038b811682526101206020830181905282018a90525f90610140908b8d838601375f848d01830152998a16604084015297891660608301525094871660808601529290951660a084015260c08301526001600160a01b0393841660e0830152909216610100830152601f909201601f191601019291505056fe6101a06040525f80546227100162ffffff19909116179055600580546001600160a01b031990811673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2179091556006805490911673e592427a0aece92de3edee1f18e0157c058615641790553480156200006b575f80fd5b50604051620058a7380380620058a78339810160408190526200008e9162000a73565b6001600160a01b0389166080523060a052604051899089908990899084908490829082906200010390620000cf908490849033908190819060240162000bc4565b60408051601f198184030181529190526020810180516001600160e01b03908116634b839d7360e11b179091526200056716565b505073d377919fa87120584b21279a491f82d5265a139c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc555050506001600160a01b0382811660c0525f196001555f8054642e90edd000600255600160281b600160901b0319166b01f41f401b580000000000001790558116156200026e57806001600160a01b031660e0816001600160a01b031681525050816001600160a01b031660e0516001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001e6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200020c919062000c06565b6001600160a01b031614620002575760405162461bcd60e51b815260206004820152600c60248201526b085b195b99195c95985d5b1d60a21b60448201526064015b60405180910390fd5b6200026e6001600160a01b038316825f19620005f6565b505050506001600160a01b03858116610140528416610100819052610120849052604051632c3c915760e01b815260048101859052632c3c91579060240160a060405180830381865afa158015620002c8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620002ee919062000c22565b8051600880546001600160a01b03199081166001600160a01b0393841690811790925560208401516009805483169185169190911790556040840151600a805483169185169190911790556060840151600b8054909216908416179055608090920151600c5588161480156200037157506009546001600160a01b038a81169116145b6200037a575f80fd5b886001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003b7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620003dd919062000cb5565b620003ea90600a62000de4565b6101608181525050866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200042f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000455919062000cb5565b6200046290600a62000de4565b610180526200047d6001600160a01b038a16855f19620005f6565b620004946001600160a01b038816855f19620005f6565b620004a0612710600455565b8060065f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000503573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000529919062000cb5565b60ff1660081462000538575f80fd5b50600d80546001600160a01b0319166001600160a01b03929092169190911790555062000e4695505050505050565b60605f8073d377919fa87120584b21279a491f82d5265a139c6001600160a01b03168460405162000599919062000df4565b5f60405180830381855af49150503d805f8114620005d3576040519150601f19603f3d011682016040523d82523d5f602084013e620005d8565b606091505b509150915081620005ef576040513d805f833e8082fd5b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152620006509085908390620006cb16565b620006c5576040516001600160a01b03841660248201525f6044820152620006b990859063095ea7b360e01b9060640160408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b03938416179052906200077716565b620006c5848262000777565b50505050565b5f805f846001600160a01b031684604051620006e8919062000df4565b5f604051808303815f865af19150503d805f811462000723576040519150601f19603f3d011682016040523d82523d5f602084013e62000728565b606091505b5091509150818015620007565750805115806200075657508080602001905181019062000756919062000e11565b80156200076c57506001600160a01b0385163b15155b925050505b92915050565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201525f90620007c5906001600160a01b0385169084906200084e565b905080515f1480620007e8575080806020019051810190620007e8919062000e11565b620008495760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016200024e565b505050565b60606200085e84845f8562000866565b949350505050565b606082471015620008c95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016200024e565b5f80866001600160a01b03168587604051620008e6919062000df4565b5f6040518083038185875af1925050503d805f811462000922576040519150601f19603f3d011682016040523d82523d5f602084013e62000927565b606091505b5090925090506200093b8783838762000946565b979650505050505050565b60608315620009b95782515f03620009b1576001600160a01b0385163b620009b15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200024e565b50816200085e565b6200085e8383815115620009d05781518083602001fd5b8060405162461bcd60e51b81526004016200024e919062000e32565b80516001600160a01b038116811462000a03575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171562000a475762000a4762000a08565b604052919050565b5f5b8381101562000a6b57818101518382015260200162000a51565b50505f910152565b5f805f805f805f805f6101208a8c03121562000a8d575f80fd5b62000a988a620009ec565b60208b01519099506001600160401b038082111562000ab5575f80fd5b818c0191508c601f83011262000ac9575f80fd5b81518181111562000ade5762000ade62000a08565b62000af3601f8201601f191660200162000a1c565b91508082528d602082850101111562000b0a575f80fd5b62000b1d81602084016020860162000a4f565b50985062000b30905060408b01620009ec565b965062000b4060608b01620009ec565b955062000b5060808b01620009ec565b945062000b6060a08b01620009ec565b935060c08a0151925062000b7760e08b01620009ec565b915062000b886101008b01620009ec565b90509295985092959850929598565b5f815180845262000bb081602086016020860162000a4f565b601f01601f19169290920160200192915050565b5f60018060a01b03808816835260a0602084015262000be760a084018862000b97565b9581166040840152938416606083015250911660809091015292915050565b5f6020828403121562000c17575f80fd5b620005ef82620009ec565b5f60a0828403121562000c33575f80fd5b60405160a081016001600160401b038111828210171562000c585762000c5862000a08565b60405262000c6683620009ec565b815262000c7660208401620009ec565b602082015262000c8960408401620009ec565b604082015262000c9c60608401620009ec565b6060820152608083015160808201528091505092915050565b5f6020828403121562000cc6575f80fd5b815160ff81168114620005ef575f80fd5b634e487b7160e01b5f52601160045260245ffd5b600181815b8085111562000d2b57815f190482111562000d0f5762000d0f62000cd7565b8085161562000d1d57918102915b93841c939080029062000cf0565b509250929050565b5f8262000d435750600162000771565b8162000d5157505f62000771565b816001811462000d6a576002811462000d755762000d95565b600191505062000771565b60ff84111562000d895762000d8962000cd7565b50506001821b62000771565b5060208310610133831016604e8410600b841016171562000dba575081810a62000771565b62000dc6838362000ceb565b805f190482111562000ddc5762000ddc62000cd7565b029392505050565b5f620005ef60ff84168362000d33565b5f825162000e0781846020870162000a4f565b9190910192915050565b5f6020828403121562000e22575f80fd5b81518015158114620005ef575f80fd5b602081525f620005ef602083018462000b97565b60805160a05160c05160e051610100516101205161014051610160516101805161482d6200107a5f395f6128d301525f6128f901525f8181610444015281816108a90152610a0801525f8181610612015281816112f901528181612f66015261322c01525f818161079801528181610fb00152818161133101528181611e730152818161248601528181612f9e015281816131c30152818161320a0152818161326b01528181613a000152613a5b01525f81816103f10152818161098501528181610df501528181611ba001528181611dee015281816122de0152818161235c015281816123f30152613af401525f81816105160152818161094701528181610b18015281816110d5015281816112760152818161149f0152818161192a015281816121ff015281816126250152818161266c015281816127c101528181612e3f01528181612ec101528181612f2c01528181613578015281816138c30152818161394e01526139b901525f8181610ba401528181610ca701528181610d2301528181610ee601528181610fee0152818161115c015281816113c20152818161143f0152818161154f015281816115cb01528181611657015281816116dd01528181611758015281816117ee015281816118690152818161197801528181611a4e01528181611f2b015261207201525f818161090a01528181610b3e01528181611503015281816118f001528181612109015281816126460152818161268d015281816128020152613998015261482d5ff3fe608060405234801561000f575f80fd5b5060043610610376575f3560e01c80636ed71ede116101d1578063bdc8144b11610102578063ebf27802116100a0578063f0fa55a91161007a578063f0fa55a914610822578063f3ce280a14610835578063f887ea4014610878578063fde813a81461088b57610376565b8063ebf27802146107f3578063ecf7085814610806578063ee8bfa311461080f57610376565b8063d8fbc833116100dc578063d8fbc83314610793578063da769c47146107ba578063e4d746fb146107cf578063e8621149146107eb57610376565b8063bdc8144b14610752578063d19a3bb814610765578063d69686011461078057610376565b8063950b3d731161016f578063ac00ff2611610149578063ac00ff261461071b578063b1ea668d1461072e578063b2a1913714610736578063b6a165061461074957610376565b8063950b3d73146106e25780639b90fb16146106f55780639d7fb70c1461070857610376565b8063797bf343116101ab578063797bf3431461064f5780637b9e68f2146106575780637d969932146106bc5780638e72e31b146106ce57610376565b80636ed71ede1461060d578063702651db14610634578063735fd1891461063c57610376565b80633e032a3b116102ab57806355b90362116102495780636353f822116102235780636353f822146105ba5780636687500e146105e15780636718835f146105e95780636be36a1d1461060557610376565b806355b90362146105895780635bc34efe1461059c5780635d265d3f146105a457610376565b806349317f1d1161028557806349317f1d1461054b5780634a5d0943146105535780635001f3b514610563578063503160d91461057657610376565b80633e032a3b146104de578063456dc17a1461051157806346aa2f121461053857610376565b8063278b73121161031857806331b7f9b2116102f257806331b7f9b2146104a857806338dbd359146104b0578063392f7a70146104c35780633d6cb575146104cb57610376565b8063278b73121461046f5780632d49be91146104825780632e3ecf2c1461049557610376565b806306876d191161035457806306876d19146103ec578063085c884f1461042b578063180cb47f1461043f578063181443671461046657610376565b806301681a62146103ab57806302402ae0146103be57806304bd4629146103d9575b73d377919fa87120584b21279a491f82d5265a139c365f80375f80365f845af43d5f803e8080156103a5573d5ff35b3d5ffd5b005b6103a96103b936600461406a565b61089e565b6103c6610a8f565b6040519081526020015b60405180910390f35b6103c66103e736600461406a565b610acd565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103d0565b6103c6610439366004614083565b50600190565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6103c660045481565b6103a961047d36600461406a565b610b8f565b6103a9610490366004614083565b610c92565b6103a96104a336600461409a565b610d0e565b6103c6610dde565b6103a96104be3660046140f2565b610ed1565b6103c6610f66565b6103a96104d9366004614083565b610fd9565b5f546104f890600160501b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103d0565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6103c661054636600461406a565b610fea565b6103c6611117565b5f54610100900461ffff166103c6565b600554610413906001600160a01b031681565b6103a9610584366004614083565b611136565b6103a961059736600461411e565b611147565b6103c661125f565b6105ac6112ad565b6040516103d092919061419c565b5f546105ce90600160401b900461ffff1681565b60405161ffff90911681526020016103d0565b6103c66112ea565b5f546105f59060ff1681565b60405190151581526020016103d0565b600c546103c6565b6103c67f000000000000000000000000000000000000000000000000000000000000000081565b6103a96113ad565b6103a961064a3660046141b6565b61142a565b6103c66114ec565b600854600954600a54600b54600c54610681946001600160a01b0390811694811693811692169085565b604080516001600160a01b0396871681529486166020860152928516928401929092529092166060820152608081019190915260a0016103d0565b5f546301000000900461ffff166103c6565b5f546105ce90600160301b900461ffff1681565b6103a96106f0366004614083565b61153a565b6103a9610703366004614083565b6115b6565b6103a9610716366004614083565b611631565b6103a96107293660046140f2565b611642565b6103a96116c8565b6103a961074436600461406a565b611743565b6103c660025481565b6103a9610760366004614083565b6117d9565b61041373d377919fa87120584b21279a491f82d5265a139c81565b6103a961078e366004614083565b611854565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b5f546105f59065010000000000900460ff1681565b6103c66107dd366004614083565b5068056bc75e2d6310000090565b6103c66118d0565b6103a9610801366004614083565b611963565b6103c660015481565b600d54610413906001600160a01b031681565b6103a9610830366004614083565b611a39565b6108646108433660046141de565b600760209081525f928352604080842090915290825290205462ffffff1681565b60405162ffffff90911681526020016103d0565b600654610413906001600160a01b031681565b6103a9610899366004614083565b611b15565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109085760405162461bcd60e51b81526004016108ff9060208082526004908201526310b3b7bb60e11b604082015260600190565b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b03161415801561097c57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b80156109ba57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b6109ef5760405162461bcd60e51b815260206004820152600660248201526502173776565760d41b60448201526064016108ff565b6040516370a0823160e01b8152306004820152610a8c907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b038416906370a0823190602401602060405180830381865afa158015610a57573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a7b9190614206565b6001600160a01b0384169190611b26565b50565b5f80610a9961125f565b610aa1610dde565b610aab9190614231565b90505f610ab6610f66565b9050808210610ac7575f9250505090565b03919050565b5f80610ad76112ea565b610ae2906001614231565b90505f610aed611b89565b9050610af7610dde565b811015610b7457610b06611bdf565b670de0b6b3a7640000610b62610b3c847f0000000000000000000000000000000000000000000000000000000000000000611c0a565b7f0000000000000000000000000000000000000000000000000000000000000000611ca1565b0281610b7057610b70614244565b0491505b81610b7d6114ec565b610b879190614231565b949350505050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610bec575f80fd5b505afa158015610bfe573d5f803e3d5ffd5b50505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c3e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c629190614258565b60ff16600814610c70575f80fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610cef575f80fd5b505afa158015610d01573d5f803e3d5ffd5b50505050610a8c81600455565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610d6b575f80fd5b505afa158015610d7d573d5f803e3d5ffd5b5050506001600160a01b038085165f8181526007602081815260408084209589168452948152848320805462ffffff1990811662ffffff8a16908117909255928252858420948452939052929020805490921617905550505050565b505050565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a9082906370a08231906024015b602060405180830381865afa158015610e4b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e6f9190614206565b6040518263ffffffff1660e01b8152600401610e8d91815260200190565b602060405180830381865afa158015610ea8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ecc9190614206565b905090565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610f2e575f80fd5b505afa158015610f40573d5f803e3d5ffd5b50505f8054931515650100000000000265ff000000000019909416939093179092555050565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b5482166060820152600c5460808201525f91610ecc917f00000000000000000000000000000000000000000000000000000000000000009091169030611d2e565b610fe1611d82565b610a8c81611db9565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611048573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061106c9190614206565b90505f816001541161107e575f61108c565b8160015461108c9190614278565b90505f61109a5f1983611dc2565b90505f6110b56110a8611dd7565b6110b0611e25565b611dc2565b905061110d826110b06110c6611bdf565b670de0b6b3a76400006110f9867f0000000000000000000000000000000000000000000000000000000000000000611c0a565b611103919061428b565b610b3c91906142a2565b9695505050505050565b5f611120611d82565b611128611ebf565b905061113381611f10565b90565b61113e611d82565b610a8c81612053565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156111a4575f80fd5b505afa1580156111b6573d5f803e3d5ffd5b505050506123288161ffff16111580156111d757508061ffff168261ffff16105b80156111e6575061ffff821615155b6112205760405162461bcd60e51b815260206004820152600b60248201526a34b73b30b634b210262a2b60a91b60448201526064016108ff565b5f805469ffffffff0000000000001916600160301b61ffff9485160269ffff0000000000000000191617600160401b9290931691909102919091179055565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401610e8d565b5f60606112b861205c565b6040805160048152602481019091526020810180516001600160e01b031663440368a360e01b17905290939092509050565b6040516349e2903160e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906393c5206290604401606060405180830381865afa158015611376573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061139a919061431c565b604001516001600160801b031692915050565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b15801561140a575f80fd5b505afa15801561141c573d5f803e3d5ffd5b50505050611428612260565b565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b158015611487575f80fd5b505afa158015611499573d5f803e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036114e3576114df816122ba565b5050565b6114df81612465565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401610e8d565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611597575f80fd5b505afa1580156115a9573d5f803e3d5ffd5b50505050610a8c816124f2565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611613575f80fd5b505afa158015611625573d5f803e3d5ffd5b50505060029190915550565b611639611d82565b610a8c8161258b565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b15801561169f575f80fd5b505afa1580156116b1573d5f803e3d5ffd5b50505f805460ff1916931515939093179092555050565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b158015611725575f80fd5b505afa158015611737573d5f803e3d5ffd5b5050505061142861259b565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156117a0575f80fd5b505afa1580156117b2573d5f803e3d5ffd5b5050600580546001600160a01b0319166001600160a01b0394909416939093179092555050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611836575f80fd5b505afa158015611848573d5f803e3d5ffd5b50505060019190915550565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156118b1575f80fd5b505afa1580156118c3573d5f803e3d5ffd5b50505050610a8c816125b6565b5f806118da6112ea565b9050805f036118ea575f91505090565b611914817f0000000000000000000000000000000000000000000000000000000000000000611c0a565b670de0b6b3a764000061194e611928610f66565b7f0000000000000000000000000000000000000000000000000000000000000000611c0a565b028161195c5761195c614244565b0491505090565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b1580156119c0575f80fd5b505afa1580156119d2573d5f803e3d5ffd5b505050505f198103611a30575f6119e761125f565b90505f816119f3610dde565b6119fd9190614231565b90505f611a08610f66565b9050808211611a17575f611a2a565b611a2a611a248284614278565b84611dc2565b93505050505b610a8c81612616565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611a96575f80fd5b505afa158015611aa8573d5f803e3d5ffd5b505050506127108110611ae85760405162461bcd60e51b8152602060048201526008602482015267736c69707061676560c01b60448201526064016108ff565b5f805467ffffffffffffffff909216600160501b0267ffffffffffffffff60501b19909216919091179055565b611b1d611d82565b610a8c816126b6565b6040516001600160a01b038316602482015260448101829052610dd990849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526126eb565b604051636c82bbbf60e11b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a90829063d905777e90602401610e30565b5f8054600c5461271091600160301b900461ffff16905b0281611c0457611c04614244565b04905090565b5f825f03611c1957505f611c9b565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c55573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c799190614258565b60ff16600a0a611c88836127be565b840281611c9757611c97614244565b0490505b92915050565b5f825f03611cb057505f611c9b565b611cb9826127be565b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d199190614258565b60ff16600a0a840281611c9757611c97614244565b5f80611d3b8460a0902090565b90505f611d526001600160a01b038716838661293f565b90505f80611d6088886129f1565b9094509250611d7491508490508383612ce7565b9450505050505b9392505050565b3330146114285760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b60448201526064016108ff565b610a8c81612d0b565b5f818310611dd05781611d7b565b5090919050565b60405163402d267d60e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063402d267d90602401610e8d565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b5482166060820152600c5460808201525f9182918291611e9b917f0000000000000000000000000000000000000000000000000000000000000000909116906129f1565b509250509150808211611eae575f611eb8565b611eb88183614278565b9250505090565b5f611ec8612260565b611ee4611edf611ed66114ec565b6110b030610fea565b612dad565b611eec612f11565b611ef46112ea565b611efc6114ec565b611f069190614231565b610ecc9190614278565b5f5460ff16611f2857505f805460ff19166001179055565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fa99190614206565b90508082111561201c575f5461271090611fcc90610100900461ffff168361428b565b611fd691906142a2565b611fe08284614278565b11156114df5760405162461bcd60e51b815260206004820152600b60248201526a6865616c7468436865636b60a81b60448201526064016108ff565b818111156114df575f546127109061203f906301000000900461ffff168361428b565b61204991906142a2565b611fe08383614278565b610a8c81612dad565b5f612065612f57565b156120705750600190565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120cc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120f09190614206565b5f036120fb57505f90565b5f61212d6121076112ea565b7f0000000000000000000000000000000000000000000000000000000000000000611c0a565b90505f61213b611928610f66565b90505f80831161214b575f612168565b8261215e670de0b6b3a76400008461428b565b61216891906142a2565b9050612172613109565b811115612183576001935050505090565b5f61218c611bdf565b9050811580159061219a57505f5b156121ae5760025448111594505050505090565b80821080156121cd575067016345785d8a00006121cb8383614278565b115b15612256575f670de0b6b3a76400006121e6838761428b565b6121f091906142a2565b90508381035f61222e612223837f0000000000000000000000000000000000000000000000000000000000000000611ca1565b6110b06110a8611dd7565b9050805f03612244575f97505050505050505090565b60025448111597505050505050505090565b5f94505050505090565b5f61226961125f565b612271610dde565b61227b9190614231565b90505f612286610f66565b9050808211156114df575f61229b8284614278565b90506122a681613124565b610dd96122b5826110b061125f565b612616565b604051630a28a47760e01b8152600481018290525f906123cd906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630a28a47790602401602060405180830381865afa158015612323573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123479190614206565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156123a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b09190614206565b604051635d043b2960e11b815260048101829052306024820181905260448201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ba087652906064016020604051808303815f875af1158015612441573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd99190614206565b805f0361246f5750565b604051638720316d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638720316d906124c2906008908590309081906004016143bd565b5f604051808303815f87803b1580156124d9575f80fd5b505af11580156124eb573d5f803e3d5ffd5b5050505050565b5f81116125305760405162461bcd60e51b815260206004820152600c60248201526b085e995c9bc81c1c9bd99a5d60a21b60448201526064016108ff565b61ffff81111561256e5760405162461bcd60e51b8152602060048201526009602482015268042e8dede40d0d2ced60bb1b60448201526064016108ff565b5f805461ffff9092166101000262ffff0019909216919091179055565b610a8c611edf826110b030610fea565b6114286125b16125a961125f565b6110b0610f66565b61316c565b61271081106125f55760405162461bcd60e51b815260206004820152600b60248201526a085b1bdcdcc81b1a5b5a5d60aa1b60448201526064016108ff565b5f805461ffff90921663010000000264ffff00000019909216919091179055565b805f036126205750565b6114df7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000836126b1857f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006132e6565b613329565b6126c2816110b0611b89565b905080156126d3576126d3816122ba565b6126db61259b565b610a8c6126e661354d565b612465565b5f61273f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135da9092919063ffffffff16565b905080515f148061275f57508080602001905181019061275f91906143f1565b610dd95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108ff565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361280057611c9b6135e8565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031603612936575f6128426135e8565b90505f60086002015f9054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612898573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128bc9190614206565b90506128f76ec097ce7bc90715b34b9f10000000007f000000000000000000000000000000000000000000000000000000000000000061428b565b7f0000000000000000000000000000000000000000000000000000000000000000612922848461428b565b61292c919061428b565b610b8791906142a2565b5f80fd5b919050565b5f8061295361294e8585613693565b613706565b604051637784c68560e01b81529091506001600160a01b03861690637784c6859061298290849060040161440c565b5f60405180830381865afa15801561299c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526129c3919081019061444f565b5f815181106129d4576129d46144f0565b60200260200101515f1c6001600160801b03169150509392505050565b5f805f805f612a018660a0902090565b604051632e3071cd60e11b8152600481018290529091505f906001600160a01b03891690635c60e39a9060240160c060405180830381865afa158015612a49573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a6d9190614504565b90505f81608001516001600160801b031642612a899190614278565b90508015801590612aa6575060408201516001600160801b031615155b8015612abe575060608801516001600160a01b031615155b15612cb3576060888101805160408051638c00bf6b60e01b81528c516001600160a01b0390811660048301526020808f015182166024840152838f0151821660448401529451811660648301526080808f0151608484015288516001600160801b0390811660a485015295890151861660c484015292880151851660e483015294870151841661010482015290860151831661012482015260a08601519092166101448301525f921690638c00bf6b9061016401602060405180830381865afa158015612b8d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bb19190614206565b90505f612bd5612bc1838561374f565b60408601516001600160801b0316906137ad565b9050612be0816137c1565b84604001818151612bf191906145a1565b6001600160801b0316905250612c06816137c1565b84518590612c159083906145a1565b6001600160801b0390811690915260a086015116159050612cb0575f612c518560a001516001600160801b0316836137ad90919063ffffffff16565b90505f612c8582875f01516001600160801b0316612c6f9190614278565b60208801518491906001600160801b0316613816565b9050612c90816137c1565b86602001818151612ca191906145a1565b6001600160801b031690525050505b50505b508051602082015160408301516060909301516001600160801b039283169b9183169a509282169850911695509350505050565b5f610b87612cf6600185614231565b612d03620f424085614231565b86919061383a565b5f612d146114ec565b9050612d27612d228361385b565b613124565b612d2f61259b565b612d3e6126e6836110b061354d565b80612d476114ec565b612d519190614278565b82118015612d6557505f612d63610f66565b115b8015612d765750612d74610dde565b155b8015612d8d57505f5465010000000000900460ff16155b156114df57612d9a613916565b612da261259b565b6114df6126e661354d565b612db6816139df565b5f612dc26121076112ea565b90505f612dd0611928610f66565b90505f808311612de0575f612dfd565b82612df3670de0b6b3a76400008461428b565b612dfd91906142a2565b90505f612e08611bdf565b905081811115612e80575f670de0b6b3a7640000612e26838761428b565b612e3091906142a2565b90508381035f612e63612223837f0000000000000000000000000000000000000000000000000000000000000000611ca1565b9050600354811115612e7857612e7881613a3a565b505050612eef565b612e88613109565b821115612eef575f670de0b6b3a7640000612ea3868461428b565b612ead91906142a2565b9050612ee5612d22612ebf8387614278565b7f0000000000000000000000000000000000000000000000000000000000000000611ca1565b612eed61259b565b505b5f612ef861125f565b90508015612f0957612f0981613ad8565b505050505050565b5f80612f1b610a8f565b90508015612f5357612f50610b3c827f0000000000000000000000000000000000000000000000000000000000000000611c0a565b91505b5090565b6040516349e2903160e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906393c5206290604401606060405180830381865afa158015612fe3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613007919061431c565b905080602001516001600160801b03165f03613024575f91505090565b5f6ec097ce7bc90715b34b9f100000000060086002015f9054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613088573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130ac9190614206565b83604001516001600160801b03166130c4919061428b565b6130ce91906142a2565b90505f670de0b6b3a7640000600860040154836130eb919061428b565b6130f591906142a2565b905080613100610f66565b11935050505090565b5f8054600c5461271091600160401b900461ffff1690611bf6565b5f61312d61125f565b905081811015613146576131418183614278565b613148565b5f5b9150613156826110b0611b89565b9150815f03613163575050565b6114df826122ba565b805f036131765750565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b549091166060820152600c5460808201525f9081906131e8907f0000000000000000000000000000000000000000000000000000000000000000906129f1565b9350935050505f6132516131fd858585613816565b6110b06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000003061293f565b6040516320b76e8160e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906320b76e81906132a7906008905f90869030906004016145c8565b60408051808303815f875af11580156132c2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f09919061460e565b5f835f036132f557505f611d7b565b5f546127109061331690600160501b900467ffffffffffffffff1682614278565b6129226133238787611c0a565b85611ca1565b5f821580159061333b57506004548310155b15610b8757600654613357906001600160a01b03168685613b66565b6005546001600160a01b038681169116148061338057506005546001600160a01b038581169116145b156134535760408051610100810182526001600160a01b0380881680835287821660208085018290525f92835260078152858320918352528381205462ffffff168385015230606084015242608084015260a0830187905260c0830186905260e0830152600654925163414bf38960e01b81529192169063414bf3899061340b908490600401614630565b6020604051808303815f875af1158015613427573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061344b9190614206565b915050610b87565b6001600160a01b038086165f908152600760208181526040808420600554861680865290835281852054938352818520958a16855294825280842054905193946134ad948b9462ffffff90811694929316918b910161469a565b60408051601f1981840301815260065460a08401835281845230602085015242848401526060840188905260808401879052915163c04b8d5960e01b81529093506001600160a01b039091169163c04b8d599161350d919060040161473c565b6020604051808303815f875af1158015613529573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061110d9190614206565b5f806135576112ea565b90505f613562610f66565b9050805f036135715750919050565b5f61359c827f0000000000000000000000000000000000000000000000000000000000000000611c0a565b90505f6135bc6135aa611bdf565b611103670de0b6b3a76400008561428b565b9050838111156135d0575f94505050505090565b9092039392505050565b6060610b8784845f85613c05565b5f80600d5f9054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561363a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061365e9190614206565b90505f811361293a5760405162461bcd60e51b81526020600482015260016024820152600360fc1b60448201526064016108ff565b5f6001828460026040516020016136b4929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b0390941690830152810191909152606001604051602081830303815290604052805190602001205f1c611d7b9190614231565b6040805160018082528183019092526060915f91906020808301908036833701905050905082815f8151811061373e5761373e6144f0565b602090810291909101015292915050565b5f8061375b838561428b565b90505f61377b8280613776670de0b6b3a7640000600261428b565b613cd1565b90505f6137968284613776670de0b6b3a7640000600361428b565b9050806137a38385614231565b61110d9190614231565b5f611d7b8383670de0b6b3a7640000613cd1565b6040805180820190915260148152731b585e081d5a5b9d0c4c8e08195e18d95959195960621b60208201525f906001600160801b03831115611dd05760405162461bcd60e51b81526004016108ff919061474e565b5f610b87613827620f424084614231565b613832600186614231565b869190613cd1565b5f81613847600182614278565b613851858761428b565b61292c9190614231565b5f815f0361386a57505f919050565b5f6138736112ea565b905080831061388457611d7b610f66565b5f6138926121078584614278565b90505f670de0b6b3a76400006138a6611bdf565b6138b0908461428b565b6138ba91906142a2565b90505f6138e7827f0000000000000000000000000000000000000000000000000000000000000000611ca1565b90505f6138f2610f66565b9050808210613901575f61390b565b61390b8282614278565b979650505050505050565b5f61391f610a8f565b5f8054919250906127109061394590600160501b900467ffffffffffffffff1682614231565b613972610b3c857f0000000000000000000000000000000000000000000000000000000000000000611c0a565b61397c919061428b565b61398691906142a2565b9050805f03613993575050565b610dd97f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008484613cdd565b805f036139e95750565b60405163238d657960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063238d6579906124c29060089085903090600401614760565b805f03613a445750565b6040516350d8cd4b60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906350d8cd4b90613a999060089085905f90309081906004016147a0565b60408051808303815f875af1158015613ab4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd9919061460e565b604051636e553f6560e01b8152600481018290523060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636e553f65906044016020604051808303815f875af1158015613b42573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114df9190614206565b604051636eb1769f60e11b81523060048201526001600160a01b03848116602483015282919084169063dd62ed3e90604401602060405180830381865afa158015613bb3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bd79190614206565b1015610dd957613bf16001600160a01b038316845f613e7d565b610dd96001600160a01b0383168483613e7d565b606082471015613c665760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016108ff565b5f80866001600160a01b03168587604051613c8191906147dc565b5f6040518083038185875af1925050503d805f8114613cbb576040519150601f19603f3d011682016040523d82523d5f602084013e613cc0565b606091505b509150915061390b87838387613f11565b5f8161292c848661428b565b5f8115801590613cef57506004548210155b15610b8757600654613d0b906001600160a01b03168684613b66565b6005546001600160a01b0386811691161480613d3457506005546001600160a01b038581169116145b15613dbf5760408051610100810182526001600160a01b0380881680835287821660208085018290525f92835260078152858320918352528381205462ffffff168385015230606084015242608084015260a0830187905260c0830186905260e08301526006549251631b67c43360e31b81529192169063db3e21989061340b908490600401614630565b6005546001600160a01b039081165f818152600760208181526040808420868b168552825280842054958b16845291815281832084845281528183205491519294613e1d948a9462ffffff92831694919391909216918c910161469a565b60408051601f1981840301815260065460a084018352818452306020850152428484015260608401889052608084018790529151631e51809360e31b81529093506001600160a01b039091169163f28c04989161350d919060040161473c565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613ece8482613f89565b613f0b576040516001600160a01b03841660248201525f6044820152613f0190859063095ea7b360e01b90606401611b52565b613f0b84826126eb565b50505050565b60608315613f7f5782515f03613f78576001600160a01b0385163b613f785760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108ff565b5081610b87565b610b87838361402a565b5f805f846001600160a01b031684604051613fa491906147dc565b5f604051808303815f865af19150503d805f8114613fdd576040519150601f19603f3d011682016040523d82523d5f602084013e613fe2565b606091505b509150915081801561400c57508051158061400c57508080602001905181019061400c91906143f1565b801561402157506001600160a01b0385163b15155b95945050505050565b81511561403a5781518083602001fd5b8060405162461bcd60e51b81526004016108ff919061474e565b80356001600160a01b038116811461293a575f80fd5b5f6020828403121561407a575f80fd5b611d7b82614054565b5f60208284031215614093575f80fd5b5035919050565b5f805f606084860312156140ac575f80fd5b6140b584614054565b92506140c360208501614054565b9150604084013562ffffff811681146140da575f80fd5b809150509250925092565b8015158114610a8c575f80fd5b5f60208284031215614102575f80fd5b8135611d7b816140e5565b803561ffff8116811461293a575f80fd5b5f806040838503121561412f575f80fd5b6141388361410d565b91506141466020840161410d565b90509250929050565b5f5b83811015614169578181015183820152602001614151565b50505f910152565b5f815180845261418881602086016020860161414f565b601f01601f19169290920160200192915050565b8215158152604060208201525f610b876040830184614171565b5f80604083850312156141c7575f80fd5b6141d083614054565b946020939093013593505050565b5f80604083850312156141ef575f80fd5b6141f883614054565b915061414660208401614054565b5f60208284031215614216575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115611c9b57611c9b61421d565b634e487b7160e01b5f52601260045260245ffd5b5f60208284031215614268575f80fd5b815160ff81168114611d7b575f80fd5b81810381811115611c9b57611c9b61421d565b8082028115828204841417611c9b57611c9b61421d565b5f826142bc57634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156142fe576142fe6142c1565b604052919050565b80516001600160801b038116811461293a575f80fd5b5f6060828403121561432c575f80fd5b6040516060810181811067ffffffffffffffff8211171561434f5761434f6142c1565b6040528251815261436260208401614306565b602082015261437360408401614306565b60408201529392505050565b80546001600160a01b039081168352600182015481166020840152600282015481166040840152600382015416606083015260040154608090910152565b61010081016143cc828761437f565b60a08201949094526001600160a01b0392831660c0820152911660e090910152919050565b5f60208284031215614401575f80fd5b8151611d7b816140e5565b602080825282518282018190525f9190848201906040850190845b8181101561444357835183529284019291840191600101614427565b50909695505050505050565b5f6020808385031215614460575f80fd5b825167ffffffffffffffff80821115614477575f80fd5b818501915085601f83011261448a575f80fd5b81518181111561449c5761449c6142c1565b8060051b91506144ad8483016142d5565b81815291830184019184810190888411156144c6575f80fd5b938501935b838510156144e4578451825293850193908501906144cb565b98975050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60c08284031215614514575f80fd5b60405160c0810181811067ffffffffffffffff82111715614537576145376142c1565b60405261454383614306565b815261455160208401614306565b602082015261456260408401614306565b604082015261457360608401614306565b606082015261458460808401614306565b608082015261459560a08401614306565b60a08201529392505050565b6001600160801b038181168382160190808211156145c1576145c161421d565b5092915050565b5f6101206145d6838861437f565b60a08301959095525060c08101929092526001600160a01b031660e082015261010081018290525f9181019190915261014001919050565b5f806040838503121561461f575f80fd5b505080516020909101519092909150565b6101008101611c9b828480516001600160a01b03908116835260208083015182169084015260408083015162ffffff16908401526060808301518216908401526080808301519084015260a0828101519084015260c0808301519084015260e09182015116910152565b6bffffffffffffffffffffffff19606096871b811682526001600160e81b031960e896871b8116601484015294871b811660178301529290941b909216602b840152921b909116602e82015260420190565b5f815160a0845261470060a0850182614171565b6020848101516001600160a01b031690860152604080850151908601526060808501519086015260809384015193909401929092525090919050565b602081525f611d7b60208301846146ec565b602081525f611d7b6020830184614171565b5f61010061476e838761437f565b60a0830194909452506001600160a01b039190911660c082015260e081018290525f9181019190915261012001919050565b61012081016147af828861437f565b60a082019590955260c08101939093526001600160a01b0391821660e08401521661010090910152919050565b5f82516147ed81846020870161414f565b919091019291505056fea264697066735822122036e1c05f22693a25fcf2164d78ddf6b5a361cfbf84dfeb52c15642d4ae9aafa964736f6c63430008170033a2646970667358221220124bf9188917a632c38071a8dc8af8a8210488057bd927a01407d971a1ae31a564736f6c634300081700330000000000000000000000001b5f15dcb82d25f91c65b53cee151e8b9fbdd2710000000000000000000000005a74cb32d36f2f517db6f7b0a0591e09b22cde69000000000000000000000000604e586f17ce106b64185a7a0d2c1da5bace711e00000000000000000000000016388463d60ffe0661cf7f1f31a7d658ac790ff7000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff52000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564
Deployed Bytecode
0x608060405234801562000010575f80fd5b5060043610620000a8575f3560e01c806388a8d602116200006b57806388a8d602146200015f578063aced16611462000172578063d8fbc8331462000186578063ed27f7c914620001ae578063f887ea4014620001c2575f80fd5b80630b18f8a514620000ac578063180cb47f14620000df5780633740401714620001075780634a945f8d146200013257806370905dce146200014b575b5f80fd5b620000c3620000bd36600462000552565b620001ea565b6040516001600160a01b03909116815260200160405180910390f35b620000c37f000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff5281565b620000c36200011836600462000615565b60046020525f90815260409020546001600160a01b031681565b62000149620001433660046200062d565b6200048d565b005b600354620000c3906001600160a01b031681565b5f54620000c3906001600160a01b031681565b600254620000c3906001600160a01b031681565b620000c37f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb81565b600154620000c3906001600160a01b031681565b620000c37f000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156481565b5f828152600460205260408120546001600160a01b0316156200020b575f80fd5b5f88888888887f000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff527f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb8a8a7f000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564604051620002849062000528565b620002999a9998979695949392919062000687565b604051809103905ff080158015620002b3573d5f803e3d5ffd5b5060015460405163352f8d5160e11b81526001600160a01b039182166004820152919250821690636a5f1aa2906024015f604051808303815f87803b158015620002fb575f80fd5b505af11580156200030e573d5f803e3d5ffd5b5050600254604051633a43a3f360e11b81526001600160a01b039182166004820152908416925063748747e691506024015f604051808303815f87803b15801562000357575f80fd5b505af11580156200036a573d5f803e3d5ffd5b50505f54604051630f629b7960e41b81526001600160a01b039182166004820152908416925063f629b79091506024015f604051808303815f87803b158015620003b2575f80fd5b505af1158015620003c5573d5f803e3d5ffd5b5050600354604051630d768ce560e21b81526001600160a01b03918216600482015290841692506335da339491506024015f604051808303815f87803b1580156200040e575f80fd5b505af115801562000421573d5f803e3d5ffd5b50506040518692506001600160a01b03841691507f0df9ebba034d173a99a9f19984c4f6b69c45f86fdf65be5234f4040127fb1b17905f90a35f9384526004602052604090932080546001600160a01b0319166001600160a01b03851617905550909695505050505050565b5f546001600160a01b03163314620004d95760405162461bcd60e51b815260206004820152600b60248201526a085b585b9859d95b595b9d60aa1b604482015260640160405180910390fd5b5f80546001600160a01b039586166001600160a01b0319918216179091556001805494861694821694909417909355600280549285169284169290921790915560038054919093169116179055565b6158a7806200070883390190565b80356001600160a01b03811681146200054d575f80fd5b919050565b5f805f805f805f60c0888a03121562000569575f80fd5b620005748862000536565b9650602088013567ffffffffffffffff8082111562000591575f80fd5b818a0191508a601f830112620005a5575f80fd5b813581811115620005b4575f80fd5b8b6020828501011115620005c6575f80fd5b602083019850809750505050620005e06040890162000536565b9350620005f06060890162000536565b9250608088013591506200060760a0890162000536565b905092959891949750929550565b5f6020828403121562000626575f80fd5b5035919050565b5f805f806080858703121562000641575f80fd5b6200064c8562000536565b93506200065c6020860162000536565b92506200066c6040860162000536565b91506200067c6060860162000536565b905092959194509250565b6001600160a01b038b811682526101206020830181905282018a90525f90610140908b8d838601375f848d01830152998a16604084015297891660608301525094871660808601529290951660a084015260c08301526001600160a01b0393841660e0830152909216610100830152601f909201601f191601019291505056fe6101a06040525f80546227100162ffffff19909116179055600580546001600160a01b031990811673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2179091556006805490911673e592427a0aece92de3edee1f18e0157c058615641790553480156200006b575f80fd5b50604051620058a7380380620058a78339810160408190526200008e9162000a73565b6001600160a01b0389166080523060a052604051899089908990899084908490829082906200010390620000cf908490849033908190819060240162000bc4565b60408051601f198184030181529190526020810180516001600160e01b03908116634b839d7360e11b179091526200056716565b505073d377919fa87120584b21279a491f82d5265a139c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc555050506001600160a01b0382811660c0525f196001555f8054642e90edd000600255600160281b600160901b0319166b01f41f401b580000000000001790558116156200026e57806001600160a01b031660e0816001600160a01b031681525050816001600160a01b031660e0516001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001e6573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906200020c919062000c06565b6001600160a01b031614620002575760405162461bcd60e51b815260206004820152600c60248201526b085b195b99195c95985d5b1d60a21b60448201526064015b60405180910390fd5b6200026e6001600160a01b038316825f19620005f6565b505050506001600160a01b03858116610140528416610100819052610120849052604051632c3c915760e01b815260048101859052632c3c91579060240160a060405180830381865afa158015620002c8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620002ee919062000c22565b8051600880546001600160a01b03199081166001600160a01b0393841690811790925560208401516009805483169185169190911790556040840151600a805483169185169190911790556060840151600b8054909216908416179055608090920151600c5588161480156200037157506009546001600160a01b038a81169116145b6200037a575f80fd5b886001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620003b7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190620003dd919062000cb5565b620003ea90600a62000de4565b6101608181525050866001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200042f573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000455919062000cb5565b6200046290600a62000de4565b610180526200047d6001600160a01b038a16855f19620005f6565b620004946001600160a01b038816855f19620005f6565b620004a0612710600455565b8060065f6101000a8154816001600160a01b0302191690836001600160a01b03160217905550816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000503573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019062000529919062000cb5565b60ff1660081462000538575f80fd5b50600d80546001600160a01b0319166001600160a01b03929092169190911790555062000e4695505050505050565b60605f8073d377919fa87120584b21279a491f82d5265a139c6001600160a01b03168460405162000599919062000df4565b5f60405180830381855af49150503d805f8114620005d3576040519150601f19603f3d011682016040523d82523d5f602084013e620005d8565b606091505b509150915081620005ef576040513d805f833e8082fd5b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b17909152620006509085908390620006cb16565b620006c5576040516001600160a01b03841660248201525f6044820152620006b990859063095ea7b360e01b9060640160408051808303601f190181529190526020810180516001600160e01b0319939093166001600160e01b03938416179052906200077716565b620006c5848262000777565b50505050565b5f805f846001600160a01b031684604051620006e8919062000df4565b5f604051808303815f865af19150503d805f811462000723576040519150601f19603f3d011682016040523d82523d5f602084013e62000728565b606091505b5091509150818015620007565750805115806200075657508080602001905181019062000756919062000e11565b80156200076c57506001600160a01b0385163b15155b925050505b92915050565b6040805180820190915260208082527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564908201525f90620007c5906001600160a01b0385169084906200084e565b905080515f1480620007e8575080806020019051810190620007e8919062000e11565b620008495760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016200024e565b505050565b60606200085e84845f8562000866565b949350505050565b606082471015620008c95760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016200024e565b5f80866001600160a01b03168587604051620008e6919062000df4565b5f6040518083038185875af1925050503d805f811462000922576040519150601f19603f3d011682016040523d82523d5f602084013e62000927565b606091505b5090925090506200093b8783838762000946565b979650505050505050565b60608315620009b95782515f03620009b1576001600160a01b0385163b620009b15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016200024e565b50816200085e565b6200085e8383815115620009d05781518083602001fd5b8060405162461bcd60e51b81526004016200024e919062000e32565b80516001600160a01b038116811462000a03575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171562000a475762000a4762000a08565b604052919050565b5f5b8381101562000a6b57818101518382015260200162000a51565b50505f910152565b5f805f805f805f805f6101208a8c03121562000a8d575f80fd5b62000a988a620009ec565b60208b01519099506001600160401b038082111562000ab5575f80fd5b818c0191508c601f83011262000ac9575f80fd5b81518181111562000ade5762000ade62000a08565b62000af3601f8201601f191660200162000a1c565b91508082528d602082850101111562000b0a575f80fd5b62000b1d81602084016020860162000a4f565b50985062000b30905060408b01620009ec565b965062000b4060608b01620009ec565b955062000b5060808b01620009ec565b945062000b6060a08b01620009ec565b935060c08a0151925062000b7760e08b01620009ec565b915062000b886101008b01620009ec565b90509295985092959850929598565b5f815180845262000bb081602086016020860162000a4f565b601f01601f19169290920160200192915050565b5f60018060a01b03808816835260a0602084015262000be760a084018862000b97565b9581166040840152938416606083015250911660809091015292915050565b5f6020828403121562000c17575f80fd5b620005ef82620009ec565b5f60a0828403121562000c33575f80fd5b60405160a081016001600160401b038111828210171562000c585762000c5862000a08565b60405262000c6683620009ec565b815262000c7660208401620009ec565b602082015262000c8960408401620009ec565b604082015262000c9c60608401620009ec565b6060820152608083015160808201528091505092915050565b5f6020828403121562000cc6575f80fd5b815160ff81168114620005ef575f80fd5b634e487b7160e01b5f52601160045260245ffd5b600181815b8085111562000d2b57815f190482111562000d0f5762000d0f62000cd7565b8085161562000d1d57918102915b93841c939080029062000cf0565b509250929050565b5f8262000d435750600162000771565b8162000d5157505f62000771565b816001811462000d6a576002811462000d755762000d95565b600191505062000771565b60ff84111562000d895762000d8962000cd7565b50506001821b62000771565b5060208310610133831016604e8410600b841016171562000dba575081810a62000771565b62000dc6838362000ceb565b805f190482111562000ddc5762000ddc62000cd7565b029392505050565b5f620005ef60ff84168362000d33565b5f825162000e0781846020870162000a4f565b9190910192915050565b5f6020828403121562000e22575f80fd5b81518015158114620005ef575f80fd5b602081525f620005ef602083018462000b97565b60805160a05160c05160e051610100516101205161014051610160516101805161482d6200107a5f395f6128d301525f6128f901525f8181610444015281816108a90152610a0801525f8181610612015281816112f901528181612f66015261322c01525f818161079801528181610fb00152818161133101528181611e730152818161248601528181612f9e015281816131c30152818161320a0152818161326b01528181613a000152613a5b01525f81816103f10152818161098501528181610df501528181611ba001528181611dee015281816122de0152818161235c015281816123f30152613af401525f81816105160152818161094701528181610b18015281816110d5015281816112760152818161149f0152818161192a015281816121ff015281816126250152818161266c015281816127c101528181612e3f01528181612ec101528181612f2c01528181613578015281816138c30152818161394e01526139b901525f8181610ba401528181610ca701528181610d2301528181610ee601528181610fee0152818161115c015281816113c20152818161143f0152818161154f015281816115cb01528181611657015281816116dd01528181611758015281816117ee015281816118690152818161197801528181611a4e01528181611f2b015261207201525f818161090a01528181610b3e01528181611503015281816118f001528181612109015281816126460152818161268d015281816128020152613998015261482d5ff3fe608060405234801561000f575f80fd5b5060043610610376575f3560e01c80636ed71ede116101d1578063bdc8144b11610102578063ebf27802116100a0578063f0fa55a91161007a578063f0fa55a914610822578063f3ce280a14610835578063f887ea4014610878578063fde813a81461088b57610376565b8063ebf27802146107f3578063ecf7085814610806578063ee8bfa311461080f57610376565b8063d8fbc833116100dc578063d8fbc83314610793578063da769c47146107ba578063e4d746fb146107cf578063e8621149146107eb57610376565b8063bdc8144b14610752578063d19a3bb814610765578063d69686011461078057610376565b8063950b3d731161016f578063ac00ff2611610149578063ac00ff261461071b578063b1ea668d1461072e578063b2a1913714610736578063b6a165061461074957610376565b8063950b3d73146106e25780639b90fb16146106f55780639d7fb70c1461070857610376565b8063797bf343116101ab578063797bf3431461064f5780637b9e68f2146106575780637d969932146106bc5780638e72e31b146106ce57610376565b80636ed71ede1461060d578063702651db14610634578063735fd1891461063c57610376565b80633e032a3b116102ab57806355b90362116102495780636353f822116102235780636353f822146105ba5780636687500e146105e15780636718835f146105e95780636be36a1d1461060557610376565b806355b90362146105895780635bc34efe1461059c5780635d265d3f146105a457610376565b806349317f1d1161028557806349317f1d1461054b5780634a5d0943146105535780635001f3b514610563578063503160d91461057657610376565b80633e032a3b146104de578063456dc17a1461051157806346aa2f121461053857610376565b8063278b73121161031857806331b7f9b2116102f257806331b7f9b2146104a857806338dbd359146104b0578063392f7a70146104c35780633d6cb575146104cb57610376565b8063278b73121461046f5780632d49be91146104825780632e3ecf2c1461049557610376565b806306876d191161035457806306876d19146103ec578063085c884f1461042b578063180cb47f1461043f578063181443671461046657610376565b806301681a62146103ab57806302402ae0146103be57806304bd4629146103d9575b73d377919fa87120584b21279a491f82d5265a139c365f80375f80365f845af43d5f803e8080156103a5573d5ff35b3d5ffd5b005b6103a96103b936600461406a565b61089e565b6103c6610a8f565b6040519081526020015b60405180910390f35b6103c66103e736600461406a565b610acd565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016103d0565b6103c6610439366004614083565b50600190565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6103c660045481565b6103a961047d36600461406a565b610b8f565b6103a9610490366004614083565b610c92565b6103a96104a336600461409a565b610d0e565b6103c6610dde565b6103a96104be3660046140f2565b610ed1565b6103c6610f66565b6103a96104d9366004614083565b610fd9565b5f546104f890600160501b900467ffffffffffffffff1681565b60405167ffffffffffffffff90911681526020016103d0565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b6103c661054636600461406a565b610fea565b6103c6611117565b5f54610100900461ffff166103c6565b600554610413906001600160a01b031681565b6103a9610584366004614083565b611136565b6103a961059736600461411e565b611147565b6103c661125f565b6105ac6112ad565b6040516103d092919061419c565b5f546105ce90600160401b900461ffff1681565b60405161ffff90911681526020016103d0565b6103c66112ea565b5f546105f59060ff1681565b60405190151581526020016103d0565b600c546103c6565b6103c67f000000000000000000000000000000000000000000000000000000000000000081565b6103a96113ad565b6103a961064a3660046141b6565b61142a565b6103c66114ec565b600854600954600a54600b54600c54610681946001600160a01b0390811694811693811692169085565b604080516001600160a01b0396871681529486166020860152928516928401929092529092166060820152608081019190915260a0016103d0565b5f546301000000900461ffff166103c6565b5f546105ce90600160301b900461ffff1681565b6103a96106f0366004614083565b61153a565b6103a9610703366004614083565b6115b6565b6103a9610716366004614083565b611631565b6103a96107293660046140f2565b611642565b6103a96116c8565b6103a961074436600461406a565b611743565b6103c660025481565b6103a9610760366004614083565b6117d9565b61041373d377919fa87120584b21279a491f82d5265a139c81565b6103a961078e366004614083565b611854565b6104137f000000000000000000000000000000000000000000000000000000000000000081565b5f546105f59065010000000000900460ff1681565b6103c66107dd366004614083565b5068056bc75e2d6310000090565b6103c66118d0565b6103a9610801366004614083565b611963565b6103c660015481565b600d54610413906001600160a01b031681565b6103a9610830366004614083565b611a39565b6108646108433660046141de565b600760209081525f928352604080842090915290825290205462ffffff1681565b60405162ffffff90911681526020016103d0565b600654610413906001600160a01b031681565b6103a9610899366004614083565b611b15565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146109085760405162461bcd60e51b81526004016108ff9060208082526004908201526310b3b7bb60e11b604082015260600190565b60405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b03161415801561097c57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b80156109ba57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b031614155b6109ef5760405162461bcd60e51b815260206004820152600660248201526502173776565760d41b60448201526064016108ff565b6040516370a0823160e01b8152306004820152610a8c907f0000000000000000000000000000000000000000000000000000000000000000906001600160a01b038416906370a0823190602401602060405180830381865afa158015610a57573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a7b9190614206565b6001600160a01b0384169190611b26565b50565b5f80610a9961125f565b610aa1610dde565b610aab9190614231565b90505f610ab6610f66565b9050808210610ac7575f9250505090565b03919050565b5f80610ad76112ea565b610ae2906001614231565b90505f610aed611b89565b9050610af7610dde565b811015610b7457610b06611bdf565b670de0b6b3a7640000610b62610b3c847f0000000000000000000000000000000000000000000000000000000000000000611c0a565b7f0000000000000000000000000000000000000000000000000000000000000000611ca1565b0281610b7057610b70614244565b0491505b81610b7d6114ec565b610b879190614231565b949350505050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610bec575f80fd5b505afa158015610bfe573d5f803e3d5ffd5b50505050806001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c3e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c629190614258565b60ff16600814610c70575f80fd5b600d80546001600160a01b0319166001600160a01b0392909216919091179055565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610cef575f80fd5b505afa158015610d01573d5f803e3d5ffd5b50505050610a8c81600455565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610d6b575f80fd5b505afa158015610d7d573d5f803e3d5ffd5b5050506001600160a01b038085165f8181526007602081815260408084209589168452948152848320805462ffffff1990811662ffffff8a16908117909255928252858420948452939052929020805490921617905550505050565b505050565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a9082906370a08231906024015b602060405180830381865afa158015610e4b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e6f9190614206565b6040518263ffffffff1660e01b8152600401610e8d91815260200190565b602060405180830381865afa158015610ea8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ecc9190614206565b905090565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610f2e575f80fd5b505afa158015610f40573d5f803e3d5ffd5b50505f8054931515650100000000000265ff000000000019909416939093179092555050565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b5482166060820152600c5460808201525f91610ecc917f00000000000000000000000000000000000000000000000000000000000000009091169030611d2e565b610fe1611d82565b610a8c81611db9565b5f807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611048573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061106c9190614206565b90505f816001541161107e575f61108c565b8160015461108c9190614278565b90505f61109a5f1983611dc2565b90505f6110b56110a8611dd7565b6110b0611e25565b611dc2565b905061110d826110b06110c6611bdf565b670de0b6b3a76400006110f9867f0000000000000000000000000000000000000000000000000000000000000000611c0a565b611103919061428b565b610b3c91906142a2565b9695505050505050565b5f611120611d82565b611128611ebf565b905061113381611f10565b90565b61113e611d82565b610a8c81612053565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156111a4575f80fd5b505afa1580156111b6573d5f803e3d5ffd5b505050506123288161ffff16111580156111d757508061ffff168261ffff16105b80156111e6575061ffff821615155b6112205760405162461bcd60e51b815260206004820152600b60248201526a34b73b30b634b210262a2b60a91b60448201526064016108ff565b5f805469ffffffff0000000000001916600160301b61ffff9485160269ffff0000000000000000191617600160401b9290931691909102919091179055565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401610e8d565b5f60606112b861205c565b6040805160048152602481019091526020810180516001600160e01b031663440368a360e01b17905290939092509050565b6040516349e2903160e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906393c5206290604401606060405180830381865afa158015611376573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061139a919061431c565b604001516001600160801b031692915050565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b15801561140a575f80fd5b505afa15801561141c573d5f803e3d5ffd5b50505050611428612260565b565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b158015611487575f80fd5b505afa158015611499573d5f803e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b0316036114e3576114df816122ba565b5050565b6114df81612465565b6040516370a0823160e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401610e8d565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611597575f80fd5b505afa1580156115a9573d5f803e3d5ffd5b50505050610a8c816124f2565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611613575f80fd5b505afa158015611625573d5f803e3d5ffd5b50505060029190915550565b611639611d82565b610a8c8161258b565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b15801561169f575f80fd5b505afa1580156116b1573d5f803e3d5ffd5b50505f805460ff1916931515939093179092555050565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b158015611725575f80fd5b505afa158015611737573d5f803e3d5ffd5b5050505061142861259b565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156117a0575f80fd5b505afa1580156117b2573d5f803e3d5ffd5b5050600580546001600160a01b0319166001600160a01b0394909416939093179092555050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611836575f80fd5b505afa158015611848573d5f803e3d5ffd5b50505060019190915550565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156118b1575f80fd5b505afa1580156118c3573d5f803e3d5ffd5b50505050610a8c816125b6565b5f806118da6112ea565b9050805f036118ea575f91505090565b611914817f0000000000000000000000000000000000000000000000000000000000000000611c0a565b670de0b6b3a764000061194e611928610f66565b7f0000000000000000000000000000000000000000000000000000000000000000611c0a565b028161195c5761195c614244565b0491505090565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b1580156119c0575f80fd5b505afa1580156119d2573d5f803e3d5ffd5b505050505f198103611a30575f6119e761125f565b90505f816119f3610dde565b6119fd9190614231565b90505f611a08610f66565b9050808211611a17575f611a2a565b611a2a611a248284614278565b84611dc2565b93505050505b610a8c81612616565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611a96575f80fd5b505afa158015611aa8573d5f803e3d5ffd5b505050506127108110611ae85760405162461bcd60e51b8152602060048201526008602482015267736c69707061676560c01b60448201526064016108ff565b5f805467ffffffffffffffff909216600160501b0267ffffffffffffffff60501b19909216919091179055565b611b1d611d82565b610a8c816126b6565b6040516001600160a01b038316602482015260448101829052610dd990849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526126eb565b604051636c82bbbf60e11b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906307a2d13a90829063d905777e90602401610e30565b5f8054600c5461271091600160301b900461ffff16905b0281611c0457611c04614244565b04905090565b5f825f03611c1957505f611c9b565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611c55573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c799190614258565b60ff16600a0a611c88836127be565b840281611c9757611c97614244565b0490505b92915050565b5f825f03611cb057505f611c9b565b611cb9826127be565b826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611cf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d199190614258565b60ff16600a0a840281611c9757611c97614244565b5f80611d3b8460a0902090565b90505f611d526001600160a01b038716838661293f565b90505f80611d6088886129f1565b9094509250611d7491508490508383612ce7565b9450505050505b9392505050565b3330146114285760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b60448201526064016108ff565b610a8c81612d0b565b5f818310611dd05781611d7b565b5090919050565b60405163402d267d60e01b81523060048201525f907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063402d267d90602401610e8d565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b5482166060820152600c5460808201525f9182918291611e9b917f0000000000000000000000000000000000000000000000000000000000000000909116906129f1565b509250509150808211611eae575f611eb8565b611eb88183614278565b9250505090565b5f611ec8612260565b611ee4611edf611ed66114ec565b6110b030610fea565b612dad565b611eec612f11565b611ef46112ea565b611efc6114ec565b611f069190614231565b610ecc9190614278565b5f5460ff16611f2857505f805460ff19166001179055565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611f85573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fa99190614206565b90508082111561201c575f5461271090611fcc90610100900461ffff168361428b565b611fd691906142a2565b611fe08284614278565b11156114df5760405162461bcd60e51b815260206004820152600b60248201526a6865616c7468436865636b60a81b60448201526064016108ff565b818111156114df575f546127109061203f906301000000900461ffff168361428b565b61204991906142a2565b611fe08383614278565b610a8c81612dad565b5f612065612f57565b156120705750600190565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa1580156120cc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120f09190614206565b5f036120fb57505f90565b5f61212d6121076112ea565b7f0000000000000000000000000000000000000000000000000000000000000000611c0a565b90505f61213b611928610f66565b90505f80831161214b575f612168565b8261215e670de0b6b3a76400008461428b565b61216891906142a2565b9050612172613109565b811115612183576001935050505090565b5f61218c611bdf565b9050811580159061219a57505f5b156121ae5760025448111594505050505090565b80821080156121cd575067016345785d8a00006121cb8383614278565b115b15612256575f670de0b6b3a76400006121e6838761428b565b6121f091906142a2565b90508381035f61222e612223837f0000000000000000000000000000000000000000000000000000000000000000611ca1565b6110b06110a8611dd7565b9050805f03612244575f97505050505050505090565b60025448111597505050505050505090565b5f94505050505090565b5f61226961125f565b612271610dde565b61227b9190614231565b90505f612286610f66565b9050808211156114df575f61229b8284614278565b90506122a681613124565b610dd96122b5826110b061125f565b612616565b604051630a28a47760e01b8152600481018290525f906123cd906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690630a28a47790602401602060405180830381865afa158015612323573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123479190614206565b6040516370a0823160e01b81523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156123a9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110b09190614206565b604051635d043b2960e11b815260048101829052306024820181905260448201529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063ba087652906064016020604051808303815f875af1158015612441573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd99190614206565b805f0361246f5750565b604051638720316d60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690638720316d906124c2906008908590309081906004016143bd565b5f604051808303815f87803b1580156124d9575f80fd5b505af11580156124eb573d5f803e3d5ffd5b5050505050565b5f81116125305760405162461bcd60e51b815260206004820152600c60248201526b085e995c9bc81c1c9bd99a5d60a21b60448201526064016108ff565b61ffff81111561256e5760405162461bcd60e51b8152602060048201526009602482015268042e8dede40d0d2ced60bb1b60448201526064016108ff565b5f805461ffff9092166101000262ffff0019909216919091179055565b610a8c611edf826110b030610fea565b6114286125b16125a961125f565b6110b0610f66565b61316c565b61271081106125f55760405162461bcd60e51b815260206004820152600b60248201526a085b1bdcdcc81b1a5b5a5d60aa1b60448201526064016108ff565b5f805461ffff90921663010000000264ffff00000019909216919091179055565b805f036126205750565b6114df7f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000836126b1857f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000006132e6565b613329565b6126c2816110b0611b89565b905080156126d3576126d3816122ba565b6126db61259b565b610a8c6126e661354d565b612465565b5f61273f826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166135da9092919063ffffffff16565b905080515f148061275f57508080602001905181019061275f91906143f1565b610dd95760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016108ff565b5f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03160361280057611c9b6135e8565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b031603612936575f6128426135e8565b90505f60086002015f9054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612898573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128bc9190614206565b90506128f76ec097ce7bc90715b34b9f10000000007f000000000000000000000000000000000000000000000000000000000000000061428b565b7f0000000000000000000000000000000000000000000000000000000000000000612922848461428b565b61292c919061428b565b610b8791906142a2565b5f80fd5b919050565b5f8061295361294e8585613693565b613706565b604051637784c68560e01b81529091506001600160a01b03861690637784c6859061298290849060040161440c565b5f60405180830381865afa15801561299c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526129c3919081019061444f565b5f815181106129d4576129d46144f0565b60200260200101515f1c6001600160801b03169150509392505050565b5f805f805f612a018660a0902090565b604051632e3071cd60e11b8152600481018290529091505f906001600160a01b03891690635c60e39a9060240160c060405180830381865afa158015612a49573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a6d9190614504565b90505f81608001516001600160801b031642612a899190614278565b90508015801590612aa6575060408201516001600160801b031615155b8015612abe575060608801516001600160a01b031615155b15612cb3576060888101805160408051638c00bf6b60e01b81528c516001600160a01b0390811660048301526020808f015182166024840152838f0151821660448401529451811660648301526080808f0151608484015288516001600160801b0390811660a485015295890151861660c484015292880151851660e483015294870151841661010482015290860151831661012482015260a08601519092166101448301525f921690638c00bf6b9061016401602060405180830381865afa158015612b8d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612bb19190614206565b90505f612bd5612bc1838561374f565b60408601516001600160801b0316906137ad565b9050612be0816137c1565b84604001818151612bf191906145a1565b6001600160801b0316905250612c06816137c1565b84518590612c159083906145a1565b6001600160801b0390811690915260a086015116159050612cb0575f612c518560a001516001600160801b0316836137ad90919063ffffffff16565b90505f612c8582875f01516001600160801b0316612c6f9190614278565b60208801518491906001600160801b0316613816565b9050612c90816137c1565b86602001818151612ca191906145a1565b6001600160801b031690525050505b50505b508051602082015160408301516060909301516001600160801b039283169b9183169a509282169850911695509350505050565b5f610b87612cf6600185614231565b612d03620f424085614231565b86919061383a565b5f612d146114ec565b9050612d27612d228361385b565b613124565b612d2f61259b565b612d3e6126e6836110b061354d565b80612d476114ec565b612d519190614278565b82118015612d6557505f612d63610f66565b115b8015612d765750612d74610dde565b155b8015612d8d57505f5465010000000000900460ff16155b156114df57612d9a613916565b612da261259b565b6114df6126e661354d565b612db6816139df565b5f612dc26121076112ea565b90505f612dd0611928610f66565b90505f808311612de0575f612dfd565b82612df3670de0b6b3a76400008461428b565b612dfd91906142a2565b90505f612e08611bdf565b905081811115612e80575f670de0b6b3a7640000612e26838761428b565b612e3091906142a2565b90508381035f612e63612223837f0000000000000000000000000000000000000000000000000000000000000000611ca1565b9050600354811115612e7857612e7881613a3a565b505050612eef565b612e88613109565b821115612eef575f670de0b6b3a7640000612ea3868461428b565b612ead91906142a2565b9050612ee5612d22612ebf8387614278565b7f0000000000000000000000000000000000000000000000000000000000000000611ca1565b612eed61259b565b505b5f612ef861125f565b90508015612f0957612f0981613ad8565b505050505050565b5f80612f1b610a8f565b90508015612f5357612f50610b3c827f0000000000000000000000000000000000000000000000000000000000000000611c0a565b91505b5090565b6040516349e2903160e11b81527f000000000000000000000000000000000000000000000000000000000000000060048201523060248201525f9081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906393c5206290604401606060405180830381865afa158015612fe3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613007919061431c565b905080602001516001600160801b03165f03613024575f91505090565b5f6ec097ce7bc90715b34b9f100000000060086002015f9054906101000a90046001600160a01b03166001600160a01b031663a035b1fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613088573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130ac9190614206565b83604001516001600160801b03166130c4919061428b565b6130ce91906142a2565b90505f670de0b6b3a7640000600860040154836130eb919061428b565b6130f591906142a2565b905080613100610f66565b11935050505090565b5f8054600c5461271091600160401b900461ffff1690611bf6565b5f61312d61125f565b905081811015613146576131418183614278565b613148565b5f5b9150613156826110b0611b89565b9150815f03613163575050565b6114df826122ba565b805f036131765750565b6040805160a0810182526008546001600160a01b03908116825260095481166020830152600a54811692820192909252600b549091166060820152600c5460808201525f9081906131e8907f0000000000000000000000000000000000000000000000000000000000000000906129f1565b9350935050505f6132516131fd858585613816565b6110b06001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000003061293f565b6040516320b76e8160e01b81529091506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906320b76e81906132a7906008905f90869030906004016145c8565b60408051808303815f875af11580156132c2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f09919061460e565b5f835f036132f557505f611d7b565b5f546127109061331690600160501b900467ffffffffffffffff1682614278565b6129226133238787611c0a565b85611ca1565b5f821580159061333b57506004548310155b15610b8757600654613357906001600160a01b03168685613b66565b6005546001600160a01b038681169116148061338057506005546001600160a01b038581169116145b156134535760408051610100810182526001600160a01b0380881680835287821660208085018290525f92835260078152858320918352528381205462ffffff168385015230606084015242608084015260a0830187905260c0830186905260e0830152600654925163414bf38960e01b81529192169063414bf3899061340b908490600401614630565b6020604051808303815f875af1158015613427573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061344b9190614206565b915050610b87565b6001600160a01b038086165f908152600760208181526040808420600554861680865290835281852054938352818520958a16855294825280842054905193946134ad948b9462ffffff90811694929316918b910161469a565b60408051601f1981840301815260065460a08401835281845230602085015242848401526060840188905260808401879052915163c04b8d5960e01b81529093506001600160a01b039091169163c04b8d599161350d919060040161473c565b6020604051808303815f875af1158015613529573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061110d9190614206565b5f806135576112ea565b90505f613562610f66565b9050805f036135715750919050565b5f61359c827f0000000000000000000000000000000000000000000000000000000000000000611c0a565b90505f6135bc6135aa611bdf565b611103670de0b6b3a76400008561428b565b9050838111156135d0575f94505050505090565b9092039392505050565b6060610b8784845f85613c05565b5f80600d5f9054906101000a90046001600160a01b03166001600160a01b03166350d25bcd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561363a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061365e9190614206565b90505f811361293a5760405162461bcd60e51b81526020600482015260016024820152600360fc1b60448201526064016108ff565b5f6001828460026040516020016136b4929190918252602082015260400190565b60408051601f1981840301815282825280516020918201206001600160a01b0390941690830152810191909152606001604051602081830303815290604052805190602001205f1c611d7b9190614231565b6040805160018082528183019092526060915f91906020808301908036833701905050905082815f8151811061373e5761373e6144f0565b602090810291909101015292915050565b5f8061375b838561428b565b90505f61377b8280613776670de0b6b3a7640000600261428b565b613cd1565b90505f6137968284613776670de0b6b3a7640000600361428b565b9050806137a38385614231565b61110d9190614231565b5f611d7b8383670de0b6b3a7640000613cd1565b6040805180820190915260148152731b585e081d5a5b9d0c4c8e08195e18d95959195960621b60208201525f906001600160801b03831115611dd05760405162461bcd60e51b81526004016108ff919061474e565b5f610b87613827620f424084614231565b613832600186614231565b869190613cd1565b5f81613847600182614278565b613851858761428b565b61292c9190614231565b5f815f0361386a57505f919050565b5f6138736112ea565b905080831061388457611d7b610f66565b5f6138926121078584614278565b90505f670de0b6b3a76400006138a6611bdf565b6138b0908461428b565b6138ba91906142a2565b90505f6138e7827f0000000000000000000000000000000000000000000000000000000000000000611ca1565b90505f6138f2610f66565b9050808210613901575f61390b565b61390b8282614278565b979650505050505050565b5f61391f610a8f565b5f8054919250906127109061394590600160501b900467ffffffffffffffff1682614231565b613972610b3c857f0000000000000000000000000000000000000000000000000000000000000000611c0a565b61397c919061428b565b61398691906142a2565b9050805f03613993575050565b610dd97f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000008484613cdd565b805f036139e95750565b60405163238d657960e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063238d6579906124c29060089085903090600401614760565b805f03613a445750565b6040516350d8cd4b60e01b81526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906350d8cd4b90613a999060089085905f90309081906004016147a0565b60408051808303815f875af1158015613ab4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610dd9919061460e565b604051636e553f6560e01b8152600481018290523060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690636e553f65906044016020604051808303815f875af1158015613b42573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114df9190614206565b604051636eb1769f60e11b81523060048201526001600160a01b03848116602483015282919084169063dd62ed3e90604401602060405180830381865afa158015613bb3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bd79190614206565b1015610dd957613bf16001600160a01b038316845f613e7d565b610dd96001600160a01b0383168483613e7d565b606082471015613c665760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016108ff565b5f80866001600160a01b03168587604051613c8191906147dc565b5f6040518083038185875af1925050503d805f8114613cbb576040519150601f19603f3d011682016040523d82523d5f602084013e613cc0565b606091505b509150915061390b87838387613f11565b5f8161292c848661428b565b5f8115801590613cef57506004548210155b15610b8757600654613d0b906001600160a01b03168684613b66565b6005546001600160a01b0386811691161480613d3457506005546001600160a01b038581169116145b15613dbf5760408051610100810182526001600160a01b0380881680835287821660208085018290525f92835260078152858320918352528381205462ffffff168385015230606084015242608084015260a0830187905260c0830186905260e08301526006549251631b67c43360e31b81529192169063db3e21989061340b908490600401614630565b6005546001600160a01b039081165f818152600760208181526040808420868b168552825280842054958b16845291815281832084845281528183205491519294613e1d948a9462ffffff92831694919391909216918c910161469a565b60408051601f1981840301815260065460a084018352818452306020850152428484015260608401889052608084018790529151631e51809360e31b81529093506001600160a01b039091169163f28c04989161350d919060040161473c565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052613ece8482613f89565b613f0b576040516001600160a01b03841660248201525f6044820152613f0190859063095ea7b360e01b90606401611b52565b613f0b84826126eb565b50505050565b60608315613f7f5782515f03613f78576001600160a01b0385163b613f785760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016108ff565b5081610b87565b610b87838361402a565b5f805f846001600160a01b031684604051613fa491906147dc565b5f604051808303815f865af19150503d805f8114613fdd576040519150601f19603f3d011682016040523d82523d5f602084013e613fe2565b606091505b509150915081801561400c57508051158061400c57508080602001905181019061400c91906143f1565b801561402157506001600160a01b0385163b15155b95945050505050565b81511561403a5781518083602001fd5b8060405162461bcd60e51b81526004016108ff919061474e565b80356001600160a01b038116811461293a575f80fd5b5f6020828403121561407a575f80fd5b611d7b82614054565b5f60208284031215614093575f80fd5b5035919050565b5f805f606084860312156140ac575f80fd5b6140b584614054565b92506140c360208501614054565b9150604084013562ffffff811681146140da575f80fd5b809150509250925092565b8015158114610a8c575f80fd5b5f60208284031215614102575f80fd5b8135611d7b816140e5565b803561ffff8116811461293a575f80fd5b5f806040838503121561412f575f80fd5b6141388361410d565b91506141466020840161410d565b90509250929050565b5f5b83811015614169578181015183820152602001614151565b50505f910152565b5f815180845261418881602086016020860161414f565b601f01601f19169290920160200192915050565b8215158152604060208201525f610b876040830184614171565b5f80604083850312156141c7575f80fd5b6141d083614054565b946020939093013593505050565b5f80604083850312156141ef575f80fd5b6141f883614054565b915061414660208401614054565b5f60208284031215614216575f80fd5b5051919050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115611c9b57611c9b61421d565b634e487b7160e01b5f52601260045260245ffd5b5f60208284031215614268575f80fd5b815160ff81168114611d7b575f80fd5b81810381811115611c9b57611c9b61421d565b8082028115828204841417611c9b57611c9b61421d565b5f826142bc57634e487b7160e01b5f52601260045260245ffd5b500490565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff811182821017156142fe576142fe6142c1565b604052919050565b80516001600160801b038116811461293a575f80fd5b5f6060828403121561432c575f80fd5b6040516060810181811067ffffffffffffffff8211171561434f5761434f6142c1565b6040528251815261436260208401614306565b602082015261437360408401614306565b60408201529392505050565b80546001600160a01b039081168352600182015481166020840152600282015481166040840152600382015416606083015260040154608090910152565b61010081016143cc828761437f565b60a08201949094526001600160a01b0392831660c0820152911660e090910152919050565b5f60208284031215614401575f80fd5b8151611d7b816140e5565b602080825282518282018190525f9190848201906040850190845b8181101561444357835183529284019291840191600101614427565b50909695505050505050565b5f6020808385031215614460575f80fd5b825167ffffffffffffffff80821115614477575f80fd5b818501915085601f83011261448a575f80fd5b81518181111561449c5761449c6142c1565b8060051b91506144ad8483016142d5565b81815291830184019184810190888411156144c6575f80fd5b938501935b838510156144e4578451825293850193908501906144cb565b98975050505050505050565b634e487b7160e01b5f52603260045260245ffd5b5f60c08284031215614514575f80fd5b60405160c0810181811067ffffffffffffffff82111715614537576145376142c1565b60405261454383614306565b815261455160208401614306565b602082015261456260408401614306565b604082015261457360608401614306565b606082015261458460808401614306565b608082015261459560a08401614306565b60a08201529392505050565b6001600160801b038181168382160190808211156145c1576145c161421d565b5092915050565b5f6101206145d6838861437f565b60a08301959095525060c08101929092526001600160a01b031660e082015261010081018290525f9181019190915261014001919050565b5f806040838503121561461f575f80fd5b505080516020909101519092909150565b6101008101611c9b828480516001600160a01b03908116835260208083015182169084015260408083015162ffffff16908401526060808301518216908401526080808301519084015260a0828101519084015260c0808301519084015260e09182015116910152565b6bffffffffffffffffffffffff19606096871b811682526001600160e81b031960e896871b8116601484015294871b811660178301529290941b909216602b840152921b909116602e82015260420190565b5f815160a0845261470060a0850182614171565b6020848101516001600160a01b031690860152604080850151908601526060808501519086015260809384015193909401929092525090919050565b602081525f611d7b60208301846146ec565b602081525f611d7b6020830184614171565b5f61010061476e838761437f565b60a0830194909452506001600160a01b039190911660c082015260e081018290525f9181019190915261012001919050565b61012081016147af828861437f565b60a082019590955260c08101939093526001600160a01b0391821660e08401521661010090910152919050565b5f82516147ed81846020870161414f565b919091019291505056fea264697066735822122036e1c05f22693a25fcf2164d78ddf6b5a361cfbf84dfeb52c15642d4ae9aafa964736f6c63430008170033a2646970667358221220124bf9188917a632c38071a8dc8af8a8210488057bd927a01407d971a1ae31a564736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001b5f15dcb82d25f91c65b53cee151e8b9fbdd2710000000000000000000000005a74cb32d36f2f517db6f7b0a0591e09b22cde69000000000000000000000000604e586f17ce106b64185a7a0d2c1da5bace711e00000000000000000000000016388463d60ffe0661cf7f1f31a7d658ac790ff7000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff52000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564
-----Decoded View---------------
Arg [0] : _management (address): 0x1b5f15DCb82d25f91c65b53CEe151E8b9fBdD271
Arg [1] : _performanceFeeRecipient (address): 0x5A74Cb32D36f2f517DB6f7b0A0591e09b22cDE69
Arg [2] : _keeper (address): 0x604e586F17cE106B64185A7a0d2c1Da5bAce711E
Arg [3] : _emergencyAdmin (address): 0x16388463d60FFE0661Cf7F1f31a7D658aC790ff7
Arg [4] : _gov (address): 0xFEB4acf3df3cDEA7399794D0869ef76A6EfAff52
Arg [5] : _morpho (address): 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb
Arg [6] : _router (address): 0xE592427A0AEce92De3Edee1F18E0157C05861564
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000001b5f15dcb82d25f91c65b53cee151e8b9fbdd271
Arg [1] : 0000000000000000000000005a74cb32d36f2f517db6f7b0a0591e09b22cde69
Arg [2] : 000000000000000000000000604e586f17ce106b64185a7a0d2c1da5bace711e
Arg [3] : 00000000000000000000000016388463d60ffe0661cf7f1f31a7d658ac790ff7
Arg [4] : 000000000000000000000000feb4acf3df3cdea7399794d0869ef76a6efaff52
Arg [5] : 000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
Arg [6] : 000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.