Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor create2 #87

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions src/PreLiquidation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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 {IPreLiquidationFactory} from "./interfaces/IPreLiquidationFactory.sol";
import {IPreLiquidationCallback} from "./interfaces/IPreLiquidationCallback.sol";
import {IOracle} from "../lib/morpho-blue/src/interfaces/IOracle.sol";

Expand Down Expand Up @@ -73,25 +74,28 @@ contract PreLiquidation is IPreLiquidation, IMorphoRepayCallback {
/* 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 Meant to be called by the factory only.
/// @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);
constructor() {
// Not optimized yet: no need to make 3 calls.
IMorpho morpho = IPreLiquidationFactory(msg.sender).MORPHO();
Id id = IPreLiquidationFactory(msg.sender).id();
PreLiquidationParams memory _preLiquidationParams = IPreLiquidationFactory(msg.sender).preLiquidationParams();

require(morpho.market(id).lastUpdate != 0, ErrorsLib.NonexistentMarket());
MarketParams memory _marketParams = 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);
MORPHO = morpho;

ID = id;

Expand All @@ -108,7 +112,7 @@ contract PreLiquidation is IPreLiquidation, IMorphoRepayCallback {
PRE_LIF_2 = _preLiquidationParams.preLIF2;
PRE_LIQUIDATION_ORACLE = _preLiquidationParams.preLiquidationOracle;

ERC20(_marketParams.loanToken).safeApprove(morpho, type(uint256).max);
ERC20(_marketParams.loanToken).safeApprove(address(morpho), type(uint256).max);
}

/* PRE-LIQUIDATION */
Expand Down
59 changes: 49 additions & 10 deletions src/PreLiquidationFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {IMorpho, Id} from "../lib/morpho-blue/src/interfaces/IMorpho.sol";

import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {EventsLib} from "./libraries/EventsLib.sol";
import {PreLiquidationAddressLib} from "./libraries/PreLiquidationAddressLib.sol";

import {PreLiquidation} from "./PreLiquidation.sol";

Expand All @@ -22,8 +23,27 @@ contract PreLiquidationFactory is IPreLiquidationFactory {

/* STORAGE */

/// @notice Mapping which returns true if the address is a PreLiquidation contract created by this factory.
mapping(address => bool) public isPreLiquidation;
// Temporary contract creation variables.
// Not optimized yet: make those transient.
Id public id;
uint256 internal preLltv;
uint256 internal preLCF1;
uint256 internal preLCF2;
uint256 internal preLIF1;
uint256 internal preLIF2;
address internal preLiquidationOracle;

/// @notice The pre-liquidation parameters specific currently set.
function preLiquidationParams() external view returns (PreLiquidationParams memory) {
return PreLiquidationParams({
preLltv: preLltv,
preLCF1: preLCF1,
preLCF2: preLCF2,
preLIF1: preLIF1,
preLIF2: preLIF2,
preLiquidationOracle: preLiquidationOracle
});
}

/* CONSTRUCTOR */

Expand All @@ -37,20 +57,39 @@ contract PreLiquidationFactory is IPreLiquidationFactory {
/* EXTERNAL */

/// @notice Creates a PreLiquidation contract.
/// @param id The Morpho market for PreLiquidations.
/// @param preLiquidationParams The PreLiquidation params for the 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)
function createPreLiquidation(Id _id, PreLiquidationParams calldata _preLiquidationParams)
external
returns (IPreLiquidation)
{
IPreLiquidation preLiquidation =
IPreLiquidation(address(new PreLiquidation{salt: 0}(address(MORPHO), id, preLiquidationParams)));
id = _id;
preLltv = _preLiquidationParams.preLltv;
preLCF1 = _preLiquidationParams.preLCF1;
preLCF2 = _preLiquidationParams.preLCF2;
preLIF1 = _preLiquidationParams.preLIF1;
preLIF2 = _preLiquidationParams.preLIF2;
preLiquidationOracle = _preLiquidationParams.preLiquidationOracle;

bytes32 salt = PreLiquidationAddressLib.hashPreLiquidationConstructorParams(MORPHO, id, _preLiquidationParams);

address preLiquidation = PreLiquidationAddressLib.computePreLiquidationAddressFromSalt(address(this), salt);

emit EventsLib.CreatePreLiquidation(address(preLiquidation), id, preLiquidationParams);
require(preLiquidation.code.length == 0, ErrorsLib.AlreadyDeployedPreLiquidation(preLiquidation));

isPreLiquidation[address(preLiquidation)] = true;
new PreLiquidation{salt: salt}();

emit EventsLib.CreatePreLiquidation(preLiquidation, id, _preLiquidationParams);

return IPreLiquidation(preLiquidation);
}

return preLiquidation;
function isPreLiquidation(address preLiq) external view returns (bool) {
// Not optimized yet: no need to make 2 calls.
PreLiquidationParams memory _preLiquidationParams = IPreLiquidation(preLiq).preLiquidationParams();
Id _id = IPreLiquidation(preLiq).ID();
return preLiq
== PreLiquidationAddressLib.computePreLiquidationAddress(MORPHO, address(this), _id, _preLiquidationParams);
}
}
4 changes: 4 additions & 0 deletions src/interfaces/IPreLiquidationFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ interface IPreLiquidationFactory {
function createPreLiquidation(Id id, PreLiquidationParams calldata preLiquidationParams)
external
returns (IPreLiquidation preLiquidation);

function id() external view returns (Id);

function preLiquidationParams() external view returns (PreLiquidationParams memory);
}
2 changes: 2 additions & 0 deletions src/libraries/ErrorsLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,6 @@ library ErrorsLib {
/* PRELIQUIDATION FACTORY ERRORS */

error ZeroAddress();

error AlreadyDeployedPreLiquidation(address preLiquidation);
}
40 changes: 40 additions & 0 deletions src/libraries/PreLiquidationAddressLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import {PreLiquidation} from "../PreLiquidation.sol";
import {PreLiquidationParams} from "../interfaces/IPreLiquidation.sol";
import {Id, IMorpho} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol";

library PreLiquidationAddressLib {
/// @dev Should be equal to keccak256(abi.encodePacked(type(PreLiquidation).creationCode))
bytes32 internal constant INIT_CODE_HASH = 0xfd272715eba47f05c57fe589ac9906da3e899bc943693d89f067ee7ef51d01ff;

/// @notice Computes the CREATE2 address of the pre-liquidation contract generated by the `factory`
/// for a specific Morpho market `id` with the pre-liquidation parameters `preLiquidationParams`.
/// @param morpho Morpho's address.
/// @param factory PreLiquidationFactory contract address.
/// @param id Morpho market id for the pre-liquidation contract.
/// @param preLiquidationParams Pre-liquidation parameters.
/// @return preLiquidationAddress The address of this pre-liquidation contract.
function computePreLiquidationAddress(
IMorpho morpho,
address factory,
Id id,
PreLiquidationParams memory preLiquidationParams
) internal pure returns (address) {
bytes32 salt = hashPreLiquidationConstructorParams(morpho, id, preLiquidationParams);
return computePreLiquidationAddressFromSalt(factory, salt);
}

function computePreLiquidationAddressFromSalt(address factory, bytes32 salt) internal pure returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(uint8(0xff), factory, salt, INIT_CODE_HASH)))));
}

function hashPreLiquidationConstructorParams(
IMorpho morpho,
Id id,
PreLiquidationParams memory preLiquidationParams
) internal pure returns (bytes32) {
return keccak256(abi.encode(morpho, id, preLiquidationParams));
}
}
26 changes: 0 additions & 26 deletions src/libraries/periphery/PreLiquidationAddressLib.sol

This file was deleted.

19 changes: 13 additions & 6 deletions test/PreLiquidationFactoryTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ pragma solidity ^0.8.0;

import "./BaseTest.sol";

import {PreLiquidation} from "../src/PreLiquidation.sol";
import {ErrorsLib} from "../src/libraries/ErrorsLib.sol";
import {PreLiquidationAddressLib} from "../src/libraries/periphery/PreLiquidationAddressLib.sol";
import {PreLiquidationAddressLib} from "../src/libraries/PreLiquidationAddressLib.sol";

contract PreLiquidationFactoryTest is BaseTest {
using MarketParamsLib for MarketParams;
Expand Down Expand Up @@ -57,6 +58,11 @@ contract PreLiquidationFactoryTest is BaseTest {
assert(factory.isPreLiquidation(address(preLiquidation)));
}

function testInitCodeHash() public pure {
bytes32 expectedInitCodeHash = keccak256(abi.encodePacked(type(PreLiquidation).creationCode));
assertEq(PreLiquidationAddressLib.INIT_CODE_HASH, expectedInitCodeHash, "Unexpected init code hash");
}

function testCreate2Deployment(PreLiquidationParams memory preLiquidationParams) public {
preLiquidationParams = boundPreLiquidationParameters(
preLiquidationParams,
Expand All @@ -74,9 +80,8 @@ contract PreLiquidationFactoryTest is BaseTest {
factory = new PreLiquidationFactory(address(MORPHO));
IPreLiquidation preLiquidation = factory.createPreLiquidation(id, preLiquidationParams);

address preLiquidationAddress = PreLiquidationAddressLib.computePreLiquidationAddress(
address(MORPHO), address(factory), id, preLiquidationParams
);
address preLiquidationAddress =
PreLiquidationAddressLib.computePreLiquidationAddress(MORPHO, address(factory), id, preLiquidationParams);
assert(address(preLiquidation) == preLiquidationAddress);
}

Expand All @@ -96,9 +101,11 @@ contract PreLiquidationFactoryTest is BaseTest {

factory = new PreLiquidationFactory(address(MORPHO));

factory.createPreLiquidation(id, preLiquidationParams);
IPreLiquidation preLiquidation = factory.createPreLiquidation(id, preLiquidationParams);

vm.expectRevert(bytes(""));
vm.expectRevert(
abi.encodeWithSelector(ErrorsLib.AlreadyDeployedPreLiquidation.selector, address(preLiquidation))
);
factory.createPreLiquidation(id, preLiquidationParams);
}
}