Source Code
Latest 25 from a total of 280 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Redeem | 41173228 | 3 days ago | IN | 0 ETH | 0.00000301 | ||||
| Redeem | 41136860 | 3 days ago | IN | 0 ETH | 0.000006 | ||||
| Redeem | 41069162 | 5 days ago | IN | 0 ETH | 0.00002739 | ||||
| Redeem | 41069151 | 5 days ago | IN | 0 ETH | 0.00003528 | ||||
| Deposit | 41000777 | 7 days ago | IN | 0 ETH | 0.00000629 | ||||
| Deposit | 40978700 | 7 days ago | IN | 0 ETH | 0.00000787 | ||||
| Deposit | 40945043 | 8 days ago | IN | 0 ETH | 0.00000178 | ||||
| Redeem | 40858736 | 10 days ago | IN | 0 ETH | 0.00000508 | ||||
| Redeem | 40858719 | 10 days ago | IN | 0 ETH | 0.00000547 | ||||
| Deposit | 40858698 | 10 days ago | IN | 0 ETH | 0.00000625 | ||||
| Deposit | 40802988 | 11 days ago | IN | 0 ETH | 0.00000371 | ||||
| Redeem | 40795279 | 11 days ago | IN | 0 ETH | 0.00000508 | ||||
| Deposit | 40795204 | 11 days ago | IN | 0 ETH | 0.00000466 | ||||
| Redeem | 40741998 | 13 days ago | IN | 0 ETH | 0.00000117 | ||||
| Deposit | 40741983 | 13 days ago | IN | 0 ETH | 0.00000108 | ||||
| Redeem | 40737024 | 13 days ago | IN | 0 ETH | 0.00000352 | ||||
| Redeem | 40715994 | 13 days ago | IN | 0 ETH | 0.00000087 | ||||
| Deposit | 40715974 | 13 days ago | IN | 0 ETH | 0.00000081 | ||||
| Redeem | 40678660 | 14 days ago | IN | 0 ETH | 0.00000305 | ||||
| Redeem | 40658785 | 14 days ago | IN | 0 ETH | 0.00000126 | ||||
| Redeem | 40658727 | 14 days ago | IN | 0 ETH | 0.00000126 | ||||
| Redeem | 40658713 | 14 days ago | IN | 0 ETH | 0.00000127 | ||||
| Redeem | 40658689 | 14 days ago | IN | 0 ETH | 0.00000132 | ||||
| Deposit | 40591457 | 16 days ago | IN | 0 ETH | 0.00000306 | ||||
| Deposit | 40590583 | 16 days ago | IN | 0 ETH | 0.00000284 |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LeverageRouter
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 19000 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
// Dependency imports
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IMorpho} from "@morpho-blue/interfaces/IMorpho.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
// Internal imports
import {ILendingAdapter} from "../interfaces/ILendingAdapter.sol";
import {ILeverageManager} from "../interfaces/ILeverageManager.sol";
import {ILeverageToken} from "../interfaces/ILeverageToken.sol";
import {ILeverageRouter} from "../interfaces/periphery/ILeverageRouter.sol";
import {IVeloraAdapter} from "../interfaces/periphery/IVeloraAdapter.sol";
import {IMulticallExecutor} from "../interfaces/periphery/IMulticallExecutor.sol";
import {ActionData} from "../types/DataTypes.sol";
/**
* @dev The LeverageRouter contract is an immutable periphery contract that facilitates the use of flash loans and swaps
* to deposit and redeem equity from LeverageTokens.
*
* The high-level deposit flow is as follows:
* 1. The sender calls `deposit` with the amount of collateral from the sender to deposit, the amount of debt to flash loan
* (which will be swapped to collateral), the minimum amount of shares to receive, and the calldata to execute for
* the swap of the flash loaned debt to collateral
* 2. The LeverageRouter will flash loan the debt asset amount and execute the calldata to swap it to collateral
* 3. The LeverageRouter will use the collateral from the swapped debt and the collateral from the sender for the deposit
* into the LeverageToken, receiving LeverageToken shares and debt in return
* 4. The LeverageRouter will use the debt received from the deposit to repay the flash loan
* 6. The LeverageRouter will transfer the LeverageToken shares and any surplus debt assets to the sender
*
* The high-level redeem flow is the same as the deposit flow, but in reverse.
*
* @custom:contact [email protected]
*/
contract LeverageRouter is ILeverageRouter, ReentrancyGuardTransient {
/// @inheritdoc ILeverageRouter
ILeverageManager public immutable leverageManager;
/// @inheritdoc ILeverageRouter
IMorpho public immutable morpho;
/// @notice Creates a new LeverageRouter
/// @param _leverageManager The LeverageManager contract
/// @param _morpho The Morpho core protocol contract
constructor(ILeverageManager _leverageManager, IMorpho _morpho) {
leverageManager = _leverageManager;
morpho = _morpho;
}
/// @inheritdoc ILeverageRouter
function convertEquityToCollateral(ILeverageToken token, uint256 equityInCollateralAsset)
public
view
returns (uint256 collateral)
{
uint256 collateralRatio = leverageManager.getLeverageTokenState(token).collateralRatio;
ILendingAdapter lendingAdapter = leverageManager.getLeverageTokenLendingAdapter(token);
uint256 baseRatio = leverageManager.BASE_RATIO();
if (lendingAdapter.getCollateral() == 0 && lendingAdapter.getDebt() == 0) {
uint256 initialCollateralRatio = leverageManager.getLeverageTokenInitialCollateralRatio(token);
collateral = Math.mulDiv(
equityInCollateralAsset, initialCollateralRatio, initialCollateralRatio - baseRatio, Math.Rounding.Ceil
);
} else if (collateralRatio == type(uint256).max) {
collateral = equityInCollateralAsset;
} else {
collateral =
Math.mulDiv(equityInCollateralAsset, collateralRatio, collateralRatio - baseRatio, Math.Rounding.Ceil);
}
return collateral;
}
/// @inheritdoc ILeverageRouter
function previewDeposit(ILeverageToken token, uint256 collateralFromSender)
external
view
returns (ActionData memory previewData)
{
uint256 collateral = convertEquityToCollateral(token, collateralFromSender);
return leverageManager.previewDeposit(token, collateral);
}
/// @inheritdoc ILeverageRouter
function deposit(
ILeverageToken leverageToken,
uint256 collateralFromSender,
uint256 flashLoanAmount,
uint256 minShares,
IMulticallExecutor multicallExecutor,
IMulticallExecutor.Call[] calldata swapCalls
) external nonReentrant {
bytes memory depositData = abi.encode(
DepositParams({
sender: msg.sender,
leverageToken: leverageToken,
collateralFromSender: collateralFromSender,
minShares: minShares,
multicallExecutor: multicallExecutor,
swapCalls: swapCalls
})
);
morpho.flashLoan(
address(leverageManager.getLeverageTokenDebtAsset(leverageToken)),
flashLoanAmount,
abi.encode(MorphoCallbackData({action: LeverageRouterAction.Deposit, data: depositData}))
);
}
/// @inheritdoc ILeverageRouter
function redeem(
ILeverageToken token,
uint256 shares,
uint256 minCollateralForSender,
IMulticallExecutor multicallExecutor,
IMulticallExecutor.Call[] calldata swapCalls
) external nonReentrant {
uint256 debtRequired = leverageManager.previewRedeem(token, shares).debt;
bytes memory redeemData = abi.encode(
RedeemParams({
sender: msg.sender,
leverageToken: token,
shares: shares,
minCollateralForSender: minCollateralForSender,
multicallExecutor: multicallExecutor,
swapCalls: swapCalls
})
);
morpho.flashLoan(
address(leverageManager.getLeverageTokenDebtAsset(token)),
debtRequired,
abi.encode(MorphoCallbackData({action: LeverageRouterAction.Redeem, data: redeemData}))
);
}
/// @inheritdoc ILeverageRouter
function redeemWithVelora(
ILeverageToken token,
uint256 shares,
uint256 minCollateralForSender,
IVeloraAdapter veloraAdapter,
address augustus,
IVeloraAdapter.Offsets calldata offsets,
bytes calldata swapData
) external nonReentrant {
uint256 debtRequired = leverageManager.previewRedeem(token, shares).debt;
bytes memory redeemData = abi.encode(
RedeemWithVeloraParams({
sender: msg.sender,
leverageToken: token,
shares: shares,
minCollateralForSender: minCollateralForSender,
veloraAdapter: veloraAdapter,
augustus: augustus,
offsets: offsets,
swapData: swapData
})
);
morpho.flashLoan(
address(leverageManager.getLeverageTokenDebtAsset(token)),
debtRequired,
abi.encode(MorphoCallbackData({action: LeverageRouterAction.RedeemWithVelora, data: redeemData}))
);
}
/// @notice Morpho flash loan callback function
/// @param loanAmount Amount of asset flash loaned
/// @param data Encoded data passed to `morpho.flashLoan`
function onMorphoFlashLoan(uint256 loanAmount, bytes calldata data) external {
if (msg.sender != address(morpho)) revert Unauthorized();
MorphoCallbackData memory callbackData = abi.decode(data, (MorphoCallbackData));
if (callbackData.action == LeverageRouterAction.Deposit) {
DepositParams memory params = abi.decode(callbackData.data, (DepositParams));
_depositAndRepayMorphoFlashLoan(params, loanAmount);
} else if (callbackData.action == LeverageRouterAction.Redeem) {
RedeemParams memory params = abi.decode(callbackData.data, (RedeemParams));
_redeemAndRepayMorphoFlashLoan(params, loanAmount);
} else if (callbackData.action == LeverageRouterAction.RedeemWithVelora) {
RedeemWithVeloraParams memory params = abi.decode(callbackData.data, (RedeemWithVeloraParams));
_redeemWithVeloraAndRepayMorphoFlashLoan(params, loanAmount);
}
}
/// @notice Executes the deposit into a LeverageToken by flash loaning the debt asset, swapping it to collateral,
/// depositing into the LeverageToken with the sender's collateral, and using the resulting debt to repay the flash loan.
/// Any surplus debt assets after repaying the flash loan are given to the sender.
/// @param params Params for the deposit into a LeverageToken
/// @param debtLoan Amount of debt asset flash loaned
function _depositAndRepayMorphoFlashLoan(DepositParams memory params, uint256 debtLoan) internal {
IERC20 collateralAsset = leverageManager.getLeverageTokenCollateralAsset(params.leverageToken);
IERC20 debtAsset = leverageManager.getLeverageTokenDebtAsset(params.leverageToken);
// Transfer the collateral from the sender for the deposit
// slither-disable-next-line arbitrary-send-erc20
SafeERC20.safeTransferFrom(collateralAsset, params.sender, address(this), params.collateralFromSender);
// Swap the debt asset received from the flash loan to the collateral asset, used to deposit into the LeverageToken
SafeERC20.safeTransfer(debtAsset, address(params.multicallExecutor), debtLoan);
IERC20[] memory tokens = new IERC20[](2);
tokens[0] = collateralAsset;
tokens[1] = debtAsset;
params.multicallExecutor.multicallAndSweep(params.swapCalls, tokens);
// The sum of the collateral from the swap and the collateral from the sender
uint256 totalCollateral = IERC20(collateralAsset).balanceOf(address(this));
// Use the collateral from the swap and the collateral from the sender for the deposit into the LeverageToken
SafeERC20.forceApprove(collateralAsset, address(leverageManager), totalCollateral);
uint256 shares = leverageManager.deposit(params.leverageToken, totalCollateral, params.minShares).shares;
// Transfer any surplus debt assets to the sender
uint256 debtBalance = debtAsset.balanceOf(address(this));
if (debtLoan < debtBalance) {
SafeERC20.safeTransfer(debtAsset, params.sender, debtBalance - debtLoan);
}
// Transfer shares received from the deposit to the deposit sender
SafeERC20.safeTransfer(params.leverageToken, params.sender, shares);
// Approve morpho to transfer debt assets to repay the flash loan
// Note: if insufficient debt is available to repay the flash loan, the transaction will revert when Morpho
// attempts to transfer the debt assets to repay the flash loan
SafeERC20.forceApprove(debtAsset, address(morpho), debtLoan);
}
/// @notice Executes the redeem from a LeverageToken by flash loaning the debt asset, swapping the collateral asset
/// to the debt asset using arbitrary calldata, using the resulting debt to repay the flash loan, and transferring
/// the remaining collateral asset and debt assets to the sender
/// @param params Params for the redeem from a LeverageToken, using arbitrary calldata for the swap
/// @param debtLoanAmount Amount of debt asset flash loaned
function _redeemAndRepayMorphoFlashLoan(RedeemParams memory params, uint256 debtLoanAmount) internal {
IERC20 collateralAsset = leverageManager.getLeverageTokenCollateralAsset(params.leverageToken);
IERC20 debtAsset = leverageManager.getLeverageTokenDebtAsset(params.leverageToken);
// Transfer the shares from the sender
// slither-disable-next-line arbitrary-send-erc20
SafeERC20.safeTransferFrom(params.leverageToken, params.sender, address(this), params.shares);
// Use the debt from the flash loan to redeem the shares from the sender
SafeERC20.forceApprove(debtAsset, address(leverageManager), debtLoanAmount);
// slither-disable-next-line unused-return
uint256 collateralWithdrawn =
leverageManager.redeem(params.leverageToken, params.shares, params.minCollateralForSender).collateral;
// Swap the collateral asset received from the redeem to the debt asset, used to repay the flash loan.
SafeERC20.safeTransfer(collateralAsset, address(params.multicallExecutor), collateralWithdrawn);
IERC20[] memory tokens = new IERC20[](2);
tokens[0] = collateralAsset;
tokens[1] = debtAsset;
params.multicallExecutor.multicallAndSweep(params.swapCalls, tokens);
// The remaining collateral after the arbitrary swap calls is available for the sender
uint256 collateralForSender = collateralAsset.balanceOf(address(this));
// The remaining debt after the arbitrary swap calls is available for the sender, minus
// the amount of debt for repaying the flash loan
uint256 debtBalance = debtAsset.balanceOf(address(this));
uint256 debtForSender = debtBalance > debtLoanAmount ? debtBalance - debtLoanAmount : 0;
// Check slippage on collateral the sender receives
if (collateralForSender < params.minCollateralForSender) {
revert CollateralSlippageTooHigh(collateralForSender, params.minCollateralForSender);
}
// Transfer remaining collateral to the sender
if (collateralForSender > 0) {
SafeERC20.safeTransfer(collateralAsset, params.sender, collateralForSender);
}
// Transfer any remaining debt assets to the sender
if (debtForSender > 0) {
SafeERC20.safeTransfer(debtAsset, params.sender, debtForSender);
}
// Approve Morpho to spend the debt asset to repay the flash loan
SafeERC20.forceApprove(debtAsset, address(morpho), debtLoanAmount);
}
/// @notice Executes the redeem from a LeverageToken by flash loaning the debt asset, swapping the collateral asset
/// to the debt asset using Velora, using the resulting debt to repay the flash loan, and transferring the remaining
/// collateral asset to the sender
/// @param params Params for the redeem from a LeverageToken using Velora
/// @param debtLoanAmount Amount of debt asset flash loaned
function _redeemWithVeloraAndRepayMorphoFlashLoan(RedeemWithVeloraParams memory params, uint256 debtLoanAmount)
internal
{
IERC20 collateralAsset = leverageManager.getLeverageTokenCollateralAsset(params.leverageToken);
IERC20 debtAsset = leverageManager.getLeverageTokenDebtAsset(params.leverageToken);
// Transfer the shares from the sender
// slither-disable-next-line arbitrary-send-erc20
SafeERC20.safeTransferFrom(params.leverageToken, params.sender, address(this), params.shares);
// Use the debt from the flash loan to redeem the shares from the sender
SafeERC20.forceApprove(debtAsset, address(leverageManager), debtLoanAmount);
uint256 collateralWithdrawn =
leverageManager.redeem(params.leverageToken, params.shares, params.minCollateralForSender).collateral;
// Use the VeloraAdapter to swap the collateral asset received from the redeem to the debt asset, used to repay the flash loan.
// The excess collateral asset sent back to this LeverageRouter is for the sender of the redeem
// slither-disable-next-line arbitrary-send-erc20
SafeERC20.safeTransfer(collateralAsset, address(params.veloraAdapter), collateralWithdrawn);
uint256 collateralForSender = params.veloraAdapter.buy(
params.augustus,
params.swapData,
address(collateralAsset),
address(debtAsset),
debtLoanAmount,
params.offsets,
address(this)
);
// Check slippage
if (collateralForSender < params.minCollateralForSender) {
revert CollateralSlippageTooHigh(collateralForSender, params.minCollateralForSender);
}
// Transfer remaining collateral to the sender
if (collateralForSender > 0) {
SafeERC20.safeTransfer(collateralAsset, params.sender, collateralForSender);
}
// Approve Morpho to spend the debt asset to repay the flash loan
SafeERC20.forceApprove(debtAsset, address(morpho), debtLoanAmount);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(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 towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* 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²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
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²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_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.
uint256 twos = denominator & (0 - denominator);
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²⁵⁶ / 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²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, 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;
}
}
/**
* @dev 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) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* 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;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
type Id is bytes32;
struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
}
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
uint256 supplyShares;
uint128 borrowShares;
uint128 collateral;
}
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
uint128 totalSupplyAssets;
uint128 totalSupplyShares;
uint128 totalBorrowAssets;
uint128 totalBorrowShares;
uint128 lastUpdate;
uint128 fee;
}
struct Authorization {
address authorizer;
address authorized;
bool isAuthorized;
uint256 nonce;
uint256 deadline;
}
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
/// @notice The EIP-712 domain separator.
/// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on another chain sharing
/// the same chain id because the domain separator would be the same.
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice The owner of the contract.
/// @dev It has the power to change the owner.
/// @dev It has the power to set fees on markets and set the fee recipient.
/// @dev It has the power to enable but not disable IRMs and LLTVs.
function owner() external view returns (address);
/// @notice The fee recipient of all markets.
/// @dev The recipient receives the fees of a given market through a supply position on that market.
function feeRecipient() external view returns (address);
/// @notice Whether the `irm` is enabled.
function isIrmEnabled(address irm) external view returns (bool);
/// @notice Whether the `lltv` is enabled.
function isLltvEnabled(uint256 lltv) external view returns (bool);
/// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
/// @dev Anyone is authorized to modify their own positions, regardless of this variable.
function isAuthorized(address authorizer, address authorized) external view returns (bool);
/// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
function nonce(address authorizer) external view returns (uint256);
/// @notice Sets `newOwner` as `owner` of the contract.
/// @dev Warning: No two-step transfer ownership.
/// @dev Warning: The owner can be set to the zero address.
function setOwner(address newOwner) external;
/// @notice Enables `irm` as a possible IRM for market creation.
/// @dev Warning: It is not possible to disable an IRM.
function enableIrm(address irm) external;
/// @notice Enables `lltv` as a possible LLTV for market creation.
/// @dev Warning: It is not possible to disable a LLTV.
function enableLltv(uint256 lltv) external;
/// @notice Sets the `newFee` for the given market `marketParams`.
/// @param newFee The new fee, scaled by WAD.
/// @dev Warning: The recipient can be the zero address.
function setFee(MarketParams memory marketParams, uint256 newFee) external;
/// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
/// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
/// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
/// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
function setFeeRecipient(address newFeeRecipient) external;
/// @notice Creates the market `marketParams`.
/// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
/// Morpho behaves as expected:
/// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
/// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
/// burn functions are not supported.
/// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
/// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
/// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
/// - The IRM should not re-enter Morpho.
/// - The oracle should return a price with the correct scaling.
/// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties
/// (funds could get stuck):
/// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue.
/// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and
/// `toSharesDown` overflow.
/// - The IRM can revert on `borrowRate`.
/// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest`
/// overflow.
/// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and
/// `liquidate` from being used under certain market conditions.
/// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or
/// the computation of `assetsRepaid` in `liquidate` overflow.
/// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
/// the point where `totalBorrowShares` is very large and borrowing overflows.
function createMarket(MarketParams memory marketParams) external;
/// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupply` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
/// amount of shares is given for full compatibility and precision.
/// @dev Supplying a large amount can revert for overflow.
/// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to supply assets to.
/// @param assets The amount of assets to supply.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased supply position.
/// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
/// @return assetsSupplied The amount of assets supplied.
/// @return sharesSupplied The amount of shares minted.
function supply(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsSupplied, uint256 sharesSupplied);
/// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
/// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
/// conversion roundings between shares and assets.
/// @param marketParams The market to withdraw assets from.
/// @param assets The amount of assets to withdraw.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the supply position.
/// @param receiver The address that will receive the withdrawn assets.
/// @return assetsWithdrawn The amount of assets withdrawn.
/// @return sharesWithdrawn The amount of shares burned.
function withdraw(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);
/// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
/// given for full compatibility and precision.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Borrowing a large amount can revert for overflow.
/// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to borrow assets from.
/// @param assets The amount of assets to borrow.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased borrow position.
/// @param receiver The address that will receive the borrowed assets.
/// @return assetsBorrowed The amount of assets borrowed.
/// @return sharesBorrowed The amount of shares minted.
function borrow(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);
/// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoReplay` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
/// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
/// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
/// roundings between shares and assets.
/// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
/// @param marketParams The market to repay assets to.
/// @param assets The amount of assets to repay.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the debt position.
/// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
/// @return assetsRepaid The amount of assets repaid.
/// @return sharesRepaid The amount of shares burned.
function repay(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
/// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupplyCollateral` function with the given `data`.
/// @dev Interest are not accrued since it's not required and it saves gas.
/// @dev Supplying a large amount can revert for overflow.
/// @param marketParams The market to supply collateral to.
/// @param assets The amount of collateral to supply.
/// @param onBehalf The address that will own the increased collateral position.
/// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
external;
/// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
/// @param marketParams The market to withdraw collateral from.
/// @param assets The amount of collateral to withdraw.
/// @param onBehalf The address of the owner of the collateral position.
/// @param receiver The address that will receive the collateral assets.
function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
external;
/// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
/// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
/// `onMorphoLiquidate` function with the given `data`.
/// @dev Either `seizedAssets` or `repaidShares` should be zero.
/// @dev Seizing more than the collateral balance will underflow and revert without any error message.
/// @dev Repaying more than the borrow balance will underflow and revert without any error message.
/// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
/// @param marketParams The market of the position.
/// @param borrower The owner of the position.
/// @param seizedAssets The amount of collateral to seize.
/// @param repaidShares The amount of shares to repay.
/// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
/// @return The amount of assets seized.
/// @return The amount of assets repaid.
function liquidate(
MarketParams memory marketParams,
address borrower,
uint256 seizedAssets,
uint256 repaidShares,
bytes memory data
) external returns (uint256, uint256);
/// @notice Executes a flash loan.
/// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
/// markets combined, plus donations).
/// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
/// - `flashFee` is zero.
/// - `maxFlashLoan` is the token's balance of this contract.
/// - The receiver of `assets` is the caller.
/// @param token The token to flash loan.
/// @param assets The amount of assets to flash loan.
/// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
function flashLoan(address token, uint256 assets, bytes calldata data) external;
/// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
/// @param authorized The authorized address.
/// @param newIsAuthorized The new authorization status.
function setAuthorization(address authorized, bool newIsAuthorized) external;
/// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
/// @dev Warning: Reverts if the signature has already been submitted.
/// @dev The signature is malleable, but it has no impact on the security here.
/// @dev The nonce is passed as argument to be able to revert with a different error message.
/// @param authorization The `Authorization` struct.
/// @param signature The signature.
function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;
/// @notice Accrues interest for the given market `marketParams`.
function accrueInterest(MarketParams memory marketParams) external;
/// @notice Returns the data stored on the different `slots`.
function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}
/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user)
external
view
returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
/// accrual.
function market(Id id)
external
view
returns (
uint128 totalSupplyAssets,
uint128 totalSupplyShares,
uint128 totalBorrowAssets,
uint128 totalBorrowShares,
uint128 lastUpdate,
uint128 fee
);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id)
external
view
returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
}
/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user) external view returns (Position memory p);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
/// interest accrual.
function market(Id id) external view returns (Market memory m);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id) external view returns (MarketParams memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuardTransient.sol)
pragma solidity ^0.8.24;
import {TransientSlot} from "./TransientSlot.sol";
/**
* @dev Variant of {ReentrancyGuard} that uses transient storage.
*
* NOTE: This variant only works on networks where EIP-1153 is available.
*
* _Available since v5.1._
*/
abstract contract ReentrancyGuardTransient {
using TransientSlot for *;
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant REENTRANCY_GUARD_STORAGE =
0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_reentrancyGuardEntered()) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
}
function _nonReentrantAfter() private {
REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 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 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @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.encodeCall(token.transfer, (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.encodeCall(token.transferFrom, (from, to, 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.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @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.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @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 silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
// Dependency imports
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ILendingAdapter {
/// @notice Error thrown when the caller is unauthorized to call a function
error Unauthorized();
/// @notice Returns the address of the collateral asset
/// @return collateralAsset Address of the collateral asset
function getCollateralAsset() external view returns (IERC20 collateralAsset);
/// @notice Returns the address of the debt asset
/// @return debtAsset Address of the debt asset
function getDebtAsset() external view returns (IERC20 debtAsset);
/// @notice Converts an amount of collateral asset to a debt asset amount based on the lending pool oracle
/// @param collateral Collateral amount
/// @return debt Amount of debt asset
function convertCollateralToDebtAsset(uint256 collateral) external view returns (uint256 debt);
/// @notice Converts an amount of debt asset to a collateral asset amount based on the lending pool oracle
/// @param debt Debt amount
/// @return collateral Amount of collateral asset
function convertDebtToCollateralAsset(uint256 debt) external view returns (uint256 collateral);
/// @notice Returns total collateral of the position held by the lending adapter
/// @return collateral Total collateral of the position held by the lending adapter
function getCollateral() external view returns (uint256 collateral);
/// @notice Returns the total collateral of the position held by the lending adapter denominated in the debt asset
/// @return collateral Total collateral of the position held by the lending adapter denominated in the debt asset
function getCollateralInDebtAsset() external view returns (uint256 collateral);
/// @notice Returns the total debt of the position held by the lending adapter
/// @return debt Total debt of the position held by the lending adapter
function getDebt() external view returns (uint256 debt);
/// @notice Returns the total equity of the position held by the lending adapter denominated in the collateral asset
/// @return equity Equity of the position held by the lending adapter
function getEquityInCollateralAsset() external view returns (uint256 equity);
/// @notice Returns the total equity of the position held by the lending adapter denominated in the debt asset
/// @return equity Equity of the position held by the lending adapter
/// @dev Equity is calculated as collateral - debt
function getEquityInDebtAsset() external view returns (uint256 equity);
/// @notice Supplies collateral assets to the lending pool
/// @param amount Amount of assets to supply
function addCollateral(uint256 amount) external;
/// @notice Post-LeverageToken creation hook. Used for any validation logic or initialization after a LeverageToken
/// is created using this adapter
/// @param creator The address of the creator of the LeverageToken
/// @param leverageToken The address of the LeverageToken that was created
/// @dev This function is called in `LeverageManager.createNewLeverageToken` after the new LeverageToken is created
function postLeverageTokenCreation(address creator, address leverageToken) external;
/// @notice Withdraws collateral assets from the lending pool
/// @param amount Amount of collateral assets to withdraw
function removeCollateral(uint256 amount) external;
/// @notice Borrows debt assets from the lending pool
/// @param amount Amount of debt assets to borrow
function borrow(uint256 amount) external;
/// @notice Repays debt to the lending pool
/// @param amount Amount of debt assets to repay
function repay(uint256 amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
// Dependency imports
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
// Internal imports
import {IFeeManager} from "./IFeeManager.sol";
import {IRebalanceAdapterBase} from "./IRebalanceAdapterBase.sol";
import {ILeverageToken} from "./ILeverageToken.sol";
import {IBeaconProxyFactory} from "./IBeaconProxyFactory.sol";
import {ILendingAdapter} from "./ILendingAdapter.sol";
import {ActionData, LeverageTokenState, RebalanceAction, LeverageTokenConfig} from "src/types/DataTypes.sol";
interface ILeverageManager is IFeeManager {
/// @notice Error thrown when someone tries to set zero address for collateral or debt asset when creating a LeverageToken
error InvalidLeverageTokenAssets();
/// @notice Error thrown when collateral ratios are invalid for an action
error InvalidCollateralRatios();
/// @notice Error thrown when slippage is too high during mint/redeem
/// @param actual The actual amount of tokens received
/// @param expected The expected amount of tokens to receive
error SlippageTooHigh(uint256 actual, uint256 expected);
/// @notice Error thrown when caller is not authorized to rebalance
/// @param token The LeverageToken to rebalance
/// @param caller The caller of the rebalance function
error NotRebalancer(ILeverageToken token, address caller);
/// @notice Error thrown when a LeverageToken's initial collateral ratio is invalid (must be greater than the base ratio)
/// @param initialCollateralRatio The initial collateral ratio that is invalid
error InvalidLeverageTokenInitialCollateralRatio(uint256 initialCollateralRatio);
/// @notice Error thrown when a LeverageToken's state after rebalance is invalid
/// @param token The LeverageToken that has invalid state after rebalance
error InvalidLeverageTokenStateAfterRebalance(ILeverageToken token);
/// @notice Event emitted when the LeverageManager is initialized
/// @param leverageTokenFactory The factory for creating new LeverageTokens
event LeverageManagerInitialized(IBeaconProxyFactory leverageTokenFactory);
/// @notice Error thrown when attempting to rebalance a LeverageToken that is not eligible for rebalance
error LeverageTokenNotEligibleForRebalance();
/// @notice Event emitted when a new LeverageToken is created
/// @param token The new LeverageToken
/// @param collateralAsset The collateral asset of the LeverageToken
/// @param debtAsset The debt asset of the LeverageToken
/// @param config The config of the LeverageToken
event LeverageTokenCreated(
ILeverageToken indexed token, IERC20 collateralAsset, IERC20 debtAsset, LeverageTokenConfig config
);
/// @notice Event emitted when a user mints LeverageToken shares
/// @param token The LeverageToken
/// @param sender The sender of the mint
/// @param actionData The action data of the mint
event Mint(ILeverageToken indexed token, address indexed sender, ActionData actionData);
/// @notice Event emitted when a user rebalances a LeverageToken
/// @param token The LeverageToken
/// @param sender The sender of the rebalance
/// @param stateBefore The state of the LeverageToken before the rebalance
/// @param stateAfter The state of the LeverageToken after the rebalance
/// @param actions The actions that were taken
event Rebalance(
ILeverageToken indexed token,
address indexed sender,
LeverageTokenState stateBefore,
LeverageTokenState stateAfter,
RebalanceAction[] actions
);
/// @notice Event emitted when a user redeems LeverageToken shares
/// @param token The LeverageToken
/// @param sender The sender of the redeem
/// @param actionData The action data of the redeem
event Redeem(ILeverageToken indexed token, address indexed sender, ActionData actionData);
/// @notice Returns the base collateral ratio
/// @return baseRatio Base collateral ratio
function BASE_RATIO() external view returns (uint256);
/// @notice Converts an amount of collateral to an amount of debt for a LeverageToken, based on the current
/// collateral ratio of the LeverageToken
/// @param token LeverageToken to convert collateral to debt for
/// @param collateral Amount of collateral to convert to debt
/// @param rounding Rounding mode to use for the conversion
/// @return debt Amount of debt that correspond to the collateral
/// @dev For deposits/mints, Math.Rounding.Floor should be used. For withdraws/redeems, Math.Rounding.Ceil should be used.
function convertCollateralToDebt(ILeverageToken token, uint256 collateral, Math.Rounding rounding)
external
view
returns (uint256 debt);
/// @notice Converts an amount of collateral to an amount of shares for a LeverageToken, based on the current
/// collateral ratio of the LeverageToken
/// @param token LeverageToken to convert collateral to shares for
/// @param collateral Amount of collateral to convert to shares
/// @param rounding Rounding mode to use for the conversion
/// @return shares Amount of shares that correspond to the collateral
/// @dev For deposits/mints, Math.Rounding.Floor should be used. For withdraws/redeems, Math.Rounding.Ceil should be used.
function convertCollateralToShares(ILeverageToken token, uint256 collateral, Math.Rounding rounding)
external
view
returns (uint256 shares);
/// @notice Converts an amount of debt to an amount of collateral for a LeverageToken, based on the current
/// collateral ratio of the LeverageToken
/// @param token LeverageToken to convert debt to collateral for
/// @param debt Amount of debt to convert to collateral
/// @param rounding Rounding mode to use for the conversion
/// @return collateral Amount of collateral that correspond to the debt amount
/// @dev For deposits/mints, Math.Rounding.Ceil should be used. For withdraws/redeems, Math.Rounding.Floor should be used.
function convertDebtToCollateral(ILeverageToken token, uint256 debt, Math.Rounding rounding)
external
view
returns (uint256 collateral);
/// @notice Converts an amount of shares to an amount of collateral for a LeverageToken, based on the current
/// collateral ratio of the LeverageToken
/// @param token LeverageToken to convert shares to collateral for
/// @param shares Amount of shares to convert to collateral
/// @param rounding Rounding mode to use for the conversion
/// @return collateral Amount of collateral that correspond to the shares
/// @dev For deposits/mints, Math.Rounding.Ceil should be used. For withdraws/redeems, Math.Rounding.Floor should be used.
function convertSharesToCollateral(ILeverageToken token, uint256 shares, Math.Rounding rounding)
external
view
returns (uint256 collateral);
/// @notice Converts an amount of shares to an amount of debt for a LeverageToken, based on the current
/// collateral ratio of the LeverageToken
/// @param token LeverageToken to convert shares to debt for
/// @param shares Amount of shares to convert to debt
/// @param rounding Rounding mode to use for the conversion
/// @return debt Amount of debt that correspond to the shares
/// @dev For deposits/mints, Math.Rounding.Floor should be used. For withdraws/redeems, Math.Rounding.Ceil should be used.
function convertSharesToDebt(ILeverageToken token, uint256 shares, Math.Rounding rounding)
external
view
returns (uint256 debt);
/// @notice Converts an amount of shares to an amount of equity in collateral asset for a LeverageToken, based on the
/// price oracle used by the underlying lending adapter and state of the LeverageToken
/// @param token LeverageToken to convert shares to equity in collateral asset for
/// @param shares Amount of shares to convert to equity in collateral asset
/// @return equityInCollateralAsset Amount of equity in collateral asset that correspond to the shares
function convertToAssets(ILeverageToken token, uint256 shares)
external
view
returns (uint256 equityInCollateralAsset);
/// @notice Converts an amount of equity in collateral asset to an amount of shares for a LeverageToken, based on the
/// price oracle used by the underlying lending adapter and state of the LeverageToken
/// @param token LeverageToken to convert equity in collateral asset to shares for
/// @param equityInCollateralAsset Amount of equity in collateral asset to convert to shares
/// @return shares Amount of shares that correspond to the equity in collateral asset
function convertToShares(ILeverageToken token, uint256 equityInCollateralAsset)
external
view
returns (uint256 shares);
/// @notice Returns the factory for creating new LeverageTokens
/// @return factory Factory for creating new LeverageTokens
function getLeverageTokenFactory() external view returns (IBeaconProxyFactory factory);
/// @notice Returns the lending adapter for a LeverageToken
/// @param token LeverageToken to get lending adapter for
/// @return adapter Lending adapter for the LeverageToken
function getLeverageTokenLendingAdapter(ILeverageToken token) external view returns (ILendingAdapter adapter);
/// @notice Returns the collateral asset for a LeverageToken
/// @param token LeverageToken to get collateral asset for
/// @return collateralAsset Collateral asset for the LeverageToken
function getLeverageTokenCollateralAsset(ILeverageToken token) external view returns (IERC20 collateralAsset);
/// @notice Returns the debt asset for a LeverageToken
/// @param token LeverageToken to get debt asset for
/// @return debtAsset Debt asset for the LeverageToken
function getLeverageTokenDebtAsset(ILeverageToken token) external view returns (IERC20 debtAsset);
/// @notice Returns the rebalance adapter for a LeverageToken
/// @param token LeverageToken to get the rebalance adapter for
/// @return adapter Rebalance adapter for the LeverageToken
function getLeverageTokenRebalanceAdapter(ILeverageToken token)
external
view
returns (IRebalanceAdapterBase adapter);
/// @notice Returns the entire configuration for a LeverageToken
/// @param token LeverageToken to get config for
/// @return config LeverageToken configuration
function getLeverageTokenConfig(ILeverageToken token) external view returns (LeverageTokenConfig memory config);
/// @notice Returns the initial collateral ratio for a LeverageToken
/// @param token LeverageToken to get initial collateral ratio for
/// @return initialCollateralRatio Initial collateral ratio for the LeverageToken
/// @dev Initial collateral ratio is followed when the LeverageToken has no shares and on mints when debt is 0.
function getLeverageTokenInitialCollateralRatio(ILeverageToken token)
external
view
returns (uint256 initialCollateralRatio);
/// @notice Returns all data required to describe current LeverageToken state - collateral, debt, equity and collateral ratio
/// @param token LeverageToken to query state for
/// @return state LeverageToken state
function getLeverageTokenState(ILeverageToken token) external view returns (LeverageTokenState memory state);
/// @notice Previews deposit function call and returns all required data
/// @param token LeverageToken to preview deposit for
/// @param collateral Amount of collateral to deposit
/// @return previewData Preview data for deposit
/// - collateral Amount of collateral that will be added to the LeverageToken and sent to the receiver
/// - debt Amount of debt that will be borrowed and sent to the receiver
/// - shares Amount of shares that will be minted to the receiver
/// - tokenFee Amount of shares that will be charged for the deposit that are given to the LeverageToken
/// - treasuryFee Amount of shares that will be charged for the deposit that are given to the treasury
/// @dev Sender should approve leverage manager to spend collateral amount of collateral asset
function previewDeposit(ILeverageToken token, uint256 collateral) external view returns (ActionData memory);
/// @notice Previews mint function call and returns all required data
/// @param token LeverageToken to preview mint for
/// @param shares Amount of shares to mint
/// @return previewData Preview data for mint
/// - collateral Amount of collateral that will be added to the LeverageToken and sent to the receiver
/// - debt Amount of debt that will be borrowed and sent to the receiver
/// - shares Amount of shares that will be minted to the receiver
/// - tokenFee Amount of shares that will be charged for the mint that are given to the LeverageToken
/// - treasuryFee Amount of shares that will be charged for the mint that are given to the treasury
/// @dev Sender should approve leverage manager to spend collateral amount of collateral asset
function previewMint(ILeverageToken token, uint256 shares) external view returns (ActionData memory);
/// @notice Previews redeem function call and returns all required data
/// @param token LeverageToken to preview redeem for
/// @param shares Amount of shares to redeem
/// @return previewData Preview data for redeem
/// - collateral Amount of collateral that will be removed from the LeverageToken and sent to the sender
/// - debt Amount of debt that will be taken from sender and repaid to the LeverageToken
/// - shares Amount of shares that will be burned from sender
/// - tokenFee Amount of shares that will be charged for the redeem that are given to the LeverageToken
/// - treasuryFee Amount of shares that will be charged for the redeem that are given to the treasury
/// @dev Sender should approve LeverageManager to spend debt amount of debt asset
function previewRedeem(ILeverageToken token, uint256 shares) external view returns (ActionData memory);
/// @notice Previews withdraw function call and returns all required data
/// @param token LeverageToken to preview withdraw for
/// @param collateral Amount of collateral to withdraw
/// @return previewData Preview data for withdraw
/// - collateral Amount of collateral that will be removed from the LeverageToken and sent to the sender
/// - debt Amount of debt that will be taken from sender and repaid to the LeverageToken
/// - shares Amount of shares that will be burned from sender
/// - tokenFee Amount of shares that will be charged for the redeem that are given to the LeverageToken
/// - treasuryFee Amount of shares that will be charged for the redeem that are given to the treasury
/// @dev Sender should approve LeverageManager to spend debt amount of debt asset
function previewWithdraw(ILeverageToken token, uint256 collateral) external view returns (ActionData memory);
/// @notice Creates a new LeverageToken with the given config
/// @param config Configuration of the LeverageToken
/// @param name Name of the LeverageToken
/// @param symbol Symbol of the LeverageToken
/// @return token Address of the new LeverageToken
function createNewLeverageToken(LeverageTokenConfig memory config, string memory name, string memory symbol)
external
returns (ILeverageToken token);
/// @notice Deposits collateral into a LeverageToken and mints shares to the sender
/// @param token LeverageToken to deposit into
/// @param collateral Amount of collateral to deposit
/// @param minShares Minimum number of shares to mint
/// @return depositData Action data for the deposit
/// - collateral Amount of collateral that was added, including any fees
/// - debt Amount of debt that was added
/// - shares Amount of shares minted to the sender
/// - tokenFee Amount of shares that was charged for the deposit that are given to the LeverageToken
/// - treasuryFee Amount of shares that was charged for the deposit that are given to the treasury
/// @dev Sender should approve leverage manager to spend collateral amount of collateral asset
function deposit(ILeverageToken token, uint256 collateral, uint256 minShares)
external
returns (ActionData memory);
/// @notice Mints shares of a LeverageToken to the sender
/// @param token LeverageToken to mint shares for
/// @param shares Amount of shares to mint
/// @param maxCollateral Maximum amount of collateral to use for minting
/// @return mintData Action data for the mint
/// - collateral Amount of collateral that was added, including any fees
/// - debt Amount of debt that was added
/// - shares Amount of shares minted to the sender
/// - tokenFee Amount of shares that was charged for the mint that are given to the LeverageToken
/// - treasuryFee Amount of shares that was charged for the mint that are given to the treasury
/// @dev Sender should approve leverage manager to spend collateral amount of collateral asset, which can be
/// previewed with previewMint
function mint(ILeverageToken token, uint256 shares, uint256 maxCollateral) external returns (ActionData memory);
/// @notice Redeems equity from a LeverageToken and burns shares from sender
/// @param token The LeverageToken to redeem from
/// @param shares The amount of shares to redeem
/// @param minCollateral The minimum amount of collateral to receive
/// @return actionData Data about the redeem
/// - collateral Amount of collateral that was removed from LeverageToken and sent to sender
/// - debt Amount of debt that was repaid to LeverageToken, taken from sender
/// - shares Amount of the sender's shares that were burned for the redeem
/// - tokenFee Amount of shares that was charged for the redeem that are given to the LeverageToken
/// - treasuryFee Amount of shares that was charged for the redeem that are given to the treasury
function redeem(ILeverageToken token, uint256 shares, uint256 minCollateral)
external
returns (ActionData memory actionData);
/// @notice Rebalances a LeverageToken based on provided actions
/// @param leverageToken LeverageToken to rebalance
/// @param actions Rebalance actions to execute (add collateral, remove collateral, borrow or repay)
/// @param tokenIn Token to transfer in. Transfer from caller to the LeverageManager contract
/// @param tokenOut Token to transfer out. Transfer from the LeverageManager contract to caller
/// @param amountIn Amount of tokenIn to transfer in
/// @param amountOut Amount of tokenOut to transfer out
/// @dev Anyone can call this function. At the end function will just check if the affected LeverageToken is in a
/// better state than before rebalance. Caller needs to calculate and to provide tokens for rebalancing and he needs
/// to specify tokens that he wants to receive
/// @dev Note: If the sender specifies less amountOut than the maximum amount they can retrieve for their specified
/// rebalance actions, the rebalance will still be successful. The remaining amount that could have been taken
/// out can be claimed by anyone by executing rebalance with that remaining amount in amountOut.
function rebalance(
ILeverageToken leverageToken,
RebalanceAction[] calldata actions,
IERC20 tokenIn,
IERC20 tokenOut,
uint256 amountIn,
uint256 amountOut
) external;
/// @notice Withdraws collateral from a LeverageToken and burns shares from sender
/// @param token The LeverageToken to withdraw from
/// @param collateral The amount of collateral to withdraw
/// @param maxShares The maximum amount of shares to burn
/// @return actionData Data about the withdraw
/// - collateral Amount of collateral that was removed from LeverageToken and sent to sender
/// - debt Amount of debt that was repaid to LeverageToken, taken from sender
/// - shares Amount of the sender's shares that were burned for the withdraw
/// - tokenFee Amount of shares that was charged for the withdraw that are given to the LeverageToken
/// - treasuryFee Amount of shares that was charged for the withdraw that are given to the treasury
function withdraw(ILeverageToken token, uint256 collateral, uint256 maxShares)
external
returns (ActionData memory actionData);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// Dependency imports
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ILeverageToken is IERC20 {
/// @notice Event emitted when the leverage token is initialized
/// @param name The name of the LeverageToken
/// @param symbol The symbol of the LeverageToken
event LeverageTokenInitialized(string name, string symbol);
/// @notice Converts an amount of LeverageToken shares to an amount of equity in collateral asset, based on the
/// price oracle used by the underlying lending adapter and state of the LeverageToken.
/// @notice Equity in collateral asset is equal to the difference between collateral and debt denominated
/// in the collateral asset.
/// @param shares The number of shares to convert to equity in collateral asset
/// @return assets Amount of equity in collateral asset that correspond to the shares
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/// @notice Converts an amount of equity in collateral asset to an amount of LeverageToken shares, based on the
/// price oracle used by the underlying lending adapter and state of the LeverageToken.
/// @notice Equity in collateral asset is equal to the difference between collateral and debt denominated
/// in the collateral asset.
/// @param assets The amount of equity in collateral asset to convert to shares
/// @return shares The number of shares that correspond to the equity in collateral asset
function convertToShares(uint256 assets) external view returns (uint256 shares);
/// @notice Mints new tokens to the specified address
/// @param to The address to mint tokens to
/// @param amount The amount of tokens to mint
/// @dev Only the owner can call this function. Owner should be the LeverageManager contract
function mint(address to, uint256 amount) external;
/// @notice Burns tokens from the specified address
/// @param from The address to burn tokens from
/// @param amount The amount of tokens to burn
/// @dev Only the owner can call this function. Owner should be the LeverageManager contract
function burn(address from, uint256 amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
// Dependency imports
import {IMorpho} from "@morpho-blue/interfaces/IMorpho.sol";
// Internal imports
import {ILeverageManager} from "../ILeverageManager.sol";
import {ILeverageToken} from "../ILeverageToken.sol";
import {IMulticallExecutor} from "./IMulticallExecutor.sol";
import {IVeloraAdapter} from "./IVeloraAdapter.sol";
import {ActionData} from "src/types/DataTypes.sol";
interface ILeverageRouter {
enum LeverageRouterAction {
Deposit,
Redeem,
RedeemWithVelora
}
/// @notice Deposit related parameters to pass to the Morpho flash loan callback handler for deposits
struct DepositParams {
// Address of the sender of the deposit
address sender;
// LeverageToken to deposit into
ILeverageToken leverageToken;
// Amount of collateral from the sender to deposit
uint256 collateralFromSender;
// Minimum amount of shares (LeverageTokens) to receive
uint256 minShares;
// multicall executor to use for the swap
IMulticallExecutor multicallExecutor;
// External calls to execute for the swap of flash loaned debt to collateral
IMulticallExecutor.Call[] swapCalls;
}
/// @notice Morpho flash loan callback data to pass to the Morpho flash loan callback handler
struct MorphoCallbackData {
LeverageRouterAction action;
bytes data;
}
/// @notice Redeem related parameters to pass to the Morpho flash loan callback handler for redeems
struct RedeemParams {
// Address of the sender of the redeem
address sender;
// LeverageToken to redeem from
ILeverageToken leverageToken;
// Amount of shares to redeem
uint256 shares;
// Minimum amount of collateral for the sender to receive
uint256 minCollateralForSender;
// multicall executor to use for the swap
IMulticallExecutor multicallExecutor;
// External calls to execute for the swap of flash loaned debt to collateral
IMulticallExecutor.Call[] swapCalls;
}
/// @notice Redeem related parameters to pass to the Morpho flash loan callback handler for redeems using Velora
struct RedeemWithVeloraParams {
// Address of the sender of the redeem, whose shares will be burned and the collateral asset will be transferred to
address sender;
// LeverageToken to redeem from
ILeverageToken leverageToken;
// Amount of shares to redeem
uint256 shares;
// Minimum amount of collateral for the sender to receive
uint256 minCollateralForSender;
// Velora adapter to use for the swap
IVeloraAdapter veloraAdapter;
// Velora Augustus contract to use for the swap
address augustus;
// Offsets for the Velora swap
IVeloraAdapter.Offsets offsets;
// Calldata for the Velora swap
bytes swapData;
}
/// @notice Error thrown when the remaining collateral is less than the minimum collateral for the sender to receive
/// @param remainingCollateral The remaining collateral after the swap
/// @param minCollateralForSender The minimum collateral for the sender to receive
error CollateralSlippageTooHigh(uint256 remainingCollateral, uint256 minCollateralForSender);
/// @notice Error thrown when the collateral from the swap + the collateral from the sender is less than the collateral required for the deposit
/// @param available The collateral from the swap + the collateral from the sender, available for the deposit
/// @param required The collateral required for the deposit
error InsufficientCollateralForDeposit(uint256 available, uint256 required);
/// @notice Error thrown when the cost of a swap exceeds the maximum allowed cost
/// @param actualCost The actual cost of the swap
/// @param maxCost The maximum allowed cost of the swap
error MaxSwapCostExceeded(uint256 actualCost, uint256 maxCost);
/// @notice Error thrown when the caller is not authorized to execute a function
error Unauthorized();
/// @notice Converts an amount of equity to an amount of collateral for a LeverageToken, based on the current
/// collateral ratio of the LeverageToken
/// @param token LeverageToken to convert equity to collateral for
/// @param equityInCollateralAsset Amount of equity to convert to collateral, denominated in the collateral asset of the LeverageToken
/// @return collateral Amount of collateral that correspond to the equity amount
function convertEquityToCollateral(ILeverageToken token, uint256 equityInCollateralAsset)
external
view
returns (uint256 collateral);
/// @notice The LeverageManager contract
/// @return _leverageManager The LeverageManager contract
function leverageManager() external view returns (ILeverageManager _leverageManager);
/// @notice The Morpho core protocol contract
/// @return _morpho The Morpho core protocol contract
function morpho() external view returns (IMorpho _morpho);
/// @notice Previews the deposit function call for an amount of equity and returns all required data
/// @param token LeverageToken to preview deposit for
/// @param collateralFromSender The amount of collateral from the sender to deposit
/// @return previewData Preview data for deposit
/// - collateral Total amount of collateral that will be added to the LeverageToken (including collateral from swapping flash loaned debt)
/// - debt Amount of debt that will be borrowed
/// - shares Amount of shares that will be minted
/// - tokenFee Amount of shares that will be charged for the deposit that are given to the LeverageToken
/// - treasuryFee Amount of shares that will be charged for the deposit that are given to the treasury
function previewDeposit(ILeverageToken token, uint256 collateralFromSender)
external
view
returns (ActionData memory);
/// @notice Deposits collateral into a LeverageToken and mints shares to the sender. Any surplus debt received from
/// the deposit of (collateralFromSender + debt swapped to collateral) is given to the sender.
/// @param leverageToken LeverageToken to deposit into
/// @param collateralFromSender Collateral asset amount from the sender to deposit
/// @param flashLoanAmount Amount of debt to flash loan, which is swapped to collateral and used to deposit into the LeverageToken
/// @param minShares Minimum number of shares expected to be received by the sender
/// @param multicallExecutor multicall executor to use for the swap
/// @param swapCalls External calls to execute for the swap of flash loaned debt to collateral for the LeverageToken deposit.
/// The calls are executed by the `multicallExecutor` contract after receiving the flash loaned debt. Thus, for any encoded approvals and
/// swaps that require the `from` address to be encoded,`from` must be set to the `multicallExecutor` contract address. The receiver of the swap
/// must either be the `multicallExecutor` or this `LeverageRouter` contract - any leftover collateral and debt assets after the execution
/// of the calls are swept to this `LeverageRouter` contract.
function deposit(
ILeverageToken leverageToken,
uint256 collateralFromSender,
uint256 flashLoanAmount,
uint256 minShares,
IMulticallExecutor multicallExecutor,
IMulticallExecutor.Call[] calldata swapCalls
) external;
/// @notice Redeems an amount of shares of a LeverageToken and transfers collateral asset to the sender, using arbitrary
/// calldata for the swap of collateral from the redemption to debt to repay the flash loan. Any surplus debt assets
/// after repaying the flash loan are given to the sender along with the remaining collateral asset.
/// @param token LeverageToken to redeem from
/// @param shares Amount of shares to redeem
/// @param minCollateralForSender Minimum amount of collateral for the sender to receive
/// @param multicallExecutor multicall executor to use for the swap
/// @param swapCalls External calls to execute for the swap of collateral from the redemption to debt to repay the flash loan.
/// The calls are executed by the `multicallExecutor` contract after receiving the collateral from the redemption. Thus, for
/// any encoded approvals and swaps that require the `from` address to be encoded, `from` must be set to the `multicallExecutor`
/// contract address. The receiver of the swap must either be the `multicallExecutor` or this `LeverageRouter` contract - any leftover
/// collateral and debt assets after the execution of the calls are swept to this `LeverageRouter` contract.
function redeem(
ILeverageToken token,
uint256 shares,
uint256 minCollateralForSender,
IMulticallExecutor multicallExecutor,
IMulticallExecutor.Call[] calldata swapCalls
) external;
/// @notice Redeems an amount of shares of a LeverageToken and transfers collateral asset to the sender, using Velora
/// for the required swap of collateral from the redemption to debt to repay the flash loan
/// @param token LeverageToken to redeem from
/// @param shares Amount of shares to redeem
/// @param minCollateralForSender Minimum amount of collateral for the sender to receive
/// @param veloraAdapter Velora adapter to use for the swap
/// @param augustus Velora Augustus address to use for the swap
/// @param offsets Offsets to use for updating the Velora Augustus calldata
/// @param swapData Velora swap calldata to use for the swap
/// @dev The calldata should be for using Velora for an exact output swap of the collateral asset to the debt asset
/// for the debt amount flash loaned, which is equal to the amount of debt removed from the LeverageToken for the
/// redemption of shares. The exact output amount in the calldata is updated on chain to match the up to date debt
/// amount for the redemption of shares, which typically occurs due to borrow interest accrual and price changes
/// between off chain and on chain execution
function redeemWithVelora(
ILeverageToken token,
uint256 shares,
uint256 minCollateralForSender,
IVeloraAdapter veloraAdapter,
address augustus,
IVeloraAdapter.Offsets calldata offsets,
bytes calldata swapData
) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.26;
/// @notice Interface of Velora Adapter.
/// @dev This adapter was copied from the original version implemented by Morpho
/// https://github.com/morpho-org/bundler3/blob/4887f33299ba6e60b54a51237b16e7392dceeb97/src/interfaces/IParaswapAdapter.sol
interface IVeloraAdapter {
/// @notice The offsets are:
/// - exactAmount, the offset in augustus calldata of the exact amount to sell / buy.
/// - limitAmount, the offset in augustus calldata of the minimum amount to buy / maximum amount to sell
/// - quotedAmount, the offset in augustus calldata of the initially quoted buy amount / initially quoted sell amount.
/// Set to 0 if the quoted amount is not present in augustus calldata so that it is not used.
struct Offsets {
uint256 exactAmount;
uint256 limitAmount;
uint256 quotedAmount;
}
/// @notice Thrown when the Augustus address is not in the Augustus registry
error InvalidAugustus(address augustus);
/// @notice Thrown when the receiver is invalid
error InvalidReceiver(address receiver);
/// @notice Buys an exact amount. Uses the entire balance of the inputToken in the adapter as the maximum input amount if
/// the amount to buy is adjusted.
/// @notice Compatibility with Augustus versions different from 6.2 is not guaranteed.
/// @notice This function should be used immediately after sending the inputToken to the adapter, in the same transaction.
/// @notice Any tokens remaining in the adapter (inputToken and outputToken) after a swap are transferred to the receiver
/// @notice The calldata must be for a "BUY" Augustus swap, not a "SELL".
/// @param augustus Address of the swapping contract. Must be in Velora's Augustus registry.
/// @param callData Swap data to call `augustus`. Contains routing information.
/// @param inputToken Token to sell.
/// @param outputToken Token to buy.
/// @param newOutputAmount Adjusted amount to buy. Will be used to update callData before sent to Augustus contract.
/// @param offsets Offsets in callData of the exact buy amount (`exactAmount`), maximum sell amount (`limitAmount`)
/// and quoted sell amount (`quotedAmount`).
/// @dev The quoted sell amount will change only if its offset is not zero.
/// @param receiver Address to which leftover `inputToken` assets will be sent. `outputToken` assets may also be
/// sent to this address if the receiver on the `callData` passed to `buy` is set to the VeloraAdapter.
/// @return excessInputAmount The amount of `inputToken` that was not used in the swap.
function buy(
address augustus,
bytes memory callData,
address inputToken,
address outputToken,
uint256 newOutputAmount,
Offsets calldata offsets,
address receiver
) external returns (uint256 excessInputAmount);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
// Dependency imports
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IMulticallExecutor {
/// @notice Struct containing the target, value, and data for a single external call.
struct Call {
address target; // Call target
uint256 value; // ETH value to send
bytes data; // Calldata to execute
}
/// @notice Executes a multicall and sweeps tokens afterwards
/// @param calls The calls to execute
/// @param tokens The tokens to sweep to the sender after executing the calls. To sweep ETH, include address(0).
function multicallAndSweep(Call[] calldata calls, IERC20[] calldata tokens) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {ILendingAdapter} from "src/interfaces/ILendingAdapter.sol";
import {IRebalanceAdapterBase} from "src/interfaces/IRebalanceAdapterBase.sol";
/// @dev Enum defining internal actions that a LendingAdapter can perform on a lending pool
enum ActionType {
AddCollateral,
RemoveCollateral,
Borrow,
Repay
}
/// @dev Enum defining actions that users can perform on a LeverageToken
enum ExternalAction {
Mint,
Redeem
}
/// @dev Struct that contains all data related to a LeverageToken action
struct ActionData {
/// @dev Amount of collateral added or withdrawn
uint256 collateral;
/// @dev Amount of debt borrowed or repaid
uint256 debt;
/// @dev Amount of shares the user gains or loses for the action (whether that be via minting, burning, or fees)
uint256 shares;
/// @dev Fee charged for the action to the leverage token, denominated in shares
uint256 tokenFee;
/// @dev Fee charged for the action to the treasury, denominated in shares
uint256 treasuryFee;
}
/// @dev Struct containing auction parameters
struct Auction {
/// @dev Whether the LeverageToken is over-collateralized
bool isOverCollateralized;
/// @dev Timestamp when the auction started
uint120 startTimestamp;
/// @dev Timestamp when the auction ends/ended
uint120 endTimestamp;
}
/// @dev Struct that contains the base LeverageToken config stored in LeverageManager
struct BaseLeverageTokenConfig {
/// @dev LendingAdapter for the LeverageToken
ILendingAdapter lendingAdapter;
/// @dev RebalanceAdapter for the LeverageToken
IRebalanceAdapterBase rebalanceAdapter;
}
/// @dev Struct that contains the entire LeverageToken config
struct LeverageTokenConfig {
/// @dev LendingAdapter for the LeverageToken
ILendingAdapter lendingAdapter;
/// @dev RebalanceAdapter for the LeverageToken
IRebalanceAdapterBase rebalanceAdapter;
/// @dev Fee for mint action, defined as a percentage
uint256 mintTokenFee;
/// @dev Fee for redeem action, defined as a percentage
uint256 redeemTokenFee;
}
/// @dev Struct that contains all data describing the state of a LeverageToken
struct LeverageTokenState {
/// @dev Collateral denominated in debt asset
uint256 collateralInDebtAsset;
/// @dev Debt
uint256 debt;
/// @dev Equity denominated in debt asset
uint256 equity;
/// @dev Collateral ratio on 8 decimals
uint256 collateralRatio;
}
/// @dev Struct that contains all data related to a rebalance action
struct RebalanceAction {
/// @dev Type of action to perform
ActionType actionType;
/// @dev Amount to perform the action with
uint256 amount;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/TransientSlot.sol)
// This file was procedurally generated from scripts/generate/templates/TransientSlot.js.
pragma solidity ^0.8.24;
/**
* @dev Library for reading and writing value-types to specific transient storage slots.
*
* Transient slots are often used to store temporary values that are removed after the current transaction.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* * Example reading and writing values using transient storage:
* ```solidity
* contract Lock {
* using TransientSlot for *;
*
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
*
* modifier locked() {
* require(!_LOCK_SLOT.asBoolean().tload());
*
* _LOCK_SLOT.asBoolean().tstore(true);
* _;
* _LOCK_SLOT.asBoolean().tstore(false);
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library TransientSlot {
/**
* @dev UDVT that represent a slot holding a address.
*/
type AddressSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a AddressSlot.
*/
function asAddress(bytes32 slot) internal pure returns (AddressSlot) {
return AddressSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bool.
*/
type BooleanSlot is bytes32;
/**
* @dev Cast an arbitrary slot to a BooleanSlot.
*/
function asBoolean(bytes32 slot) internal pure returns (BooleanSlot) {
return BooleanSlot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a bytes32.
*/
type Bytes32Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Bytes32Slot.
*/
function asBytes32(bytes32 slot) internal pure returns (Bytes32Slot) {
return Bytes32Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a uint256.
*/
type Uint256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Uint256Slot.
*/
function asUint256(bytes32 slot) internal pure returns (Uint256Slot) {
return Uint256Slot.wrap(slot);
}
/**
* @dev UDVT that represent a slot holding a int256.
*/
type Int256Slot is bytes32;
/**
* @dev Cast an arbitrary slot to a Int256Slot.
*/
function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
return Int256Slot.wrap(slot);
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(AddressSlot slot) internal view returns (address value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(AddressSlot slot, address value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(BooleanSlot slot) internal view returns (bool value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(BooleanSlot slot, bool value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Bytes32Slot slot, bytes32 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Uint256Slot slot) internal view returns (uint256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Uint256Slot slot, uint256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
/**
* @dev Load the value held at location `slot` in transient storage.
*/
function tload(Int256Slot slot) internal view returns (int256 value) {
assembly ("memory-safe") {
value := tload(slot)
}
}
/**
* @dev Store `value` at location `slot` in transient storage.
*/
function tstore(Int256Slot slot, int256 value) internal {
assembly ("memory-safe") {
tstore(slot, value)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* 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.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) 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
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {ILeverageToken} from "./ILeverageToken.sol";
import {ExternalAction} from "src/types/DataTypes.sol";
interface IFeeManager {
/// @notice Error emitted when `FEE_MANAGER_ROLE` tries to set fee higher than `MAX_FEE`
/// @param fee The fee that was set
/// @param maxFee The maximum fee that can be set
error FeeTooHigh(uint256 fee, uint256 maxFee);
/// @notice Error emitted when trying to set the treasury address to the zero address
error ZeroAddressTreasury();
/// @notice Emitted when the default management fee for new LeverageTokens is updated
/// @param fee The default management fee for new LeverageTokens, 100_00 is 100%
event DefaultManagementFeeAtCreationSet(uint256 fee);
/// @notice Emitted when a LeverageToken fee is set for a specific action
/// @param leverageToken The LeverageToken that the fee was set for
/// @param action The action that the fee was set for
/// @param fee The fee that was set
event LeverageTokenActionFeeSet(ILeverageToken indexed leverageToken, ExternalAction indexed action, uint256 fee);
/// @notice Emitted when the management fee is charged for a LeverageToken
/// @param leverageToken The LeverageToken that the management fee was charged for
/// @param sharesFee The amount of shares that were minted to the treasury
event ManagementFeeCharged(ILeverageToken indexed leverageToken, uint256 sharesFee);
/// @notice Emitted when the management fee is set
/// @param token The LeverageToken that the management fee was set for
/// @param fee The fee that was set
event ManagementFeeSet(ILeverageToken indexed token, uint256 fee);
/// @notice Emitted when a treasury fee is set for a specific action
/// @param action The action that the fee was set for
/// @param fee The fee that was set
event TreasuryActionFeeSet(ExternalAction indexed action, uint256 fee);
/// @notice Emitted when the treasury address is set
/// @param treasury The address of the treasury
event TreasurySet(address treasury);
/// @notice Function that charges any accrued management fees for the LeverageToken by minting shares to the treasury
/// @param token LeverageToken to charge management fee for
/// @dev If the treasury is not set, the management fee is not charged (shares are not minted to the treasury) but
/// still accrues
function chargeManagementFee(ILeverageToken token) external;
/// @notice Returns the default management fee for new LeverageTokens
/// @return fee The default management fee for new LeverageTokens, 100_00 is 100%
function getDefaultManagementFeeAtCreation() external view returns (uint256 fee);
/// @notice Returns the total supply of the LeverageToken adjusted for any accrued management fees
/// @param token LeverageToken to get fee adjusted total supply for
/// @return totalSupply Fee adjusted total supply of the LeverageToken
function getFeeAdjustedTotalSupply(ILeverageToken token) external view returns (uint256 totalSupply);
/// @notice Returns the timestamp of the most recent management fee accrual for a LeverageToken
/// @param leverageToken The LeverageToken to get the timestamp for
/// @return timestamp The timestamp of the most recent management fee accrual
function getLastManagementFeeAccrualTimestamp(ILeverageToken leverageToken)
external
view
returns (uint120 timestamp);
/// @notice Returns the LeverageToken fee for a specific action
/// @param leverageToken The LeverageToken to get fee for
/// @param action The action to get fee for
/// @return fee Fee for action, 100_00 is 100%
function getLeverageTokenActionFee(ILeverageToken leverageToken, ExternalAction action)
external
view
returns (uint256 fee);
/// @notice Returns the management fee for a LeverageToken
/// @param token LeverageToken to get management fee for
/// @return fee Management fee for the LeverageToken, 100_00 is 100%
function getManagementFee(ILeverageToken token) external view returns (uint256 fee);
/// @notice Returns the address of the treasury
/// @return treasury The address of the treasury
function getTreasury() external view returns (address treasury);
/// @notice Returns the treasury fee for a specific action
/// @param action Action to get fee for
/// @return fee Fee for action, 100_00 is 100%
function getTreasuryActionFee(ExternalAction action) external view returns (uint256 fee);
/// @notice Sets the default management fee for new LeverageTokens
/// @param fee The default management fee for new LeverageTokens, 100_00 is 100%
/// @dev Only `FEE_MANAGER_ROLE` can call this function
function setDefaultManagementFeeAtCreation(uint256 fee) external;
/// @notice Sets the management fee for a LeverageToken
/// @param token LeverageToken to set management fee for
/// @param fee Management fee, 100_00 is 100%
/// @dev Only `FEE_MANAGER_ROLE` can call this function
function setManagementFee(ILeverageToken token, uint256 fee) external;
/// @notice Sets the address of the treasury. The treasury receives all treasury and management fees from the
/// LeverageManager.
/// @param treasury The address of the treasury
/// @dev Only `FEE_MANAGER_ROLE` can call this function
function setTreasury(address treasury) external;
/// @notice Sets the treasury fee for a specific action
/// @param action The action to set fee for
/// @param fee The fee for action, 100_00 is 100%
/// @dev Only `FEE_MANAGER_ROLE` can call this function.
function setTreasuryActionFee(ExternalAction action, uint256 fee) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {ILeverageToken} from "src/interfaces/ILeverageToken.sol";
import {LeverageTokenState} from "src/types/DataTypes.sol";
/// @title IRebalanceAdapterBase
/// @notice Interface for the base RebalanceAdapter
/// @dev This is minimal interface required for the RebalanceAdapter to be used by the LeverageManager
interface IRebalanceAdapterBase {
/// @notice Returns the initial collateral ratio for a LeverageToken. Must be > `LeverageManager.BASE_RATIO()`
/// @param token LeverageToken to get initial collateral ratio for
/// @return initialCollateralRatio Initial collateral ratio for the LeverageToken
/// @dev Initial collateral ratio is followed when the LeverageToken has no shares and on mints when debt is 0.
function getLeverageTokenInitialCollateralRatio(ILeverageToken token)
external
view
returns (uint256 initialCollateralRatio);
/// @notice Validates if a LeverageToken is eligible for rebalance
/// @param token LeverageToken to check eligibility for
/// @param state State of the LeverageToken
/// @param caller Caller of the function
/// @return isEligible True if LeverageToken is eligible for rebalance, false otherwise
function isEligibleForRebalance(ILeverageToken token, LeverageTokenState memory state, address caller)
external
view
returns (bool isEligible);
/// @notice Validates if the LeverageToken's state after rebalance is valid
/// @param token LeverageToken to validate state for
/// @param stateBefore State of the LeverageToken before rebalance
/// @return isValid True if state after rebalance is valid, false otherwise
function isStateAfterRebalanceValid(ILeverageToken token, LeverageTokenState memory stateBefore)
external
view
returns (bool isValid);
/// @notice Post-LeverageToken creation hook. Used for any validation logic or initialization after a LeverageToken
/// is created using this adapter
/// @param creator The address of the creator of the LeverageToken
/// @param leverageToken The address of the LeverageToken that was created
/// @dev This function is called in `LeverageManager.createNewLeverageToken` after the new LeverageToken is created
function postLeverageTokenCreation(address creator, address leverageToken) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
interface IBeaconProxyFactory {
/// @notice Error thrown when an invalid address is provided
error InvalidAddress();
/// @notice Emitted when a new BeaconProxy is created
/// @param proxy The address of the new BeaconProxy
/// @param data The data used to initialize the BeaconProxy
/// @param baseSalt The base salt used for deterministic deployment
event BeaconProxyCreated(address indexed proxy, bytes data, bytes32 baseSalt);
/// @notice Computes the address of a BeaconProxy before deployment
/// @param sender The address that will deploy the BeaconProxy using the factory
/// @param data The initialization data passed to the BeaconProxy
/// @param baseSalt The base salt used for deterministic deployment
/// @return proxy The predicted address of the BeaconProxy
function computeProxyAddress(address sender, bytes memory data, bytes32 baseSalt)
external
view
returns (address proxy);
/// @notice Returns the number of BeaconProxys deployed by the factory
/// @return _numProxies The number of BeaconProxys deployed by the factory
function numProxies() external view returns (uint256 _numProxies);
/// @notice Creates a new beacon proxy
/// @param data The initialization data passed to the proxy
/// @param baseSalt The base salt used for deterministic deployment
/// @return proxy The address of the new BeaconProxy
function createProxy(bytes memory data, bytes32 baseSalt) external returns (address proxy);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"remappings": [
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/",
"@morpho-blue/=lib/morpho-blue/src/",
"@morpho-blue-irm/=lib/morpho-blue-irm/src/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"morpho-blue-irm/=lib/morpho-blue-irm/src/",
"morpho-blue/=lib/morpho-blue/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"solmate/=lib/morpho-blue-irm/lib/solmate/src/"
],
"optimizer": {
"enabled": true,
"runs": 19000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract ILeverageManager","name":"_leverageManager","type":"address"},{"internalType":"contract IMorpho","name":"_morpho","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"remainingCollateral","type":"uint256"},{"internalType":"uint256","name":"minCollateralForSender","type":"uint256"}],"name":"CollateralSlippageTooHigh","type":"error"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"InsufficientCollateralForDeposit","type":"error"},{"inputs":[{"internalType":"uint256","name":"actualCost","type":"uint256"},{"internalType":"uint256","name":"maxCost","type":"uint256"}],"name":"MaxSwapCostExceeded","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[{"internalType":"contract ILeverageToken","name":"token","type":"address"},{"internalType":"uint256","name":"equityInCollateralAsset","type":"uint256"}],"name":"convertEquityToCollateral","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILeverageToken","name":"leverageToken","type":"address"},{"internalType":"uint256","name":"collateralFromSender","type":"uint256"},{"internalType":"uint256","name":"flashLoanAmount","type":"uint256"},{"internalType":"uint256","name":"minShares","type":"uint256"},{"internalType":"contract IMulticallExecutor","name":"multicallExecutor","type":"address"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IMulticallExecutor.Call[]","name":"swapCalls","type":"tuple[]"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"leverageManager","outputs":[{"internalType":"contract ILeverageManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morpho","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"loanAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onMorphoFlashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILeverageToken","name":"token","type":"address"},{"internalType":"uint256","name":"collateralFromSender","type":"uint256"}],"name":"previewDeposit","outputs":[{"components":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"tokenFee","type":"uint256"},{"internalType":"uint256","name":"treasuryFee","type":"uint256"}],"internalType":"struct ActionData","name":"previewData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ILeverageToken","name":"token","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"minCollateralForSender","type":"uint256"},{"internalType":"contract IMulticallExecutor","name":"multicallExecutor","type":"address"},{"components":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IMulticallExecutor.Call[]","name":"swapCalls","type":"tuple[]"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ILeverageToken","name":"token","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"minCollateralForSender","type":"uint256"},{"internalType":"contract IVeloraAdapter","name":"veloraAdapter","type":"address"},{"internalType":"address","name":"augustus","type":"address"},{"components":[{"internalType":"uint256","name":"exactAmount","type":"uint256"},{"internalType":"uint256","name":"limitAmount","type":"uint256"},{"internalType":"uint256","name":"quotedAmount","type":"uint256"}],"internalType":"struct IVeloraAdapter.Offsets","name":"offsets","type":"tuple"},{"internalType":"bytes","name":"swapData","type":"bytes"}],"name":"redeemWithVelora","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60c0346100df57601f612fd338819003918201601f19168301916001600160401b038311848410176100e35780849260409485528339810103126100df578051906001600160a01b03821682036100df5760200151906001600160a01b03821682036100df5760805260a052604051612edb90816100f882396080518181816101bf015281816102a6015281816103d50152818161089901528181610d7e015281816113c3015281816119cd01528181611be801526123ab015260a05181818160bc0152818161058e015281816107cb015281816119630152611cbc0152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe60806040526004361015610011575f80fd5b5f5f3560e01c8063280c8d9714611b165780632b1ae79714611ac85780632ed76c921461186557806331f570721461075f578063458bd4ef146102ca57806381fd6c021461025b578063b8f82b26146100e35763d8fbc83314610072575f80fd5b346100e057807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b80fd5b50346100e05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e0576101a660a0610120611e3c565b83608060405161012f81611f7f565b828152826020820152826040820152826060820152015261015260243582612393565b6040517fb8f82b2600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092166004830152602482015291829081906044820190565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9081156102505760a09291610223575b506080604051918051835260208101516020840152604081015160408401526060810151606084015201516080820152f35b6102439150823d8411610249575b61023b8183611f9b565b810190611fbe565b5f6101f1565b503d610231565b6040513d84823e3d90fd5b50346100e057807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100e0576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e05780610304611e3c565b602435906064359173ffffffffffffffffffffffffffffffffffffffff831680930361075a57610332611e5f565b9060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c3601126106e8576101043567ffffffffffffffff81116107565761037e903690600401611eb3565b949091610389612974565b6040517fcbe52ae300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018390527f000000000000000000000000000000000000000000000000000000000000000016969060a0816044818b5afa801561074b576020918a9161072c575b500151946040519361042185611f46565b338552602085019773ffffffffffffffffffffffffffffffffffffffff169586895260408601948552606086019060443582526080870192835260a087019373ffffffffffffffffffffffffffffffffffffffff1684526040519461048586611f63565b60a435865260c435602087015260e435604087015260c0880195865236906104ac92612038565b9460e08701958652604051998a9760208901602090525173ffffffffffffffffffffffffffffffffffffffff1660408901525173ffffffffffffffffffffffffffffffffffffffff1660608801525160808701525160a08601525173ffffffffffffffffffffffffffffffffffffffff1660c08501525173ffffffffffffffffffffffffffffffffffffffff1660e0840152516101008301610561916040809180518452602081015160208501520151910152565b5161016082016101409052610180820161057a91612156565b03601f198101845261058c9084611f9b565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690604051809581927fa792e3a800000000000000000000000000000000000000000000000000000000835260048301525a92602491602094fa928315610721576106379486946106ec575b506040516106459161061f82611f2a565b600282526020820152604051958691602083016122cd565b03601f198101865285611f9b565b803b156106e85773ffffffffffffffffffffffffffffffffffffffff85809461069e604051978896879586947fe0232b420000000000000000000000000000000000000000000000000000000086521660048501612327565b03925af18015610250576106d3575b507f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d80f35b816106dd91611f9b565b6100e057805f6106ad565b8480fd5b6106459194506107139060203d60201161071a575b61070b8183611f9b565b8101906122a1565b939061060e565b503d610701565b6040513d87823e3d90fd5b610745915060a03d60a0116102495761023b8183611f9b565b5f610410565b6040513d8b823e3d90fd5b8580fd5b505050fd5b50346100e05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e05760043560243567ffffffffffffffff8111611861576107b2903690600401611eb3565b919073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001692833303611839578101906020818303126106e85780359067ffffffffffffffff821161075657016040818303126106e8576040519161082a83611f2a565b81356003811015611835578352602082013567ffffffffffffffff811161183557610855920161206e565b90602081019182528051600381101561180857610d3c57506108819051602080825183010191016127fd565b9173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001692602081019373ffffffffffffffffffffffffffffffffffffffff85511691604051927ff3d4510b0000000000000000000000000000000000000000000000000000000084526004840152602083602481855afa928315610d31578793610d10575b5073ffffffffffffffffffffffffffffffffffffffff86511692604051937fa792e3a80000000000000000000000000000000000000000000000000000000085526004850152602084602481865afa938415610c69578894610cef575b506109a273ffffffffffffffffffffffffffffffffffffffff835116604084015190309084612c37565b60808201926109c98773ffffffffffffffffffffffffffffffffffffffff86511687612dbf565b604051906109d8606083611f9b565b600282526040366020840137896109ee83612a38565b9273ffffffffffffffffffffffffffffffffffffffff851680945273ffffffffffffffffffffffffffffffffffffffff610a2782612a72565b97818a1680995251169060a087015190823b15610ceb57610a7a928492836040518096819582947f29f77f1600000000000000000000000000000000000000000000000000000000845260048401612a82565b03925af1801561025057610cd2575b50506020602492604051938480927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa918215610cc7578a92610c93575b509081610ae38260a094610b6096612ca5565b73ffffffffffffffffffffffffffffffffffffffff8a5116918b606087015193604051968795869485937f0efe6a8b0000000000000000000000000000000000000000000000000000000085526004850160409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b03925af18015610c695760406020916024938b91610c74575b50015193604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115610c69578891610c19575b509073ffffffffffffffffffffffffffffffffffffffff80610bf79884610bf296958a10610bfa575b50511691511690612dbf565b612ca5565b80f35b610c1390610c0c8b8588511692612359565b9089612dbf565b5f610be6565b929190506020833d602011610c61575b81610c3660209383611f9b565b81010312610c5d57915190919073ffffffffffffffffffffffffffffffffffffffff610bbd565b5f80fd5b3d9150610c29565b6040513d8a823e3d90fd5b610c8d915060a03d60a0116102495761023b8183611f9b565b5f610b79565b91506020823d602011610cbf575b81610cae60209383611f9b565b81010312610c5d5790519080610ad0565b3d9150610ca1565b6040513d8c823e3d90fd5b81610cdc91611f9b565b610ce757895f610a89565b8980fd5b8380fd5b610d0991945060203d60201161071a5761070b8183611f9b565b925f610978565b610d2a91935060203d60201161071a5761070b8183611f9b565b915f61091b565b6040513d89823e3d90fd5b809493945160038110156117db576001036112485750610d669051602080825183010191016127fd565b9273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693602081019073ffffffffffffffffffffffffffffffffffffffff82511695604051967ff3d4510b0000000000000000000000000000000000000000000000000000000088526004880152602087602481845afa96871561123d57869761121c575b5073ffffffffffffffffffffffffffffffffffffffff83511692604051937fa792e3a80000000000000000000000000000000000000000000000000000000085526004850152602084602481855afa938415610d315787946111f4575b5060a08173ffffffffffffffffffffffffffffffffffffffff80610f0a94511691610e948288511660408901948551913091612c37565b610e9f8a878a612ca5565b511690519360608601948a865193604051968795869485937f2b83cccd0000000000000000000000000000000000000000000000000000000085526004850160409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b03925af1908115610d315787916111d5575b505196610f45608084019873ffffffffffffffffffffffffffffffffffffffff8a511683612dbf565b60405197610f5460608a611f9b565b6002895260403660208b0137610f6989612a38565b9873ffffffffffffffffffffffffffffffffffffffff8316809a5273ffffffffffffffffffffffffffffffffffffffff610fa282612a72565b92818816809452511660a0860151813b156111d157918a91610ff493836040518096819582947f29f77f1600000000000000000000000000000000000000000000000000000000845260048401612a82565b03925af1801561074b576111b8575b5060206024996040519a8b80927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa988915610c69578899611183575b506020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115610c69578891611151575b508680821115611148576110a291612359565b915b51808910611118575087610bf797986110ef575b5050806110c7575b5050612ca5565b73ffffffffffffffffffffffffffffffffffffffff6110e892511683612dbf565b5f806110c0565b6111119173ffffffffffffffffffffffffffffffffffffffff85511690612dbf565b5f806110b8565b876044918a7f54448808000000000000000000000000000000000000000000000000000000008352600452602452fd5b505086916110a4565b90506020813d60201161117b575b8161116c60209383611f9b565b81010312610c5d57515f61108f565b3d915061115f565b9098506020813d6020116111b0575b8161119f60209383611f9b565b81010312610c5d5751976020611049565b3d9150611192565b6111c3898092611f9b565b6111cd575f611003565b8780fd5b8a80fd5b6111ee915060a03d60a0116102495761023b8183611f9b565b5f610f1c565b610f0a91945061121460a09160203d60201161071a5761070b8183611f9b565b949150610e5d565b61123691975060203d60201161071a5761070b8183611f9b565b955f610e00565b6040513d88823e3d90fd5b5160038110156117ae57600214611261575b5050905080f35b51928351840193602081860312610ceb5760208101519067ffffffffffffffff82116106e8570193601f198582030161014060208201126106e857604051906112a982611f46565b6112b560208801612796565b82526112c360408801612796565b9260208301938452606088015191604084019283526080890151916060850192835260a08a01519973ffffffffffffffffffffffffffffffffffffffff8b168b03610ce757608086019a8b5260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6061133e60c08401612796565b9460a089019586520112610ce7576040519161135983611f63565b60e082015183526101008201516020840152610120820151604084015260c087019283526101408201519067ffffffffffffffff82116117aa576113a49260209182019201016127b7565b9960e086019a8b5273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169473ffffffffffffffffffffffffffffffffffffffff88511695604051967ff3d4510b0000000000000000000000000000000000000000000000000000000088526004880152602087602481845afa96871561179f578c9761177e575b5073ffffffffffffffffffffffffffffffffffffffff89511698604051997fa792e3a8000000000000000000000000000000000000000000000000000000008b5260048b015260208a602481855afa998a1561177357908c8e92839c611738575b509173ffffffffffffffffffffffffffffffffffffffff826114df61154497958f978f98610bf260a09a8780859b5116915116908b51913091612c37565b5116925190895193604051968795869485937f2b83cccd0000000000000000000000000000000000000000000000000000000085526004850160409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b03925af190811561172d578b9161170e575b5051815173ffffffffffffffffffffffffffffffffffffffff169061157b9187612dbf565b5173ffffffffffffffffffffffffffffffffffffffff16915173ffffffffffffffffffffffffffffffffffffffff1699519051996040519a8b9283927f60776d6e000000000000000000000000000000000000000000000000000000008452600484015260248301610120905261012483016115f691612156565b73ffffffffffffffffffffffffffffffffffffffff878116604485015289166064840152608483018b9052815160a4840152602082015160c484015260409091015160e48301523061010483015203818a5a94602095f1978815610d315787986116da575b50518088106116aa575095611679959681611681575b505050612ca5565b805f8061125a565b73ffffffffffffffffffffffffffffffffffffffff6116a293511690612dbf565b5f8080611671565b86604491897f54448808000000000000000000000000000000000000000000000000000000008352600452602452fd5b9097506020813d602011611706575b816116f660209383611f9b565b81010312610c5d5751965f61165b565b3d91506116e9565b611727915060a03d60a0116102495761023b8183611f9b565b5f611556565b6040513d8d823e3d90fd5b919b50939183918b9060203d811161176c575b6117558183611f9b565b8101611760916122a1565b9c9250949092946114a1565b503d61174b565b6040513d8f823e3d90fd5b61179891975060203d60201161071a5761070b8183611f9b565b955f611440565b6040513d8e823e3d90fd5b8b80fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b8680fd5b6004857f82b42900000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b50346100e05760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e0578061189e611e3c565b6118a6611e5f565b60a43567ffffffffffffffff811161075a5761193e926119236118d061194c933690600401611e82565b6118d8612974565b73ffffffffffffffffffffffffffffffffffffffff80604051956118fb87611ee1565b338752169687602087015260243560408701526064356060870152166080850152369161208c565b60a0820152604051938491602080840152604083019061217b565b03601f198101845283611f9b565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001691604051917fa792e3a8000000000000000000000000000000000000000000000000000000008352600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa918215611abd578492611a94575b50611a37611a299160405190611a1282611f2a565b8682526020820152604051928391602083016122cd565b03601f198101835282611f9b565b823b1561075a5761069e928492836040518096819582947fe0232b4200000000000000000000000000000000000000000000000000000000845273ffffffffffffffffffffffffffffffffffffffff604435911660048501612327565b611a29919250611ab5611a379160203d60201161071a5761070b8183611f9b565b9291506119fd565b6040513d86823e3d90fd5b50346100e05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e0576020611b0e611b05611e3c565b60243590612393565b604051908152f35b5034610c5d5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610c5d57611b4e611e3c565b60243560643573ffffffffffffffffffffffffffffffffffffffff8116809103610c5d5760843567ffffffffffffffff8111610c5d57611b92903690600401611e82565b9290611b9c612974565b6040517fcbe52ae300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018490527f00000000000000000000000000000000000000000000000000000000000000001695909160a0836044818a5afa908115611ded57611c95966020611c7a93611ca3965f91611e1d575b5001519673ffffffffffffffffffffffffffffffffffffffff60405195611c5687611ee1565b3387521696876020870152604086015260443560608601526080850152369161208c565b60a0820152604051948591602080840152604083019061217b565b03601f198101855284611f9b565b602073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016916024604051809781937fa792e3a800000000000000000000000000000000000000000000000000000000835260048301525afa928315611ded57610637945f94611df8575b50604051611d4b91611d3382611f2a565b600182526020820152604051958691602083016122cd565b803b15610c5d5773ffffffffffffffffffffffffffffffffffffffff5f8094611da4604051978896879586947fe0232b420000000000000000000000000000000000000000000000000000000086521660048501612327565b03925af18015611ded57611dda575b50807f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d80f35b611de691505f90611f9b565b5f5f611db3565b6040513d5f823e3d90fd5b611d4b919450611e169060203d60201161071a5761070b8183611f9b565b9390611d22565b611e36915060a03d60a0116102495761023b8183611f9b565b5f611c30565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203610c5d57565b6084359073ffffffffffffffffffffffffffffffffffffffff82168203610c5d57565b9181601f84011215610c5d5782359167ffffffffffffffff8311610c5d576020808501948460051b010111610c5d57565b9181601f84011215610c5d5782359167ffffffffffffffff8311610c5d5760208381860195010111610c5d57565b60c0810190811067ffffffffffffffff821117611efd57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff821117611efd57604052565b610100810190811067ffffffffffffffff821117611efd57604052565b6060810190811067ffffffffffffffff821117611efd57604052565b60a0810190811067ffffffffffffffff821117611efd57604052565b90601f601f19910116810190811067ffffffffffffffff821117611efd57604052565b908160a0910312610c5d57608060405191611fd883611f7f565b805183526020810151602084015260408101516040840152606081015160608401520151608082015290565b67ffffffffffffffff8111611efd5760051b60200190565b67ffffffffffffffff8111611efd57601f01601f191660200190565b9291926120448261201c565b916120526040519384611f9b565b829481845281830111610c5d578281602093845f960137010152565b9080601f83011215610c5d5781602061208993359101612038565b90565b9291909261209984612004565b936120a76040519586611f9b565b602085828152019060051b820191838311610c5d5780915b8383106120cd575050505050565b823567ffffffffffffffff8111610c5d578201606081870312610c5d57604051916120f783611f63565b813573ffffffffffffffffffffffffffffffffffffffff81168103610c5d5783526020820135602084015260408201359267ffffffffffffffff8411610c5d576121468860209586950161206e565b60408201528152019201916120bf565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b9060a060c082019273ffffffffffffffffffffffffffffffffffffffff815116835273ffffffffffffffffffffffffffffffffffffffff6020820151166020840152604081015160408401526060810151606084015273ffffffffffffffffffffffffffffffffffffffff608082015116608084015201519160c060a0830152825180915260e0820191602060e08360051b8301019401925f915b83831061222557505050505090565b9091929394602080612292837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff208660019603018752606060408b5173ffffffffffffffffffffffffffffffffffffffff815116845285810151868501520151918160408201520190612156565b97019301930191939290612216565b90816020910312610c5d575173ffffffffffffffffffffffffffffffffffffffff81168103610c5d5790565b6020815281519160038310156122fa57602060609161208994828501520151916040808201520190612156565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b612089939273ffffffffffffffffffffffffffffffffffffffff60609316825260208201528160408201520190612156565b9190820391821161236657565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169173ffffffffffffffffffffffffffffffffffffffff604051917f9571d2120000000000000000000000000000000000000000000000000000000083521690816004820152608081602481875afa8015611ded575f90612727575b60609150015190604051917fa341017c000000000000000000000000000000000000000000000000000000008352816004840152602083602481885afa928315611ded575f936126d6575b50604051927fdd1dc325000000000000000000000000000000000000000000000000000000008452602084600481895afa938415611ded575f9461268e575b5073ffffffffffffffffffffffffffffffffffffffff166040517f5c1548fb000000000000000000000000000000000000000000000000000000008152602081600481855afa908115611ded575f9161265c575b501590816125e1575b50156125a357506020906024604051809681937fcbf24d2500000000000000000000000000000000000000000000000000000000835260048301525afa928315611ded575f9361256d575b50916125676120899382612359565b916129e8565b92506020833d60201161259b575b8161258860209383611f9b565b81010312610c5d57915191612567612558565b3d915061257b565b919350507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81036125d45750905090565b6125676120899382612359565b60049150602090604051928380927f14a6bf0f0000000000000000000000000000000000000000000000000000000082525afa908115611ded575f9161262a575b50155f61250d565b90506020813d602011612654575b8161264560209383611f9b565b81010312610c5d57515f612622565b3d9150612638565b90506020813d602011612686575b8161267760209383611f9b565b81010312610c5d57515f612504565b3d915061266a565b9093506020813d6020116126ce575b816126aa60209383611f9b565b81010312610c5d57519273ffffffffffffffffffffffffffffffffffffffff6124b0565b3d915061269d565b9092506020813d60201161271f575b816126f260209383611f9b565b81010312610c5d575173ffffffffffffffffffffffffffffffffffffffff81168103610c5d57915f612471565b3d91506126e5565b506080813d60801161278e575b8161274160809383611f9b565b81010312610c5d576040516080810181811067ffffffffffffffff821117611efd576060928391604052805183526020810151602084015260408101516040840152015182820152612426565b3d9150612734565b519073ffffffffffffffffffffffffffffffffffffffff82168203610c5d57565b81601f82011215610c5d578051906127ce8261201c565b926127dc6040519485611f9b565b82845260208383010111610c5d57815f9260208093018386015e8301015290565b602081830312610c5d5780519067ffffffffffffffff8211610c5d57019060c082820312610c5d576040519161283283611ee1565b61283b81612796565b835261284960208201612796565b60208401526040810151604084015260608101516060840152608081015173ffffffffffffffffffffffffffffffffffffffff81168103610c5d57608084015260a08101519067ffffffffffffffff8211610c5d57019080601f83011215610c5d578151916128b783612004565b926128c56040519485611f9b565b80845260208085019160051b83010191838311610c5d5760208101915b8383106128f657505050505060a082015290565b825167ffffffffffffffff8111610c5d578201906060601f198388030112610c5d576040519061292582611f63565b61293160208401612796565b82526040830151602083015260608301519167ffffffffffffffff8311610c5d57612964886020809695819601016127b7565b60408201528152019201916128e2565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c6129c05760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b7f3ee5aeb5000000000000000000000000000000000000000000000000000000005f5260045ffd5b91906129f5828285612b7a565b928215612a0b5709151581018091116123665790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b805115612a455760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b805160011015612a455760400190565b604081016040825282518091526060820190602060608260051b8501019401915f905b828210612b0057505050506020818303910152602080835192838152019201905f5b818110612ad45750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101612ac7565b90919294602080612b6c837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08960019603018652606060408b5173ffffffffffffffffffffffffffffffffffffffff815116845285810151868501520151918160408201520190612156565b970192019201909291612aa5565b91818302917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81850993838086109503948086039514612c295784831115612c115790829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b82634e487b715f52156003026011186020526024601cfd5b505080925015612a0b570490565b90919273ffffffffffffffffffffffffffffffffffffffff612ca39481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252612c9e608483611f9b565b612e1e565b565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000602080830191825273ffffffffffffffffffffffffffffffffffffffff85166024840152604480840196909652948252929390925f90612d0a606486611f9b565b84519082855af15f513d82612d8d575b505015612d2657505050565b612c9e612ca39373ffffffffffffffffffffffffffffffffffffffff604051917f095ea7b30000000000000000000000000000000000000000000000000000000060208401521660248201525f604482015260448152612d87606482611f9b565b82612e1e565b909150612db7575073ffffffffffffffffffffffffffffffffffffffff81163b15155b5f80612d1a565b600114612db0565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff90921660248301526044820192909252612ca391612c9e826064810161193e565b905f602091828151910182855af115611ded575f513d612e9c575073ffffffffffffffffffffffffffffffffffffffff81163b155b612e5a5750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60011415612e5356fea2646970667358221220ca657b0e72498b657de8b4f1006ebe445e54045826e4512df7e90419aa40107964736f6c634300081e003300000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a8000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f5f3560e01c8063280c8d9714611b165780632b1ae79714611ac85780632ed76c921461186557806331f570721461075f578063458bd4ef146102ca57806381fd6c021461025b578063b8f82b26146100e35763d8fbc83314610072575f80fd5b346100e057807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e057602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb168152f35b80fd5b50346100e05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e0576101a660a0610120611e3c565b83608060405161012f81611f7f565b828152826020820152826040820152826060820152015261015260243582612393565b6040517fb8f82b2600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff9092166004830152602482015291829081906044820190565b038173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a8165afa9081156102505760a09291610223575b506080604051918051835260208101516020840152604081015160408401526060810151606084015201516080820152f35b6102439150823d8411610249575b61023b8183611f9b565b810190611fbe565b5f6101f1565b503d610231565b6040513d84823e3d90fd5b50346100e057807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e057602060405173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a8168152f35b50346100e0576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e05780610304611e3c565b602435906064359173ffffffffffffffffffffffffffffffffffffffff831680930361075a57610332611e5f565b9060607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c3601126106e8576101043567ffffffffffffffff81116107565761037e903690600401611eb3565b949091610389612974565b6040517fcbe52ae300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018390527f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a816969060a0816044818b5afa801561074b576020918a9161072c575b500151946040519361042185611f46565b338552602085019773ffffffffffffffffffffffffffffffffffffffff169586895260408601948552606086019060443582526080870192835260a087019373ffffffffffffffffffffffffffffffffffffffff1684526040519461048586611f63565b60a435865260c435602087015260e435604087015260c0880195865236906104ac92612038565b9460e08701958652604051998a9760208901602090525173ffffffffffffffffffffffffffffffffffffffff1660408901525173ffffffffffffffffffffffffffffffffffffffff1660608801525160808701525160a08601525173ffffffffffffffffffffffffffffffffffffffff1660c08501525173ffffffffffffffffffffffffffffffffffffffff1660e0840152516101008301610561916040809180518452602081015160208501520151910152565b5161016082016101409052610180820161057a91612156565b03601f198101845261058c9084611f9b565b7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb73ffffffffffffffffffffffffffffffffffffffff1690604051809581927fa792e3a800000000000000000000000000000000000000000000000000000000835260048301525a92602491602094fa928315610721576106379486946106ec575b506040516106459161061f82611f2a565b600282526020820152604051958691602083016122cd565b03601f198101865285611f9b565b803b156106e85773ffffffffffffffffffffffffffffffffffffffff85809461069e604051978896879586947fe0232b420000000000000000000000000000000000000000000000000000000086521660048501612327565b03925af18015610250576106d3575b507f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d80f35b816106dd91611f9b565b6100e057805f6106ad565b8480fd5b6106459194506107139060203d60201161071a575b61070b8183611f9b565b8101906122a1565b939061060e565b503d610701565b6040513d87823e3d90fd5b610745915060a03d60a0116102495761023b8183611f9b565b5f610410565b6040513d8b823e3d90fd5b8580fd5b505050fd5b50346100e05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e05760043560243567ffffffffffffffff8111611861576107b2903690600401611eb3565b919073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb1692833303611839578101906020818303126106e85780359067ffffffffffffffff821161075657016040818303126106e8576040519161082a83611f2a565b81356003811015611835578352602082013567ffffffffffffffff811161183557610855920161206e565b90602081019182528051600381101561180857610d3c57506108819051602080825183010191016127fd565b9173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a81692602081019373ffffffffffffffffffffffffffffffffffffffff85511691604051927ff3d4510b0000000000000000000000000000000000000000000000000000000084526004840152602083602481855afa928315610d31578793610d10575b5073ffffffffffffffffffffffffffffffffffffffff86511692604051937fa792e3a80000000000000000000000000000000000000000000000000000000085526004850152602084602481865afa938415610c69578894610cef575b506109a273ffffffffffffffffffffffffffffffffffffffff835116604084015190309084612c37565b60808201926109c98773ffffffffffffffffffffffffffffffffffffffff86511687612dbf565b604051906109d8606083611f9b565b600282526040366020840137896109ee83612a38565b9273ffffffffffffffffffffffffffffffffffffffff851680945273ffffffffffffffffffffffffffffffffffffffff610a2782612a72565b97818a1680995251169060a087015190823b15610ceb57610a7a928492836040518096819582947f29f77f1600000000000000000000000000000000000000000000000000000000845260048401612a82565b03925af1801561025057610cd2575b50506020602492604051938480927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa918215610cc7578a92610c93575b509081610ae38260a094610b6096612ca5565b73ffffffffffffffffffffffffffffffffffffffff8a5116918b606087015193604051968795869485937f0efe6a8b0000000000000000000000000000000000000000000000000000000085526004850160409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b03925af18015610c695760406020916024938b91610c74575b50015193604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115610c69578891610c19575b509073ffffffffffffffffffffffffffffffffffffffff80610bf79884610bf296958a10610bfa575b50511691511690612dbf565b612ca5565b80f35b610c1390610c0c8b8588511692612359565b9089612dbf565b5f610be6565b929190506020833d602011610c61575b81610c3660209383611f9b565b81010312610c5d57915190919073ffffffffffffffffffffffffffffffffffffffff610bbd565b5f80fd5b3d9150610c29565b6040513d8a823e3d90fd5b610c8d915060a03d60a0116102495761023b8183611f9b565b5f610b79565b91506020823d602011610cbf575b81610cae60209383611f9b565b81010312610c5d5790519080610ad0565b3d9150610ca1565b6040513d8c823e3d90fd5b81610cdc91611f9b565b610ce757895f610a89565b8980fd5b8380fd5b610d0991945060203d60201161071a5761070b8183611f9b565b925f610978565b610d2a91935060203d60201161071a5761070b8183611f9b565b915f61091b565b6040513d89823e3d90fd5b809493945160038110156117db576001036112485750610d669051602080825183010191016127fd565b9273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a81693602081019073ffffffffffffffffffffffffffffffffffffffff82511695604051967ff3d4510b0000000000000000000000000000000000000000000000000000000088526004880152602087602481845afa96871561123d57869761121c575b5073ffffffffffffffffffffffffffffffffffffffff83511692604051937fa792e3a80000000000000000000000000000000000000000000000000000000085526004850152602084602481855afa938415610d315787946111f4575b5060a08173ffffffffffffffffffffffffffffffffffffffff80610f0a94511691610e948288511660408901948551913091612c37565b610e9f8a878a612ca5565b511690519360608601948a865193604051968795869485937f2b83cccd0000000000000000000000000000000000000000000000000000000085526004850160409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b03925af1908115610d315787916111d5575b505196610f45608084019873ffffffffffffffffffffffffffffffffffffffff8a511683612dbf565b60405197610f5460608a611f9b565b6002895260403660208b0137610f6989612a38565b9873ffffffffffffffffffffffffffffffffffffffff8316809a5273ffffffffffffffffffffffffffffffffffffffff610fa282612a72565b92818816809452511660a0860151813b156111d157918a91610ff493836040518096819582947f29f77f1600000000000000000000000000000000000000000000000000000000845260048401612a82565b03925af1801561074b576111b8575b5060206024996040519a8b80927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa988915610c69578899611183575b506020602491604051928380927f70a082310000000000000000000000000000000000000000000000000000000082523060048301525afa908115610c69578891611151575b508680821115611148576110a291612359565b915b51808910611118575087610bf797986110ef575b5050806110c7575b5050612ca5565b73ffffffffffffffffffffffffffffffffffffffff6110e892511683612dbf565b5f806110c0565b6111119173ffffffffffffffffffffffffffffffffffffffff85511690612dbf565b5f806110b8565b876044918a7f54448808000000000000000000000000000000000000000000000000000000008352600452602452fd5b505086916110a4565b90506020813d60201161117b575b8161116c60209383611f9b565b81010312610c5d57515f61108f565b3d915061115f565b9098506020813d6020116111b0575b8161119f60209383611f9b565b81010312610c5d5751976020611049565b3d9150611192565b6111c3898092611f9b565b6111cd575f611003565b8780fd5b8a80fd5b6111ee915060a03d60a0116102495761023b8183611f9b565b5f610f1c565b610f0a91945061121460a09160203d60201161071a5761070b8183611f9b565b949150610e5d565b61123691975060203d60201161071a5761070b8183611f9b565b955f610e00565b6040513d88823e3d90fd5b5160038110156117ae57600214611261575b5050905080f35b51928351840193602081860312610ceb5760208101519067ffffffffffffffff82116106e8570193601f198582030161014060208201126106e857604051906112a982611f46565b6112b560208801612796565b82526112c360408801612796565b9260208301938452606088015191604084019283526080890151916060850192835260a08a01519973ffffffffffffffffffffffffffffffffffffffff8b168b03610ce757608086019a8b5260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6061133e60c08401612796565b9460a089019586520112610ce7576040519161135983611f63565b60e082015183526101008201516020840152610120820151604084015260c087019283526101408201519067ffffffffffffffff82116117aa576113a49260209182019201016127b7565b9960e086019a8b5273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a8169473ffffffffffffffffffffffffffffffffffffffff88511695604051967ff3d4510b0000000000000000000000000000000000000000000000000000000088526004880152602087602481845afa96871561179f578c9761177e575b5073ffffffffffffffffffffffffffffffffffffffff89511698604051997fa792e3a8000000000000000000000000000000000000000000000000000000008b5260048b015260208a602481855afa998a1561177357908c8e92839c611738575b509173ffffffffffffffffffffffffffffffffffffffff826114df61154497958f978f98610bf260a09a8780859b5116915116908b51913091612c37565b5116925190895193604051968795869485937f2b83cccd0000000000000000000000000000000000000000000000000000000085526004850160409194939273ffffffffffffffffffffffffffffffffffffffff606083019616825260208201520152565b03925af190811561172d578b9161170e575b5051815173ffffffffffffffffffffffffffffffffffffffff169061157b9187612dbf565b5173ffffffffffffffffffffffffffffffffffffffff16915173ffffffffffffffffffffffffffffffffffffffff1699519051996040519a8b9283927f60776d6e000000000000000000000000000000000000000000000000000000008452600484015260248301610120905261012483016115f691612156565b73ffffffffffffffffffffffffffffffffffffffff878116604485015289166064840152608483018b9052815160a4840152602082015160c484015260409091015160e48301523061010483015203818a5a94602095f1978815610d315787986116da575b50518088106116aa575095611679959681611681575b505050612ca5565b805f8061125a565b73ffffffffffffffffffffffffffffffffffffffff6116a293511690612dbf565b5f8080611671565b86604491897f54448808000000000000000000000000000000000000000000000000000000008352600452602452fd5b9097506020813d602011611706575b816116f660209383611f9b565b81010312610c5d5751965f61165b565b3d91506116e9565b611727915060a03d60a0116102495761023b8183611f9b565b5f611556565b6040513d8d823e3d90fd5b919b50939183918b9060203d811161176c575b6117558183611f9b565b8101611760916122a1565b9c9250949092946114a1565b503d61174b565b6040513d8f823e3d90fd5b61179891975060203d60201161071a5761070b8183611f9b565b955f611440565b6040513d8e823e3d90fd5b8b80fd5b6024847f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b6024867f4e487b710000000000000000000000000000000000000000000000000000000081526021600452fd5b8680fd5b6004857f82b42900000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b50346100e05760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e0578061189e611e3c565b6118a6611e5f565b60a43567ffffffffffffffff811161075a5761193e926119236118d061194c933690600401611e82565b6118d8612974565b73ffffffffffffffffffffffffffffffffffffffff80604051956118fb87611ee1565b338752169687602087015260243560408701526064356060870152166080850152369161208c565b60a0820152604051938491602080840152604083019061217b565b03601f198101845283611f9b565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb1691604051917fa792e3a8000000000000000000000000000000000000000000000000000000008352600483015260208260248173ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a8165afa918215611abd578492611a94575b50611a37611a299160405190611a1282611f2a565b8682526020820152604051928391602083016122cd565b03601f198101835282611f9b565b823b1561075a5761069e928492836040518096819582947fe0232b4200000000000000000000000000000000000000000000000000000000845273ffffffffffffffffffffffffffffffffffffffff604435911660048501612327565b611a29919250611ab5611a379160203d60201161071a5761070b8183611f9b565b9291506119fd565b6040513d86823e3d90fd5b50346100e05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100e0576020611b0e611b05611e3c565b60243590612393565b604051908152f35b5034610c5d5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610c5d57611b4e611e3c565b60243560643573ffffffffffffffffffffffffffffffffffffffff8116809103610c5d5760843567ffffffffffffffff8111610c5d57611b92903690600401611e82565b9290611b9c612974565b6040517fcbe52ae300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8681166004830152602482018490527f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a81695909160a0836044818a5afa908115611ded57611c95966020611c7a93611ca3965f91611e1d575b5001519673ffffffffffffffffffffffffffffffffffffffff60405195611c5687611ee1565b3387521696876020870152604086015260443560608601526080850152369161208c565b60a0820152604051948591602080840152604083019061217b565b03601f198101855284611f9b565b602073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb16916024604051809781937fa792e3a800000000000000000000000000000000000000000000000000000000835260048301525afa928315611ded57610637945f94611df8575b50604051611d4b91611d3382611f2a565b600182526020820152604051958691602083016122cd565b803b15610c5d5773ffffffffffffffffffffffffffffffffffffffff5f8094611da4604051978896879586947fe0232b420000000000000000000000000000000000000000000000000000000086521660048501612327565b03925af18015611ded57611dda575b50807f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d80f35b611de691505f90611f9b565b5f5f611db3565b6040513d5f823e3d90fd5b611d4b919450611e169060203d60201161071a5761070b8183611f9b565b9390611d22565b611e36915060a03d60a0116102495761023b8183611f9b565b5f611c30565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203610c5d57565b6084359073ffffffffffffffffffffffffffffffffffffffff82168203610c5d57565b9181601f84011215610c5d5782359167ffffffffffffffff8311610c5d576020808501948460051b010111610c5d57565b9181601f84011215610c5d5782359167ffffffffffffffff8311610c5d5760208381860195010111610c5d57565b60c0810190811067ffffffffffffffff821117611efd57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff821117611efd57604052565b610100810190811067ffffffffffffffff821117611efd57604052565b6060810190811067ffffffffffffffff821117611efd57604052565b60a0810190811067ffffffffffffffff821117611efd57604052565b90601f601f19910116810190811067ffffffffffffffff821117611efd57604052565b908160a0910312610c5d57608060405191611fd883611f7f565b805183526020810151602084015260408101516040840152606081015160608401520151608082015290565b67ffffffffffffffff8111611efd5760051b60200190565b67ffffffffffffffff8111611efd57601f01601f191660200190565b9291926120448261201c565b916120526040519384611f9b565b829481845281830111610c5d578281602093845f960137010152565b9080601f83011215610c5d5781602061208993359101612038565b90565b9291909261209984612004565b936120a76040519586611f9b565b602085828152019060051b820191838311610c5d5780915b8383106120cd575050505050565b823567ffffffffffffffff8111610c5d578201606081870312610c5d57604051916120f783611f63565b813573ffffffffffffffffffffffffffffffffffffffff81168103610c5d5783526020820135602084015260408201359267ffffffffffffffff8411610c5d576121468860209586950161206e565b60408201528152019201916120bf565b90601f19601f602080948051918291828752018686015e5f8582860101520116010190565b9060a060c082019273ffffffffffffffffffffffffffffffffffffffff815116835273ffffffffffffffffffffffffffffffffffffffff6020820151166020840152604081015160408401526060810151606084015273ffffffffffffffffffffffffffffffffffffffff608082015116608084015201519160c060a0830152825180915260e0820191602060e08360051b8301019401925f915b83831061222557505050505090565b9091929394602080612292837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff208660019603018752606060408b5173ffffffffffffffffffffffffffffffffffffffff815116845285810151868501520151918160408201520190612156565b97019301930191939290612216565b90816020910312610c5d575173ffffffffffffffffffffffffffffffffffffffff81168103610c5d5790565b6020815281519160038310156122fa57602060609161208994828501520151916040808201520190612156565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b612089939273ffffffffffffffffffffffffffffffffffffffff60609316825260208201528160408201520190612156565b9190820391821161236657565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a8169173ffffffffffffffffffffffffffffffffffffffff604051917f9571d2120000000000000000000000000000000000000000000000000000000083521690816004820152608081602481875afa8015611ded575f90612727575b60609150015190604051917fa341017c000000000000000000000000000000000000000000000000000000008352816004840152602083602481885afa928315611ded575f936126d6575b50604051927fdd1dc325000000000000000000000000000000000000000000000000000000008452602084600481895afa938415611ded575f9461268e575b5073ffffffffffffffffffffffffffffffffffffffff166040517f5c1548fb000000000000000000000000000000000000000000000000000000008152602081600481855afa908115611ded575f9161265c575b501590816125e1575b50156125a357506020906024604051809681937fcbf24d2500000000000000000000000000000000000000000000000000000000835260048301525afa928315611ded575f9361256d575b50916125676120899382612359565b916129e8565b92506020833d60201161259b575b8161258860209383611f9b565b81010312610c5d57915191612567612558565b3d915061257b565b919350507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81036125d45750905090565b6125676120899382612359565b60049150602090604051928380927f14a6bf0f0000000000000000000000000000000000000000000000000000000082525afa908115611ded575f9161262a575b50155f61250d565b90506020813d602011612654575b8161264560209383611f9b565b81010312610c5d57515f612622565b3d9150612638565b90506020813d602011612686575b8161267760209383611f9b565b81010312610c5d57515f612504565b3d915061266a565b9093506020813d6020116126ce575b816126aa60209383611f9b565b81010312610c5d57519273ffffffffffffffffffffffffffffffffffffffff6124b0565b3d915061269d565b9092506020813d60201161271f575b816126f260209383611f9b565b81010312610c5d575173ffffffffffffffffffffffffffffffffffffffff81168103610c5d57915f612471565b3d91506126e5565b506080813d60801161278e575b8161274160809383611f9b565b81010312610c5d576040516080810181811067ffffffffffffffff821117611efd576060928391604052805183526020810151602084015260408101516040840152015182820152612426565b3d9150612734565b519073ffffffffffffffffffffffffffffffffffffffff82168203610c5d57565b81601f82011215610c5d578051906127ce8261201c565b926127dc6040519485611f9b565b82845260208383010111610c5d57815f9260208093018386015e8301015290565b602081830312610c5d5780519067ffffffffffffffff8211610c5d57019060c082820312610c5d576040519161283283611ee1565b61283b81612796565b835261284960208201612796565b60208401526040810151604084015260608101516060840152608081015173ffffffffffffffffffffffffffffffffffffffff81168103610c5d57608084015260a08101519067ffffffffffffffff8211610c5d57019080601f83011215610c5d578151916128b783612004565b926128c56040519485611f9b565b80845260208085019160051b83010191838311610c5d5760208101915b8383106128f657505050505060a082015290565b825167ffffffffffffffff8111610c5d578201906060601f198388030112610c5d576040519061292582611f63565b61293160208401612796565b82526040830151602083015260608301519167ffffffffffffffff8311610c5d57612964886020809695819601016127b7565b60408201528152019201916128e2565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c6129c05760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d565b7f3ee5aeb5000000000000000000000000000000000000000000000000000000005f5260045ffd5b91906129f5828285612b7a565b928215612a0b5709151581018091116123665790565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b805115612a455760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b805160011015612a455760400190565b604081016040825282518091526060820190602060608260051b8501019401915f905b828210612b0057505050506020818303910152602080835192838152019201905f5b818110612ad45750505090565b825173ffffffffffffffffffffffffffffffffffffffff16845260209384019390920191600101612ac7565b90919294602080612b6c837fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa08960019603018652606060408b5173ffffffffffffffffffffffffffffffffffffffff815116845285810151868501520151918160408201520190612156565b970192019201909291612aa5565b91818302917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81850993838086109503948086039514612c295784831115612c115790829109815f0382168092046002816003021880820260020302808202600203028082026002030280820260020302808202600203028091026002030293600183805f03040190848311900302920304170290565b82634e487b715f52156003026011186020526024601cfd5b505080925015612a0b570490565b90919273ffffffffffffffffffffffffffffffffffffffff612ca39481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252612c9e608483611f9b565b612e1e565b565b6040517f095ea7b300000000000000000000000000000000000000000000000000000000602080830191825273ffffffffffffffffffffffffffffffffffffffff85166024840152604480840196909652948252929390925f90612d0a606486611f9b565b84519082855af15f513d82612d8d575b505015612d2657505050565b612c9e612ca39373ffffffffffffffffffffffffffffffffffffffff604051917f095ea7b30000000000000000000000000000000000000000000000000000000060208401521660248201525f604482015260448152612d87606482611f9b565b82612e1e565b909150612db7575073ffffffffffffffffffffffffffffffffffffffff81163b15155b5f80612d1a565b600114612db0565b6040517fa9059cbb00000000000000000000000000000000000000000000000000000000602082015273ffffffffffffffffffffffffffffffffffffffff90921660248301526044820192909252612ca391612c9e826064810161193e565b905f602091828151910182855af115611ded575f513d612e9c575073ffffffffffffffffffffffffffffffffffffffff81163b155b612e5a5750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b60011415612e5356fea2646970667358221220ca657b0e72498b657de8b4f1006ebe445e54045826e4512df7e90419aa40107964736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a8000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
-----Decoded View---------------
Arg [0] : _leverageManager (address): 0x38Ba21C6Bf31dF1b1798FCEd07B4e9b07C5ec3a8
Arg [1] : _morpho (address): 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000038ba21c6bf31df1b1798fced07b4e9b07c5ec3a8
Arg [1] : 000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.