diff --git a/README.md b/README.md index b53ced1..3230073 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,17 @@ The two main use-cases are: 1. Using normal fixed parameters when `preLIF1 = preLIF2` and `preLCF1 = preLCF2`. 2. Using health dependent liquidation when either `preLIF1 < preLIF2` or `preLCF1 < preLCF2`, similar to a Quasi Dutch Auction (as in [Euler liquidations](https://docs-v1.euler.finance/getting-started/white-paper#liquidations)). + +### Pre-liquidation parameters restrictions + +The PreLiquidation smart-contract enforces the following properties: +- preLltv < LLTV; +- preLCF1 <= preLCF2; +- preLFC1 <= 1; +- 1 <= preLIF1 <= preLIF2 <= 1 / LLTV. +Note: Using `preLCF2 > 1`, you can select at which LTV between preLltv and LLTV the entire position will be pre-liquidated. +A pre-liquidation close factor higher than 100% means that the whole position is pre-liquidatable. + ### `onPreLiquidate` callback By calling `preLiquidate` with a smart contract that implements the `IPreLiquidationCallback` interface, the liquidator can be called back. diff --git a/src/PreLiquidation.sol b/src/PreLiquidation.sol index a1f37c2..6222d36 100644 --- a/src/PreLiquidation.sol +++ b/src/PreLiquidation.sol @@ -79,12 +79,14 @@ contract PreLiquidation is IPreLiquidation, IMorphoRepayCallback { /// @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()); diff --git a/src/libraries/ErrorsLib.sol b/src/libraries/ErrorsLib.sol index 38439b3..5c3393d 100644 --- a/src/libraries/ErrorsLib.sol +++ b/src/libraries/ErrorsLib.sol @@ -12,6 +12,8 @@ library ErrorsLib { error PreLCFDecreasing(); + error PreLCFTooHigh(); + error PreLIFTooLow(); error PreLIFDecreasing(); diff --git a/test/PreLiquidationErrorTest.sol b/test/PreLiquidationErrorTest.sol index 35928fe..6b3ee24 100644 --- a/test/PreLiquidationErrorTest.sol +++ b/test/PreLiquidationErrorTest.sol @@ -40,7 +40,7 @@ contract PreLiquidationErrorTest is BaseTest { factory.createPreLiquidation(id, preLiquidationParams); } - function testCloseFactorDecreasing(PreLiquidationParams memory preLiquidationParams) public virtual { + function testLCFDecreasing(PreLiquidationParams memory preLiquidationParams) public virtual { preLiquidationParams = boundPreLiquidationParameters({ preLiquidationParams: preLiquidationParams, minPreLltv: WAD / 100, @@ -57,6 +57,22 @@ contract PreLiquidationErrorTest is BaseTest { factory.createPreLiquidation(id, preLiquidationParams); } + function testLCFHigh(PreLiquidationParams memory preLiquidationParams) public virtual { + preLiquidationParams = boundPreLiquidationParameters({ + preLiquidationParams: preLiquidationParams, + minPreLltv: WAD / 100, + maxPreLltv: marketParams.lltv - 1, + minPreLCF: WAD + 1, + maxPreLCF: type(uint256).max, + minPreLIF: WAD + 1, + maxPreLIF: WAD.wDivDown(lltv), + preLiqOracle: marketParams.oracle + }); + + vm.expectRevert(ErrorsLib.PreLCFTooHigh.selector); + factory.createPreLiquidation(id, preLiquidationParams); + } + function testLowPreLIF(PreLiquidationParams memory preLiquidationParams) public virtual { preLiquidationParams = boundPreLiquidationParameters({ preLiquidationParams: preLiquidationParams,