Overview
ETH Balance
ETH Value
$0.00Latest 7 from a total of 7 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Create Pre Liqui... | 9026899 | 141 days ago | IN | 0 ETH | 0.00000135 | ||||
| Create Pre Liqui... | 9026899 | 141 days ago | IN | 0 ETH | 0.00000135 | ||||
| Create Pre Liqui... | 9026899 | 141 days ago | IN | 0 ETH | 0.00000135 | ||||
| Create Pre Liqui... | 9026899 | 141 days ago | IN | 0 ETH | 0.00000135 | ||||
| Create Pre Liqui... | 9026899 | 141 days ago | IN | 0 ETH | 0.00000135 | ||||
| Create Pre Liqui... | 9026899 | 141 days ago | IN | 0 ETH | 0.00000135 | ||||
| Create Pre Liqui... | 9026899 | 141 days ago | IN | 0 ETH | 0.00000135 |
Latest 9 internal transactions
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 9026899 | 141 days ago | Contract Creation | 0 ETH | |||
| 9026899 | 141 days ago | Contract Creation | 0 ETH | |||
| 9026899 | 141 days ago | Contract Creation | 0 ETH | |||
| 9026899 | 141 days ago | Contract Creation | 0 ETH | |||
| 9026899 | 141 days ago | Contract Creation | 0 ETH | |||
| 9026899 | 141 days ago | Contract Creation | 0 ETH | |||
| 9026899 | 141 days ago | Contract Creation | 0 ETH | |||
| 2741993 | 213 days ago | Contract Creation | 0 ETH | |||
| 2741993 | 213 days ago | Contract Creation | 0 ETH |
Cross-Chain Transactions
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.27;
import {IPreLiquidation, PreLiquidationParams} from "./interfaces/IPreLiquidation.sol";
import {IPreLiquidationFactory} from "./interfaces/IPreLiquidationFactory.sol";
import {IMorpho, Id} from "../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {EventsLib} from "./libraries/EventsLib.sol";
import {PreLiquidation} from "./PreLiquidation.sol";
/// @title PreLiquidationFactory
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice A linear LIF and linear LCF pre-liquidation factory contract for Morpho.
contract PreLiquidationFactory is IPreLiquidationFactory {
/* IMMUTABLE */
/// @notice The address of the Morpho contract.
IMorpho public immutable MORPHO;
/* STORAGE */
/// @notice Mapping which returns true if the address is a PreLiquidation contract created by this factory.
mapping(address => bool) public isPreLiquidation;
/* CONSTRUCTOR */
/// @param morpho The address of the Morpho contract.
constructor(address morpho) {
require(morpho != address(0), ErrorsLib.ZeroAddress());
MORPHO = IMorpho(morpho);
}
/* EXTERNAL */
/// @notice Creates a PreLiquidation contract.
/// @param id The Morpho market for PreLiquidations.
/// @param preLiquidationParams The PreLiquidation params for the PreLiquidation contract.
/// @dev Warning: This function will revert without data if the pre-liquidation already exists.
function createPreLiquidation(Id id, PreLiquidationParams calldata preLiquidationParams)
external
returns (IPreLiquidation)
{
IPreLiquidation preLiquidation =
IPreLiquidation(address(new PreLiquidation{salt: 0}(address(MORPHO), id, preLiquidationParams)));
emit EventsLib.CreatePreLiquidation(address(preLiquidation), id, preLiquidationParams);
isPreLiquidation[address(preLiquidation)] = true;
return preLiquidation;
}
}// 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
/// `onMorphoRepay` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
/// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
/// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
/// roundings between shares and assets.
/// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
/// @param marketParams The market to repay assets to.
/// @param assets The amount of assets to repay.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the debt position.
/// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
/// @return assetsRepaid The amount of assets repaid.
/// @return sharesRepaid The amount of shares burned.
function repay(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
/// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupplyCollateral` function with the given `data`.
/// @dev Interest are not accrued since it's not required and it saves gas.
/// @dev Supplying a large amount can revert for overflow.
/// @param marketParams The market to supply collateral to.
/// @param assets The amount of collateral to supply.
/// @param onBehalf The address that will own the increased collateral position.
/// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
external;
/// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
/// @param marketParams The market to withdraw collateral from.
/// @param assets The amount of collateral to withdraw.
/// @param onBehalf The address of the owner of the collateral position.
/// @param receiver The address that will receive the collateral assets.
function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
external;
/// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
/// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
/// `onMorphoLiquidate` function with the given `data`.
/// @dev Either `seizedAssets` or `repaidShares` should be zero.
/// @dev Seizing more than the collateral balance will underflow and revert without any error message.
/// @dev Repaying more than the borrow balance will underflow and revert without any error message.
/// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
/// @param marketParams The market of the position.
/// @param borrower The owner of the position.
/// @param seizedAssets The amount of collateral to seize.
/// @param repaidShares The amount of shares to repay.
/// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
/// @return The amount of assets seized.
/// @return The amount of assets repaid.
function liquidate(
MarketParams memory marketParams,
address borrower,
uint256 seizedAssets,
uint256 repaidShares,
bytes memory data
) external returns (uint256, uint256);
/// @notice Executes a flash loan.
/// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
/// markets combined, plus donations).
/// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
/// - `flashFee` is zero.
/// - `maxFlashLoan` is the token's balance of this contract.
/// - The receiver of `assets` is the caller.
/// @param token The token to flash loan.
/// @param assets The amount of assets to flash loan.
/// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
function flashLoan(address token, uint256 assets, bytes calldata data) external;
/// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
/// @param authorized The authorized address.
/// @param newIsAuthorized The new authorization status.
function setAuthorization(address authorized, bool newIsAuthorized) external;
/// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
/// @dev Warning: Reverts if the signature has already been submitted.
/// @dev The signature is malleable, but it has no impact on the security here.
/// @dev The nonce is passed as argument to be able to revert with a different error message.
/// @param authorization The `Authorization` struct.
/// @param signature The signature.
function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;
/// @notice Accrues interest for the given market `marketParams`.
function accrueInterest(MarketParams memory marketParams) external;
/// @notice Returns the data stored on the different `slots`.
function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}
/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user)
external
view
returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
/// accrual.
function market(Id id)
external
view
returns (
uint128 totalSupplyAssets,
uint128 totalSupplyShares,
uint128 totalBorrowAssets,
uint128 totalBorrowShares,
uint128 lastUpdate,
uint128 fee
);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id)
external
view
returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
}
/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user) external view returns (Position memory p);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
/// interest accrual.
function market(Id id) external view returns (Market memory m);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id) external view returns (MarketParams memory);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title IMorphoLiquidateCallback
/// @notice Interface that liquidators willing to use `liquidate`'s callback must implement.
interface IMorphoLiquidateCallback {
/// @notice Callback called when a liquidation occurs.
/// @dev The callback is called only if data is not empty.
/// @param repaidAssets The amount of repaid assets.
/// @param data Arbitrary data passed to the `liquidate` function.
function onMorphoLiquidate(uint256 repaidAssets, bytes calldata data) external;
}
/// @title IMorphoRepayCallback
/// @notice Interface that users willing to use `repay`'s callback must implement.
interface IMorphoRepayCallback {
/// @notice Callback called when a repayment occurs.
/// @dev The callback is called only if data is not empty.
/// @param assets The amount of repaid assets.
/// @param data Arbitrary data passed to the `repay` function.
function onMorphoRepay(uint256 assets, bytes calldata data) external;
}
/// @title IMorphoSupplyCallback
/// @notice Interface that users willing to use `supply`'s callback must implement.
interface IMorphoSupplyCallback {
/// @notice Callback called when a supply occurs.
/// @dev The callback is called only if data is not empty.
/// @param assets The amount of supplied assets.
/// @param data Arbitrary data passed to the `supply` function.
function onMorphoSupply(uint256 assets, bytes calldata data) external;
}
/// @title IMorphoSupplyCollateralCallback
/// @notice Interface that users willing to use `supplyCollateral`'s callback must implement.
interface IMorphoSupplyCollateralCallback {
/// @notice Callback called when a supply of collateral occurs.
/// @dev The callback is called only if data is not empty.
/// @param assets The amount of supplied collateral.
/// @param data Arbitrary data passed to the `supplyCollateral` function.
function onMorphoSupplyCollateral(uint256 assets, bytes calldata data) external;
}
/// @title IMorphoFlashLoanCallback
/// @notice Interface that users willing to use `flashLoan`'s callback must implement.
interface IMorphoFlashLoanCallback {
/// @notice Callback called when a flash loan occurs.
/// @dev The callback is called only if data is not empty.
/// @param assets The amount of assets that was flash loaned.
/// @param data Arbitrary data passed to the `flashLoan` function.
function onMorphoFlashLoan(uint256 assets, bytes calldata data) external;
}// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title IOracle /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Interface that oracles used by Morpho must implement. /// @dev It is the user's responsibility to select markets with safe oracles. interface IOracle { /// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36. /// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in /// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals` /// decimals of precision. function price() external view returns (uint256); }
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @dev The maximum fee a market can have (25%).
uint256 constant MAX_FEE = 0.25e18;
/// @dev Oracle price scale.
uint256 constant ORACLE_PRICE_SCALE = 1e36;
/// @dev Liquidation cursor.
uint256 constant LIQUIDATION_CURSOR = 0.3e18;
/// @dev Max liquidation incentive factor.
uint256 constant MAX_LIQUIDATION_INCENTIVE_FACTOR = 1.15e18;
/// @dev The EIP-712 typeHash for EIP712Domain.
bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
/// @dev The EIP-712 typeHash for Authorization.
bytes32 constant AUTHORIZATION_TYPEHASH =
keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)");// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @title ErrorsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library exposing error messages. library ErrorsLib { /// @notice Thrown when the caller is not the owner. string internal constant NOT_OWNER = "not owner"; /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV. string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded"; /// @notice Thrown when the fee to set exceeds the maximum fee. string internal constant MAX_FEE_EXCEEDED = "max fee exceeded"; /// @notice Thrown when the value is already set. string internal constant ALREADY_SET = "already set"; /// @notice Thrown when the IRM is not enabled at market creation. string internal constant IRM_NOT_ENABLED = "IRM not enabled"; /// @notice Thrown when the LLTV is not enabled at market creation. string internal constant LLTV_NOT_ENABLED = "LLTV not enabled"; /// @notice Thrown when the market is already created. string internal constant MARKET_ALREADY_CREATED = "market already created"; /// @notice Thrown when a token to transfer doesn't have code. string internal constant NO_CODE = "no code"; /// @notice Thrown when the market is not created. string internal constant MARKET_NOT_CREATED = "market not created"; /// @notice Thrown when not exactly one of the input amount is zero. string internal constant INCONSISTENT_INPUT = "inconsistent input"; /// @notice Thrown when zero assets is passed as input. string internal constant ZERO_ASSETS = "zero assets"; /// @notice Thrown when a zero address is passed as input. string internal constant ZERO_ADDRESS = "zero address"; /// @notice Thrown when the caller is not authorized to conduct an action. string internal constant UNAUTHORIZED = "unauthorized"; /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`. string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral"; /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`. string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity"; /// @notice Thrown when the position to liquidate is healthy. string internal constant HEALTHY_POSITION = "position is healthy"; /// @notice Thrown when the authorization signature is invalid. string internal constant INVALID_SIGNATURE = "invalid signature"; /// @notice Thrown when the authorization signature is expired. string internal constant SIGNATURE_EXPIRED = "signature expired"; /// @notice Thrown when the nonce is invalid. string internal constant INVALID_NONCE = "invalid nonce"; /// @notice Thrown when a token transfer reverted. string internal constant TRANSFER_REVERTED = "transfer reverted"; /// @notice Thrown when a token transfer returned false. string internal constant TRANSFER_RETURNED_FALSE = "transfer returned false"; /// @notice Thrown when a token transferFrom reverted. string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted"; /// @notice Thrown when a token transferFrom returned false string internal constant TRANSFER_FROM_RETURNED_FALSE = "transferFrom returned false"; /// @notice Thrown when the maximum uint128 is exceeded. string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded"; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; uint256 constant WAD = 1e18; /// @title MathLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library to manage fixed-point arithmetic. library MathLib { /// @dev Returns (`x` * `y`) / `WAD` rounded down. function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); } /// @dev Returns (`x` * `WAD`) / `y` rounded down. function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); } /// @dev Returns (`x` * `WAD`) / `y` rounded up. function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); } /// @dev Returns (`x` * `y`) / `d` rounded down. function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) { return (x * y) / d; } /// @dev Returns (`x` * `y`) / `d` rounded up. function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) { return (x * y + (d - 1)) / d; } /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a /// continuous compound interest rate. function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) { uint256 firstTerm = x * n; uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD); uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD); return firstTerm + secondTerm + thirdTerm; } }
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {MathLib} from "./MathLib.sol";
/// @title SharesMathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Shares management library.
/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
library SharesMathLib {
using MathLib for uint256;
/// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
/// high precision computations.
/// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
/// stays low enough not to inflate these assets to a significant value.
/// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
uint256 internal constant VIRTUAL_SHARES = 1e6;
/// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
/// empty.
uint256 internal constant VIRTUAL_ASSETS = 1;
/// @dev Calculates the value of `assets` quoted in shares, rounding down.
function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding down.
function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}
/// @dev Calculates the value of `assets` quoted in shares, rounding up.
function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding up.
function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {ErrorsLib} from "../libraries/ErrorsLib.sol";
/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing helpers.
/// @dev Inspired by https://github.com/morpho-org/morpho-utils.
library UtilsLib {
/// @dev Returns true if there is exactly one zero among `x` and `y`.
function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
assembly {
z := xor(iszero(x), iszero(y))
}
}
/// @dev Returns the min of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns `x` safely cast to uint128.
function toUint128(uint256 x) internal pure returns (uint128) {
require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED);
return uint128(x);
}
/// @dev Returns max(0, x - y).
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.27;
import {Id, MarketParams, IMorpho, Position, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {IMorphoRepayCallback} from "../lib/morpho-blue/src/interfaces/IMorphoCallbacks.sol";
import {IPreLiquidation, PreLiquidationParams} from "./interfaces/IPreLiquidation.sol";
import {IPreLiquidationCallback} from "./interfaces/IPreLiquidationCallback.sol";
import {IOracle} from "../lib/morpho-blue/src/interfaces/IOracle.sol";
import {ORACLE_PRICE_SCALE} from "../lib/morpho-blue/src/libraries/ConstantsLib.sol";
import {SharesMathLib} from "../lib/morpho-blue/src/libraries/SharesMathLib.sol";
import {SafeTransferLib} from "../lib/solmate/src/utils/SafeTransferLib.sol";
import {WAD, MathLib} from "../lib/morpho-blue/src/libraries/MathLib.sol";
import {UtilsLib} from "../lib/morpho-blue/src/libraries/UtilsLib.sol";
import {ERC20} from "../lib/solmate/src/tokens/ERC20.sol";
import {EventsLib} from "./libraries/EventsLib.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
/// @title PreLiquidation
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice A linear LIF and linear LCF pre-liquidation contract for Morpho.
contract PreLiquidation is IPreLiquidation, IMorphoRepayCallback {
using SharesMathLib for uint256;
using MathLib for uint256;
using SafeTransferLib for ERC20;
/* IMMUTABLE */
/// @notice The address of the Morpho contract.
IMorpho public immutable MORPHO;
/// @notice The id of the Morpho Market specific to the PreLiquidation contract.
Id public immutable ID;
// Market parameters
address internal immutable LOAN_TOKEN;
address internal immutable COLLATERAL_TOKEN;
address internal immutable ORACLE;
address internal immutable IRM;
uint256 internal immutable LLTV;
// Pre-liquidation parameters
uint256 internal immutable PRE_LLTV;
uint256 internal immutable PRE_LCF_1;
uint256 internal immutable PRE_LCF_2;
uint256 internal immutable PRE_LIF_1;
uint256 internal immutable PRE_LIF_2;
address internal immutable PRE_LIQUIDATION_ORACLE;
/// @notice The Morpho market parameters specific to the PreLiquidation contract.
function marketParams() public view returns (MarketParams memory) {
return MarketParams({
loanToken: LOAN_TOKEN,
collateralToken: COLLATERAL_TOKEN,
oracle: ORACLE,
irm: IRM,
lltv: LLTV
});
}
/// @notice The pre-liquidation parameters specific to the PreLiquidation contract.
function preLiquidationParams() external view returns (PreLiquidationParams memory) {
return PreLiquidationParams({
preLltv: PRE_LLTV,
preLCF1: PRE_LCF_1,
preLCF2: PRE_LCF_2,
preLIF1: PRE_LIF_1,
preLIF2: PRE_LIF_2,
preLiquidationOracle: PRE_LIQUIDATION_ORACLE
});
}
/* CONSTRUCTOR */
/// @dev Initializes the PreLiquidation contract.
/// @param morpho The address of the Morpho contract.
/// @param id The id of the Morpho market on which pre-liquidations will occur.
/// @param _preLiquidationParams The pre-liquidation parameters.
/// @dev The following requirements should be met:
/// - preLltv < LLTV;
/// - preLCF1 <= preLCF2;
/// - preLCF1 <= 1;
/// - 1 <= preLIF1 <= preLIF2 <= 1 / LLTV.
constructor(address morpho, Id id, PreLiquidationParams memory _preLiquidationParams) {
require(IMorpho(morpho).market(id).lastUpdate != 0, ErrorsLib.NonexistentMarket());
MarketParams memory _marketParams = IMorpho(morpho).idToMarketParams(id);
require(_preLiquidationParams.preLltv < _marketParams.lltv, ErrorsLib.PreLltvTooHigh());
require(_preLiquidationParams.preLCF1 <= _preLiquidationParams.preLCF2, ErrorsLib.PreLCFDecreasing());
require(_preLiquidationParams.preLCF1 <= WAD, ErrorsLib.PreLCFTooHigh());
require(WAD <= _preLiquidationParams.preLIF1, ErrorsLib.PreLIFTooLow());
require(_preLiquidationParams.preLIF1 <= _preLiquidationParams.preLIF2, ErrorsLib.PreLIFDecreasing());
require(_preLiquidationParams.preLIF2 <= WAD.wDivDown(_marketParams.lltv), ErrorsLib.PreLIFTooHigh());
MORPHO = IMorpho(morpho);
ID = id;
LOAN_TOKEN = _marketParams.loanToken;
COLLATERAL_TOKEN = _marketParams.collateralToken;
ORACLE = _marketParams.oracle;
IRM = _marketParams.irm;
LLTV = _marketParams.lltv;
PRE_LLTV = _preLiquidationParams.preLltv;
PRE_LCF_1 = _preLiquidationParams.preLCF1;
PRE_LCF_2 = _preLiquidationParams.preLCF2;
PRE_LIF_1 = _preLiquidationParams.preLIF1;
PRE_LIF_2 = _preLiquidationParams.preLIF2;
PRE_LIQUIDATION_ORACLE = _preLiquidationParams.preLiquidationOracle;
ERC20(_marketParams.loanToken).safeApprove(morpho, type(uint256).max);
}
/* PRE-LIQUIDATION */
/// @notice Pre-liquidates the given borrower on the market of this contract and with the parameters of this
/// contract.
/// @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 `onPreLiquidate` callback. Pass empty data if not needed.
/// @return seizedAssets The amount of collateral seized.
/// @return repaidAssets The amount of debt repaid.
/// @dev Either `seizedAssets` or `repaidShares` should be zero.
/// @dev Reverts if the account is still liquidatable on Morpho after the pre-liquidation (withdrawCollateral will
/// fail). This can happen if either the LIF is bigger than 1/LLTV, or if the account is already unhealthy on
/// Morpho.
/// @dev The pre-liquidation close factor (preLCF) is the maximum proportion of debt that can be pre-liquidated at
/// once. It increases linearly from preLCF1 at preLltv to preLCF2 at LLTV.
/// @dev The pre-liquidation incentive factor (preLIF) is the factor by which the repaid debt is multiplied to
/// compute the seized collateral. It increases linearly from preLIF1 at preLltv to preLIF2 at LLTV.
function preLiquidate(address borrower, uint256 seizedAssets, uint256 repaidShares, bytes calldata data)
external
returns (uint256, uint256)
{
require(UtilsLib.exactlyOneZero(seizedAssets, repaidShares), ErrorsLib.InconsistentInput());
MORPHO.accrueInterest(marketParams());
Market memory market = MORPHO.market(ID);
Position memory position = MORPHO.position(ID, borrower);
uint256 collateralPrice = IOracle(PRE_LIQUIDATION_ORACLE).price();
uint256 collateralQuoted = uint256(position.collateral).mulDivDown(collateralPrice, ORACLE_PRICE_SCALE);
uint256 borrowed = uint256(position.borrowShares).toAssetsUp(market.totalBorrowAssets, market.totalBorrowShares);
// The two following require-statements ensure that collateralQuoted is different from zero.
require(borrowed <= collateralQuoted.wMulDown(LLTV), ErrorsLib.LiquidatablePosition());
// The following require-statement is equivalent to checking that ltv > PRE_LLTV.
require(borrowed > collateralQuoted.wMulDown(PRE_LLTV), ErrorsLib.NotPreLiquidatablePosition());
uint256 ltv = borrowed.wDivUp(collateralQuoted);
uint256 quotient = (ltv - PRE_LLTV).wDivDown(LLTV - PRE_LLTV);
uint256 preLIF = quotient.wMulDown(PRE_LIF_2 - PRE_LIF_1) + PRE_LIF_1;
if (seizedAssets > 0) {
uint256 seizedAssetsQuoted = seizedAssets.mulDivUp(collateralPrice, ORACLE_PRICE_SCALE);
repaidShares =
seizedAssetsQuoted.wDivUp(preLIF).toSharesUp(market.totalBorrowAssets, market.totalBorrowShares);
} else {
seizedAssets = repaidShares.toAssetsDown(market.totalBorrowAssets, market.totalBorrowShares).wMulDown(
preLIF
).mulDivDown(ORACLE_PRICE_SCALE, collateralPrice);
}
// Note that the pre-liquidation close factor can be greater than WAD (100%).
// In this case the position can be fully pre-liquidated.
uint256 preLCF = quotient.wMulDown(PRE_LCF_2 - PRE_LCF_1) + PRE_LCF_1;
uint256 repayableShares = uint256(position.borrowShares).wMulDown(preLCF);
require(repaidShares <= repayableShares, ErrorsLib.PreLiquidationTooLarge(repaidShares, repayableShares));
bytes memory callbackData = abi.encode(seizedAssets, borrower, msg.sender, data);
(uint256 repaidAssets,) = MORPHO.repay(marketParams(), 0, repaidShares, borrower, callbackData);
emit EventsLib.PreLiquidate(ID, msg.sender, borrower, repaidAssets, repaidShares, seizedAssets);
return (seizedAssets, repaidAssets);
}
/// @notice Morpho callback after repay call.
/// @dev During pre-liquidation, Morpho will call the `onMorphoRepay` callback function in `PreLiquidation` using
/// the provided `data`.
function onMorphoRepay(uint256 repaidAssets, bytes calldata callbackData) external {
require(msg.sender == address(MORPHO), ErrorsLib.NotMorpho());
(uint256 seizedAssets, address borrower, address liquidator, bytes memory data) =
abi.decode(callbackData, (uint256, address, address, bytes));
MORPHO.withdrawCollateral(marketParams(), seizedAssets, borrower, liquidator);
if (data.length > 0) {
IPreLiquidationCallback(liquidator).onPreLiquidate(repaidAssets, data);
}
ERC20(LOAN_TOKEN).safeTransferFrom(liquidator, address(this), repaidAssets);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >= 0.5.0;
import {Id, IMorpho, MarketParams} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol";
/// @notice The pre-liquidation parameters are:
/// - preLltv, the maximum LTV of a position before allowing pre-liquidation, scaled by WAD.
/// - preLCF1, the pre-liquidation close factor when the position LTV is equal to preLltv, scaled by WAD.
/// - preLCF2, the pre-liquidation close factor when the position LTV is equal to LLTV, scaled by WAD.
/// - preLIF1, the pre-liquidation incentive factor when the position LTV is equal to preLltv, scaled by WAD.
/// - preLIF2, the pre-liquidation incentive factor when the position LTV is equal to LLTV, scaled by WAD.
/// - preLiquidationOracle, the oracle used to assess whether or not a position can be preliquidated.
struct PreLiquidationParams {
uint256 preLltv;
uint256 preLCF1;
uint256 preLCF2;
uint256 preLIF1;
uint256 preLIF2;
address preLiquidationOracle;
}
interface IPreLiquidation {
function MORPHO() external view returns (IMorpho);
function ID() external view returns (Id);
function marketParams() external returns (MarketParams memory);
function preLiquidationParams() external view returns (PreLiquidationParams memory);
function preLiquidate(address borrower, uint256 seizedAssets, uint256 repaidShares, bytes calldata data)
external
returns (uint256, uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >= 0.5.0;
/// @title IPreLiquidationCallback
/// @notice Interface that "pre-liquidators" willing to use the pre-liquidation callback must implement.
interface IPreLiquidationCallback {
/// @notice Callback called when a pre-liquidation occurs.
/// @dev The callback is called only if data is not empty.
/// @param repaidAssets The amount of repaid assets.
/// @param data Arbitrary data passed to the `preLiquidate` function.
function onPreLiquidate(uint256 repaidAssets, bytes calldata data) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >= 0.5.0;
import {Id, IMorpho} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {IPreLiquidation, PreLiquidationParams} from "./IPreLiquidation.sol";
interface IPreLiquidationFactory {
function MORPHO() external view returns (IMorpho);
function isPreLiquidation(address) external returns (bool);
function createPreLiquidation(Id id, PreLiquidationParams calldata preLiquidationParams)
external
returns (IPreLiquidation preLiquidation);
}// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @title ErrorsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library exposing errors. library ErrorsLib { /* PRELIQUIDATION ERRORS */ error PreLltvTooHigh(); error PreLCFDecreasing(); error PreLCFTooHigh(); error PreLIFTooLow(); error PreLIFDecreasing(); error PreLIFTooHigh(); error InconsistentInput(); error NotPreLiquidatablePosition(); error LiquidatablePosition(); error PreLiquidationTooLarge(uint256 repaidShares, uint256 repayableShares); error NotMorpho(); error NonexistentMarket(); /* PRELIQUIDATION FACTORY ERRORS */ error ZeroAddress(); }
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {PreLiquidationParams} from "../interfaces/IPreLiquidation.sol";
/// @title EventsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing events.
library EventsLib {
/// @dev This event is emitted after calling `onPreLiquidate` which can tamper with the order of events.
event PreLiquidate(
Id indexed id,
address indexed liquidator,
address indexed borrower,
uint256 repaidAssets,
uint256 repaidShares,
uint256 seizedAssets
);
event CreatePreLiquidation(address indexed preLiquidation, Id id, PreLiquidationParams preLiquidationParams);
}{
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"appendCBOR": true,
"bytecodeHash": "none",
"useLiteralContent": false
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"solmate/=lib/bundler3/lib/permit2/lib/solmate/",
"@openzeppelin/contracts/=lib/metamorpho-1.1/lib/openzeppelin-contracts/contracts/",
"bundler3/=lib/bundler3/",
"ds-test/=lib/metamorpho-1.1/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/metamorpho-1.1/lib/erc4626-tests/",
"forge-gas-snapshot/=lib/bundler3/lib/permit2/lib/forge-gas-snapshot/src/",
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/morpho-blue/lib/halmos-cheatcodes/src/",
"metamorpho-1.1/=lib/metamorpho-1.1/",
"metamorpho/=lib/public-allocator/lib/metamorpho/",
"morpho-blue-irm/=lib/morpho-blue-irm/src/",
"morpho-blue-oracles/=lib/morpho-blue-oracles/src/",
"morpho-blue/=lib/morpho-blue/",
"murky/=lib/universal-rewards-distributor/lib/murky/src/",
"openzeppelin-contracts/=lib/metamorpho-1.1/lib/openzeppelin-contracts/",
"openzeppelin/=lib/universal-rewards-distributor/lib/openzeppelin-contracts/contracts/",
"permit2/=lib/bundler3/lib/permit2/",
"pre-liquidation/=lib/pre-liquidation/src/",
"public-allocator/=lib/public-allocator/src/",
"safe-smart-account/=lib/safe-smart-account/",
"universal-rewards-distributor/=lib/universal-rewards-distributor/"
],
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"morpho","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"preLiquidation","type":"address"},{"indexed":false,"internalType":"Id","name":"id","type":"bytes32"},{"components":[{"internalType":"uint256","name":"preLltv","type":"uint256"},{"internalType":"uint256","name":"preLCF1","type":"uint256"},{"internalType":"uint256","name":"preLCF2","type":"uint256"},{"internalType":"uint256","name":"preLIF1","type":"uint256"},{"internalType":"uint256","name":"preLIF2","type":"uint256"},{"internalType":"address","name":"preLiquidationOracle","type":"address"}],"indexed":false,"internalType":"struct PreLiquidationParams","name":"preLiquidationParams","type":"tuple"}],"name":"CreatePreLiquidation","type":"event"},{"inputs":[],"name":"MORPHO","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"},{"components":[{"internalType":"uint256","name":"preLltv","type":"uint256"},{"internalType":"uint256","name":"preLCF1","type":"uint256"},{"internalType":"uint256","name":"preLCF2","type":"uint256"},{"internalType":"uint256","name":"preLIF1","type":"uint256"},{"internalType":"uint256","name":"preLIF2","type":"uint256"},{"internalType":"address","name":"preLiquidationOracle","type":"address"}],"internalType":"struct PreLiquidationParams","name":"preLiquidationParams","type":"tuple"}],"name":"createPreLiquidation","outputs":[{"internalType":"contract IPreLiquidation","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isPreLiquidation","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a034608257601f61215838819003918201601f19168301916001600160401b03831184841017608657808492602094604052833981010312608257516001600160a01b0381169081900360825780156073576080526040516120bd908161009b8239608051818181608701526101b30152f35b63d92e233d60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c9081631c32209214610119575080631ea36725146100af57633acb56241461003d575f80fd5b346100ab575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100ab57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b5f80fd5b346100ab5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100ab5760043573ffffffffffffffffffffffffffffffffffffffff81168091036100ab575f525f602052602060ff60405f2054166040519015158152f35b346100ab5760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100ab576004359060c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126100ab57611dac9081810181811067ffffffffffffffff82111761028a5781610100915f94610305833973ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001681528560208201526101e7604082016102b7565b03019082f5801561027f576020917fc36ddf254f2ae7c3a2c82dc525fed9a804cdaf8102b19b3caa5aeda5afcca38273ffffffffffffffffffffffffffffffffffffffff60e093169283926040519081526102438682016102b7565ba2805f525f825260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055604051908152f35b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b602435815260443560208201526064356040820152608435606082015260a435608082015260c4359073ffffffffffffffffffffffffffffffffffffffff82168092036100ab5760a0015256fe61022080604052346104675780611dac803803809161001e8285610545565b83398101039061010082126104675761003681610568565b60c0602083015193603f19011261046757604051916100548361052a565b60408101518352606081015193602084019485526080820151926040850193845260a0830151906060860191825261009a60e060c0860151956080890196875201610568565b60a08701908152604051632e3071cd60e11b8152600481018590526001600160a01b0390921694909160c081602481895afa908115610473575f9161048d575b50608001516001600160801b03161561047e57604051632c3c915760e01b8152600481018590529260a084602481895afa938415610473575f946103c2575b508751966080850197885111156103b35789518151106103a457670de0b6b3a76400008a5111610395578151670de0b6b3a76400001161038657815183511061037757825188518015610363576ec097ce7bc90715b34b9f1000000000041061035457608087905260a09590955283516001600160a01b0390811660c052602085810151821660e052604080870151831661010052606087015183166101205298516101405298516101605298516101805293516101a05292516101c05291516101e052905185166102005251915163095ea7b360e01b815260048101919091525f19602482015291925f9260449290918491165af13d15601f3d1160015f51141617161561031e5760405161181b908161059182396080518181816101ce0152818161028d0152611113015260a0518181816095015281816103b3015281816104180152610a94015260c051818181611361015261167f015260e051816116b8015261010051816116f401526101205181611730015261014051818181610590015281816106a101526117570152610160518181816105ca01528181610655015281816106800152610f740152610180518181816108310152610fbe01526101a0518181816108570152610fe501526101c0518181816106f0015261100d01526101e05181818161071701526110370152610200518181816104b4015261105d0152f35b60405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606490fd5b6331aff75360e01b5f5260045ffd5b634e487b7160e01b5f52601260045260245ffd5b63141da4bd60e21b5f5260045ffd5b630136247b60e51b5f5260045ffd5b630287fe3b60e31b5f5260045ffd5b631bc4f82360e01b5f5260045ffd5b63f0586c2360e01b5f5260045ffd5b90935060a0813d60a01161046b575b816103de60a09383610545565b81010312610467576040519060a08201906001600160401b038211838310176104535760809160405261041081610568565b835261041e60208201610568565b602084015261042f60408201610568565b604084015261044060608201610568565b606084015201516080820152925f610119565b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b3d91506103d1565b6040513d5f823e3d90fd5b630ecde9b360e31b5f5260045ffd5b905060c0813d60c011610522575b816104a860c09383610545565b810103126104675761051760a0604051926104c28461052a565b6104cb8161057c565b84526104d96020820161057c565b60208501526104ea6040820161057c565b60408501526104fb6060820161057c565b606085015261050c6080820161057c565b60808501520161057c565b60a08201525f6100da565b3d915061049b565b60c081019081106001600160401b0382111761045357604052565b601f909101601f19168101906001600160401b0382119082101761045357604052565b51906001600160a01b038216820361046757565b51906001600160801b03821682036104675756fe6080806040526004361015610012575f80fd5b5f905f3560e01c90816305b4591c146110a6575080631d553cee14610f165780633078f50a146101f25780633acb5624146101835780637b9e68f2146100bb5763b3cea21714610060575f80fd5b346100b857807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b80fd5b50346100b857807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b85760a06100f4611636565b61018160405180926080809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff602082015116602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015273ffffffffffffffffffffffffffffffffffffffff60608201511660608501520151910152565bf35b50346100b857807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100b85760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b85760043573ffffffffffffffffffffffffffffffffffffffff81168103610f125760243560643567ffffffffffffffff8111610f0e576102659036906004016114f4565b60443594918391908215871514610ee65773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016926102b6611636565b843b15610eb657610371604051917f151c1ade00000000000000000000000000000000000000000000000000000000835260048301906080809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff602082015116602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015273ffffffffffffffffffffffffffffffffffffffff60608201511660608501520151910152565b858160a48183895af18015610ec257908691610ecd575b5050604051907f5c60e39a0000000000000000000000000000000000000000000000000000000082527f0000000000000000000000000000000000000000000000000000000000000000600483015260c082602481885afa918215610ec2578692610e1f575b50604051917f93c520620000000000000000000000000000000000000000000000000000000083527f0000000000000000000000000000000000000000000000000000000000000000600484015273ffffffffffffffffffffffffffffffffffffffff89166024840152606083604481895afa928315610e14578793610d6c575b50604051917fa035b1fe00000000000000000000000000000000000000000000000000000000835260208360048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa928315610d61578893610d29575b5060206ec097ce7bc90715b34b9f1000000000610517856fffffffffffffffffffffffffffffffff60408901511661177e565b049401926fffffffffffffffffffffffffffffffff8451166fffffffffffffffffffffffffffffffff6040850151166fffffffffffffffffffffffffffffffff6060860151169160018201809211610c7f57620f42408301809311610c7f579061058192916117c8565b94670de0b6b3a76400006105b57f00000000000000000000000000000000000000000000000000000000000000008361177e565b048611610d0157670de0b6b3a76400006105ef7f00000000000000000000000000000000000000000000000000000000000000008361177e565b04861115610cd957670de0b6b3a76400009586810290808204881490151715610cac577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820190828211610c7f579161064e6106539261067a94611629565b611791565b7f00000000000000000000000000000000000000000000000000000000000000009061161c565b9b6106c57f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061161c565b9c86810290808204881490151715610b78578a876107429c9d9e9f6106ed9061074894611791565b9c7f0000000000000000000000000000000000000000000000000000000000000000928e61073b857f000000000000000000000000000000000000000000000000000000000000000061161c565b915061177e565b04611629565b9215610ba55750610759908b61177e565b6ec097ce7bc90715b34b9f0fffffffff8101809111610b4b576ec097ce7bc90715b34b9f1000000000900484810290808204861490151715610b4b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820190828211610b78576107ce929161064e91611629565b6fffffffffffffffffffffffffffffffff6060816040850151169301511690620f42408201809211610b4b5760018301809311610b4b5761082861088394936fffffffffffffffffffffffffffffffff9361087b936117c8565b985b856107428c7f00000000000000000000000000000000000000000000000000000000000000009361073b857f000000000000000000000000000000000000000000000000000000000000000061161c565b91511661177e565b04808511610b1b5750610a2f9261093160c0887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f876040988e9873ffffffffffffffffffffffffffffffffffffffff8b519a8b976020890152168b8701523360608701526080808701528160a0870152868601378b8582860101520116810103017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810184528361157b565b6109f38661093d611636565b8551968795869485937f20b76e8100000000000000000000000000000000000000000000000000000000855260048501906080809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff602082015116602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015273ffffffffffffffffffffffffffffffffffffffff60608201511660608501520151910152565b8360a48401528960c484015273ffffffffffffffffffffffffffffffffffffffff8d1660e48401526101206101048401526101248301906115bc565b03925af1928315610b0f578093610ac1575b505073ffffffffffffffffffffffffffffffffffffffff60409485519284845260208401528486840152169033907fd5b01f148b35d6069b626af105bf8881bc2e30ee1ce3de4630903abab0ba858060607f000000000000000000000000000000000000000000000000000000000000000092a482519182526020820152f35b909492506040853d604011610b07575b81610ade6040938361157b565b810103126100b8575073ffffffffffffffffffffffffffffffffffffffff604094519294610a41565b3d9150610ad1565b604051903d90823e3d90fd5b85604491867f44bb1e75000000000000000000000000000000000000000000000000000000008352600452602452fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b60248b7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b989a50916fffffffffffffffffffffffffffffffff606081604084015116920151169060018101809111610b7857620f42408201809211610b785791610bf5610bfa9261064e889560443561177e565b61177e565b046ec097ce7bc90715b34b9f10000000008102908082046ec097ce7bc90715b34b9f10000000001490151715610c525761087b610c4c61088394936fffffffffffffffffffffffffffffffff93611791565b9a61082a565b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b60248c7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b60048a7f0bb9a651000000000000000000000000000000000000000000000000000000008152fd5b60048a7ff78266a2000000000000000000000000000000000000000000000000000000008152fd5b9092506020813d602011610d59575b81610d456020938361157b565b81010312610d555751915f6104e4565b8780fd5b3d9150610d38565b6040513d8a823e3d90fd5b9092506060813d606011610e0c575b81610d886060938361157b565b81010312610e0857604051906060820182811067ffffffffffffffff821117610ddb57610dcf91604091825280518452610dc4602082016115ff565b6020850152016115ff565b6040820152915f61046f565b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8680fd5b3d9150610d7b565b6040513d89823e3d90fd5b90915060c0813d60c011610eba575b81610e3b60c0938361157b565b81010312610eb657610eaa60a060405192610e5584611543565b610e5e816115ff565b8452610e6c602082016115ff565b6020850152610e7d604082016115ff565b6040850152610e8e606082016115ff565b6060850152610e9f608082016115ff565b6080850152016115ff565b60a0820152905f6103ee565b8580fd5b3d9150610e2e565b6040513d88823e3d90fd5b81610ed79161157b565b610ee257845f610388565b8480fd5b6004847ff0732dd7000000000000000000000000000000000000000000000000000000008152fd5b8380fd5b5080fd5b50346100b857807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b85760a0604051610f5381611543565b828152826020820152826040820152826060820152826080820152015260c07f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff604051610fb481611543565b82815260208101907f00000000000000000000000000000000000000000000000000000000000000008252604081017f0000000000000000000000000000000000000000000000000000000000000000815260608201907f0000000000000000000000000000000000000000000000000000000000000000825260a060808401937f000000000000000000000000000000000000000000000000000000000000000085520193857f0000000000000000000000000000000000000000000000000000000000000000168552604051968752516020870152516040860152516060850152516080840152511660a0820152f35b90503461149b5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261149b5760043560243567ffffffffffffffff811161149b576110fa9036906004016114f4565b929073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016938433036114cc5781019360808286031261149b5761115460208301611522565b9461116160408401611522565b606084013567ffffffffffffffff811161149b5784019180601f8401121561149b5782359067ffffffffffffffff821161149f576111c760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116018861157b565b8187526020828501011161149b576020815f928273ffffffffffffffffffffffffffffffffffffffff9601838a0137870101521694611204611636565b91803b1561149b575f92836101049273ffffffffffffffffffffffffffffffffffffffff6112e19560405198899788967f8720316d00000000000000000000000000000000000000000000000000000000885260048801906080809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff602082015116602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015273ffffffffffffffffffffffffffffffffffffffff60608201511660608501520151910152565b3560a48601521660c48401528960e48401525af1801561149057611479575b5090818492516113f7575b50602092606491604051917f23b872dd000000000000000000000000000000000000000000000000000000008352600483015230602483015260448201528273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af13d15601f3d116001845114161716156113995780f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b833b15611475578261144491604051809381927f8e8beec40000000000000000000000000000000000000000000000000000000083528660048401526040602484015260448301906115bc565b038183885af1801561146a571561130b5761146083809261157b565b610f12575f61130b565b6040513d85823e3d90fd5b8280fd5b611487919294505f9061157b565b5f92905f611300565b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fe51b5123000000000000000000000000000000000000000000000000000000005f5260045ffd5b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020838186019501011161149b57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b60c0810190811067ffffffffffffffff82111761149f57604052565b60a0810190811067ffffffffffffffff82111761149f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761149f57604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b51906fffffffffffffffffffffffffffffffff8216820361149b57565b91908203918211610cac57565b91908201809211610cac57565b5f60806040516116458161155f565b82815282602082015282604082015282606082015201526040516116688161155f565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016604082015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608201527f0000000000000000000000000000000000000000000000000000000000000000608082015290565b81810292918115918404141715610cac57565b811561179b570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b906117d29161177e565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810191818311610cac5761180b9261064e91611629565b9056fea164736f6c634300081b000aa164736f6c634300081b000a000000000000000000000000d50f2dfffd62f94ee4aed9ca05c61d0753268abc
Deployed Bytecode
0x6080806040526004361015610012575f80fd5b5f3560e01c9081631c32209214610119575080631ea36725146100af57633acb56241461003d575f80fd5b346100ab575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100ab57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d50f2dfffd62f94ee4aed9ca05c61d0753268abc168152f35b5f80fd5b346100ab5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100ab5760043573ffffffffffffffffffffffffffffffffffffffff81168091036100ab575f525f602052602060ff60405f2054166040519015158152f35b346100ab5760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100ab576004359060c07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc3601126100ab57611dac9081810181811067ffffffffffffffff82111761028a5781610100915f94610305833973ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d50f2dfffd62f94ee4aed9ca05c61d0753268abc1681528560208201526101e7604082016102b7565b03019082f5801561027f576020917fc36ddf254f2ae7c3a2c82dc525fed9a804cdaf8102b19b3caa5aeda5afcca38273ffffffffffffffffffffffffffffffffffffffff60e093169283926040519081526102438682016102b7565ba2805f525f825260405f2060017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00825416179055604051908152f35b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b602435815260443560208201526064356040820152608435606082015260a435608082015260c4359073ffffffffffffffffffffffffffffffffffffffff82168092036100ab5760a0015256fe61022080604052346104675780611dac803803809161001e8285610545565b83398101039061010082126104675761003681610568565b60c0602083015193603f19011261046757604051916100548361052a565b60408101518352606081015193602084019485526080820151926040850193845260a0830151906060860191825261009a60e060c0860151956080890196875201610568565b60a08701908152604051632e3071cd60e11b8152600481018590526001600160a01b0390921694909160c081602481895afa908115610473575f9161048d575b50608001516001600160801b03161561047e57604051632c3c915760e01b8152600481018590529260a084602481895afa938415610473575f946103c2575b508751966080850197885111156103b35789518151106103a457670de0b6b3a76400008a5111610395578151670de0b6b3a76400001161038657815183511061037757825188518015610363576ec097ce7bc90715b34b9f1000000000041061035457608087905260a09590955283516001600160a01b0390811660c052602085810151821660e052604080870151831661010052606087015183166101205298516101405298516101605298516101805293516101a05292516101c05291516101e052905185166102005251915163095ea7b360e01b815260048101919091525f19602482015291925f9260449290918491165af13d15601f3d1160015f51141617161561031e5760405161181b908161059182396080518181816101ce0152818161028d0152611113015260a0518181816095015281816103b3015281816104180152610a94015260c051818181611361015261167f015260e051816116b8015261010051816116f401526101205181611730015261014051818181610590015281816106a101526117570152610160518181816105ca01528181610655015281816106800152610f740152610180518181816108310152610fbe01526101a0518181816108570152610fe501526101c0518181816106f0015261100d01526101e05181818161071701526110370152610200518181816104b4015261105d0152f35b60405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b6044820152606490fd5b6331aff75360e01b5f5260045ffd5b634e487b7160e01b5f52601260045260245ffd5b63141da4bd60e21b5f5260045ffd5b630136247b60e51b5f5260045ffd5b630287fe3b60e31b5f5260045ffd5b631bc4f82360e01b5f5260045ffd5b63f0586c2360e01b5f5260045ffd5b90935060a0813d60a01161046b575b816103de60a09383610545565b81010312610467576040519060a08201906001600160401b038211838310176104535760809160405261041081610568565b835261041e60208201610568565b602084015261042f60408201610568565b604084015261044060608201610568565b606084015201516080820152925f610119565b634e487b7160e01b5f52604160045260245ffd5b5f80fd5b3d91506103d1565b6040513d5f823e3d90fd5b630ecde9b360e31b5f5260045ffd5b905060c0813d60c011610522575b816104a860c09383610545565b810103126104675761051760a0604051926104c28461052a565b6104cb8161057c565b84526104d96020820161057c565b60208501526104ea6040820161057c565b60408501526104fb6060820161057c565b606085015261050c6080820161057c565b60808501520161057c565b60a08201525f6100da565b3d915061049b565b60c081019081106001600160401b0382111761045357604052565b601f909101601f19168101906001600160401b0382119082101761045357604052565b51906001600160a01b038216820361046757565b51906001600160801b03821682036104675756fe6080806040526004361015610012575f80fd5b5f905f3560e01c90816305b4591c146110a6575080631d553cee14610f165780633078f50a146101f25780633acb5624146101835780637b9e68f2146100bb5763b3cea21714610060575f80fd5b346100b857807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b80fd5b50346100b857807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b85760a06100f4611636565b61018160405180926080809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff602082015116602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015273ffffffffffffffffffffffffffffffffffffffff60608201511660608501520151910152565bf35b50346100b857807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100b85760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b85760043573ffffffffffffffffffffffffffffffffffffffff81168103610f125760243560643567ffffffffffffffff8111610f0e576102659036906004016114f4565b60443594918391908215871514610ee65773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016926102b6611636565b843b15610eb657610371604051917f151c1ade00000000000000000000000000000000000000000000000000000000835260048301906080809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff602082015116602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015273ffffffffffffffffffffffffffffffffffffffff60608201511660608501520151910152565b858160a48183895af18015610ec257908691610ecd575b5050604051907f5c60e39a0000000000000000000000000000000000000000000000000000000082527f0000000000000000000000000000000000000000000000000000000000000000600483015260c082602481885afa918215610ec2578692610e1f575b50604051917f93c520620000000000000000000000000000000000000000000000000000000083527f0000000000000000000000000000000000000000000000000000000000000000600484015273ffffffffffffffffffffffffffffffffffffffff89166024840152606083604481895afa928315610e14578793610d6c575b50604051917fa035b1fe00000000000000000000000000000000000000000000000000000000835260208360048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa928315610d61578893610d29575b5060206ec097ce7bc90715b34b9f1000000000610517856fffffffffffffffffffffffffffffffff60408901511661177e565b049401926fffffffffffffffffffffffffffffffff8451166fffffffffffffffffffffffffffffffff6040850151166fffffffffffffffffffffffffffffffff6060860151169160018201809211610c7f57620f42408301809311610c7f579061058192916117c8565b94670de0b6b3a76400006105b57f00000000000000000000000000000000000000000000000000000000000000008361177e565b048611610d0157670de0b6b3a76400006105ef7f00000000000000000000000000000000000000000000000000000000000000008361177e565b04861115610cd957670de0b6b3a76400009586810290808204881490151715610cac577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820190828211610c7f579161064e6106539261067a94611629565b611791565b7f00000000000000000000000000000000000000000000000000000000000000009061161c565b9b6106c57f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061161c565b9c86810290808204881490151715610b78578a876107429c9d9e9f6106ed9061074894611791565b9c7f0000000000000000000000000000000000000000000000000000000000000000928e61073b857f000000000000000000000000000000000000000000000000000000000000000061161c565b915061177e565b04611629565b9215610ba55750610759908b61177e565b6ec097ce7bc90715b34b9f0fffffffff8101809111610b4b576ec097ce7bc90715b34b9f1000000000900484810290808204861490151715610b4b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820190828211610b78576107ce929161064e91611629565b6fffffffffffffffffffffffffffffffff6060816040850151169301511690620f42408201809211610b4b5760018301809311610b4b5761082861088394936fffffffffffffffffffffffffffffffff9361087b936117c8565b985b856107428c7f00000000000000000000000000000000000000000000000000000000000000009361073b857f000000000000000000000000000000000000000000000000000000000000000061161c565b91511661177e565b04808511610b1b5750610a2f9261093160c0887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f876040988e9873ffffffffffffffffffffffffffffffffffffffff8b519a8b976020890152168b8701523360608701526080808701528160a0870152868601378b8582860101520116810103017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810184528361157b565b6109f38661093d611636565b8551968795869485937f20b76e8100000000000000000000000000000000000000000000000000000000855260048501906080809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff602082015116602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015273ffffffffffffffffffffffffffffffffffffffff60608201511660608501520151910152565b8360a48401528960c484015273ffffffffffffffffffffffffffffffffffffffff8d1660e48401526101206101048401526101248301906115bc565b03925af1928315610b0f578093610ac1575b505073ffffffffffffffffffffffffffffffffffffffff60409485519284845260208401528486840152169033907fd5b01f148b35d6069b626af105bf8881bc2e30ee1ce3de4630903abab0ba858060607f000000000000000000000000000000000000000000000000000000000000000092a482519182526020820152f35b909492506040853d604011610b07575b81610ade6040938361157b565b810103126100b8575073ffffffffffffffffffffffffffffffffffffffff604094519294610a41565b3d9150610ad1565b604051903d90823e3d90fd5b85604491867f44bb1e75000000000000000000000000000000000000000000000000000000008352600452602452fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b60248b7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b989a50916fffffffffffffffffffffffffffffffff606081604084015116920151169060018101809111610b7857620f42408201809211610b785791610bf5610bfa9261064e889560443561177e565b61177e565b046ec097ce7bc90715b34b9f10000000008102908082046ec097ce7bc90715b34b9f10000000001490151715610c525761087b610c4c61088394936fffffffffffffffffffffffffffffffff93611791565b9a61082a565b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b60248c7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b60048a7f0bb9a651000000000000000000000000000000000000000000000000000000008152fd5b60048a7ff78266a2000000000000000000000000000000000000000000000000000000008152fd5b9092506020813d602011610d59575b81610d456020938361157b565b81010312610d555751915f6104e4565b8780fd5b3d9150610d38565b6040513d8a823e3d90fd5b9092506060813d606011610e0c575b81610d886060938361157b565b81010312610e0857604051906060820182811067ffffffffffffffff821117610ddb57610dcf91604091825280518452610dc4602082016115ff565b6020850152016115ff565b6040820152915f61046f565b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8680fd5b3d9150610d7b565b6040513d89823e3d90fd5b90915060c0813d60c011610eba575b81610e3b60c0938361157b565b81010312610eb657610eaa60a060405192610e5584611543565b610e5e816115ff565b8452610e6c602082016115ff565b6020850152610e7d604082016115ff565b6040850152610e8e606082016115ff565b6060850152610e9f608082016115ff565b6080850152016115ff565b60a0820152905f6103ee565b8580fd5b3d9150610e2e565b6040513d88823e3d90fd5b81610ed79161157b565b610ee257845f610388565b8480fd5b6004847ff0732dd7000000000000000000000000000000000000000000000000000000008152fd5b8380fd5b5080fd5b50346100b857807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100b85760a0604051610f5381611543565b828152826020820152826040820152826060820152826080820152015260c07f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff604051610fb481611543565b82815260208101907f00000000000000000000000000000000000000000000000000000000000000008252604081017f0000000000000000000000000000000000000000000000000000000000000000815260608201907f0000000000000000000000000000000000000000000000000000000000000000825260a060808401937f000000000000000000000000000000000000000000000000000000000000000085520193857f0000000000000000000000000000000000000000000000000000000000000000168552604051968752516020870152516040860152516060850152516080840152511660a0820152f35b90503461149b5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261149b5760043560243567ffffffffffffffff811161149b576110fa9036906004016114f4565b929073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016938433036114cc5781019360808286031261149b5761115460208301611522565b9461116160408401611522565b606084013567ffffffffffffffff811161149b5784019180601f8401121561149b5782359067ffffffffffffffff821161149f576111c760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116018861157b565b8187526020828501011161149b576020815f928273ffffffffffffffffffffffffffffffffffffffff9601838a0137870101521694611204611636565b91803b1561149b575f92836101049273ffffffffffffffffffffffffffffffffffffffff6112e19560405198899788967f8720316d00000000000000000000000000000000000000000000000000000000885260048801906080809173ffffffffffffffffffffffffffffffffffffffff815116845273ffffffffffffffffffffffffffffffffffffffff602082015116602085015273ffffffffffffffffffffffffffffffffffffffff604082015116604085015273ffffffffffffffffffffffffffffffffffffffff60608201511660608501520151910152565b3560a48601521660c48401528960e48401525af1801561149057611479575b5090818492516113f7575b50602092606491604051917f23b872dd000000000000000000000000000000000000000000000000000000008352600483015230602483015260448201528273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af13d15601f3d116001845114161716156113995780f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152fd5b833b15611475578261144491604051809381927f8e8beec40000000000000000000000000000000000000000000000000000000083528660048401526040602484015260448301906115bc565b038183885af1801561146a571561130b5761146083809261157b565b610f12575f61130b565b6040513d85823e3d90fd5b8280fd5b611487919294505f9061157b565b5f92905f611300565b6040513d5f823e3d90fd5b5f80fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fe51b5123000000000000000000000000000000000000000000000000000000005f5260045ffd5b9181601f8401121561149b5782359167ffffffffffffffff831161149b576020838186019501011161149b57565b359073ffffffffffffffffffffffffffffffffffffffff8216820361149b57565b60c0810190811067ffffffffffffffff82111761149f57604052565b60a0810190811067ffffffffffffffff82111761149f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761149f57604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b51906fffffffffffffffffffffffffffffffff8216820361149b57565b91908203918211610cac57565b91908201809211610cac57565b5f60806040516116458161155f565b82815282602082015282604082015282606082015201526040516116688161155f565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016602082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016604082015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608201527f0000000000000000000000000000000000000000000000000000000000000000608082015290565b81810292918115918404141715610cac57565b811561179b570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b906117d29161177e565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810191818311610cac5761180b9261064e91611629565b9056fea164736f6c634300081b000aa164736f6c634300081b000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000d50f2dfffd62f94ee4aed9ca05c61d0753268abc
-----Decoded View---------------
Arg [0] : morpho (address): 0xD50F2DffFd62f94Ee4AEd9ca05C61d0753268aBc
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000d50f2dfffd62f94ee4aed9ca05c61d0753268abc
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.