@@ -187,9 +187,7 @@ contract TestUsdnProtocolActionsPrepareValidateOpenPositionData is UsdnProtocolB
187187 * below its position's liquidation price
188188 * @custom:given Partial liquidations occurred that left the user's tick un-liquidated
189189 * @custom:when The user tries to validate its position
190- * @custom:then Nothing happens
191- * @custom:when The lastPrice value is updated and the user tries to validate again
192- * @custom:then The position was not liquidated and matching data is returned
190+ * @custom:then The position is liquidated
193191 */
194192 function test_prepareValidateOpenPositionDataWithStartPriceLowerThanLiquidationPrice () public {
195193 skip (1 hours);
@@ -211,8 +209,9 @@ contract TestUsdnProtocolActionsPrepareValidateOpenPositionData is UsdnProtocolB
211209 protocol.i_prepareValidateOpenPositionData (pendingAction, currentPriceData);
212210
213211 uint24 liquidationPenalty = protocol.getLiquidationPenalty ();
214- uint256 positionTotalExpo =
215- protocol.i_calcPositionTotalExpo (POSITION_AMOUNT, params.initialPrice, liqPriceWithoutPenalty);
212+ uint256 positionTotalExpo = protocol.i_calcPositionTotalExpo (
213+ POSITION_AMOUNT, params.initialPrice, data.liqPriceWithoutPenaltyNorFunding
214+ );
216215
217216 /* ------------------------ checking returned values ------------------------ */
218217 assertTrue (liquidated, "The position should have been liquidated " );
@@ -259,12 +258,84 @@ contract TestUsdnProtocolActionsPrepareValidateOpenPositionData is UsdnProtocolB
259258 );
260259 }
261260
261+ /**
262+ * @custom:scenario A user wants to validate its action but the provided price is not fresh and the lastPrice is
263+ * below its position's liquidation price (without liq penalty nor fundings)
264+ * @custom:given `startPrice` is above the liquidation price but below the liquidation price without fundings
265+ * @custom:and fundings are enabled
266+ * @custom:when The user tries to validate its position
267+ * @custom:then The position is liquidated
268+ */
269+ function test_prepareValidateOpenPositionDataWithStartPriceLowerThanLiquidationPriceWithoutPenaltyNorFunding ()
270+ public
271+ {
272+ skip (1 hours);
273+ vm.startPrank (ADMIN);
274+ protocol.setFundingSF (500 );
275+ vm.stopPrank ();
276+
277+ // big position to have high fundings
278+ setUpUserPositionInVault (USER_1, ProtocolAction.ValidateDeposit, 20 ether, DEFAULT_PARAMS.initialPrice);
279+
280+ (, posId) = protocol.initiateOpenPosition (
281+ POSITION_AMOUNT,
282+ params.initialPrice * 8 / 10 ,
283+ type (uint128 ).max,
284+ protocol.getMaxLeverage (),
285+ address (this ),
286+ USER_1,
287+ type (uint256 ).max,
288+ currentPriceData,
289+ EMPTY_PREVIOUS_DATA
290+ );
291+ timestampAtInitiate = uint40 (block .timestamp );
292+
293+ skip (4 hours);
294+
295+ // update lastPrice and lastUpdatedTimestamp
296+ protocol.liquidate (currentPriceData);
297+
298+ liqPriceWithoutPenalty = protocol.getEffectivePriceForTick (protocol.i_calcTickWithoutPenalty (posId.tick));
299+ (pendingAction,) = protocol.i_getPendingAction (USER_1);
300+ uint24 liquidationPenalty = protocol.getLiquidationPenalty ();
301+ uint256 liqPriceWithoutPenaltyNorFunding = protocol.i_getEffectivePriceForTick (
302+ protocol.i_calcTickWithoutPenalty (posId.tick, liquidationPenalty), pendingAction.var6
303+ );
304+
305+ // price below the liquidation price of the initiated position
306+ uint256 price = protocol.getEffectivePriceForTick (posId.tick) + 10 ether ;
307+ currentPriceData = abi.encode (price);
308+
309+ vm.expectEmit ();
310+ emit LiquidatedPosition (USER_1, posId, price, liqPriceWithoutPenaltyNorFunding);
311+ (ValidateOpenPositionData memory data , bool liquidated ) =
312+ protocol.i_prepareValidateOpenPositionData (pendingAction, currentPriceData);
313+
314+ /* ------------------------------ sanity checks ----------------------------- */
315+ assertGt (
316+ data.liqPriceWithoutPenaltyNorFunding,
317+ liqPriceWithoutPenalty,
318+ "liqPriceWithoutPenaltyNorFunding should be higher than liqPriceWithoutPenalty "
319+ );
320+ assertLt (data.liqPriceWithoutPenalty, price, "liqPriceWithoutPenalty should be less than the current price " );
321+ assertGt (
322+ data.liqPriceWithoutPenaltyNorFunding,
323+ price,
324+ "liqPriceWithoutPenaltyNorFunding should be higher than the current price "
325+ );
326+
327+ /* ------------------------ checking returned values ------------------------ */
328+ assertTrue (liquidated, "The position should have been liquidated " );
329+ assertFalse (data.isLiquidationPending, "There should not be any pending liquidations " );
330+ }
331+
262332 /// @notice Assert the data in ValidateOpenPositionData depending on `isEarlyReturn`
263333 function _assertData (ValidateOpenPositionData memory data , bool isEarlyReturn ) private view {
264334 uint128 currentPrice = abi.decode (currentPriceData, (uint128 ));
265335 uint24 liquidationPenalty = protocol.getLiquidationPenalty ();
266- uint256 positionTotalExpo =
267- protocol.i_calcPositionTotalExpo (POSITION_AMOUNT, params.initialPrice, liqPriceWithoutPenalty);
336+ uint256 positionTotalExpo = protocol.i_calcPositionTotalExpo (
337+ POSITION_AMOUNT, params.initialPrice, data.liqPriceWithoutPenaltyNorFunding
338+ );
268339 uint128 liqPriceWithoutPenaltyNorFunding = protocol.i_getEffectivePriceForTick (
269340 protocol.i_calcTickWithoutPenalty (data.action.tick, data.liquidationPenalty), data.action.liqMultiplier
270341 );
0 commit comments