DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: medium
Valid

The liquidation action won't be available in the last hour specified in the `resetLiquidationTime()` time frame

Summary

The liquidation action won't be available when the current time is in the last hour of the resetLiquidationTime() time frame. Causing that the time frame for liquidation will be less than expected by the resetLiquidationTime().

Vulnerability Details

The MarginCallPrimaryFacet::_canLiquidate() function helps to determinate if the short is able to be liquidated or not. If no one has liquidated during 16 hours (resetLiquidationTime()) the flag will be reset and the flagging process begins anew. As the documentation says:

* @dev Shorter has 10 hours after initial flag to bring cRatio up above maintainence margin...
* @dev ...After that, the flagger has 2 hours to liquidate the shorter. If short is not liquidated by shorter within that time, ANYBODY can then liquidate...
* @dev ...After 16 total hours have passed and the short has not been liquidated, the flag gets reset and the flagging process begins anew

The problem is that the short can't be liquidated exactly at the hour 16 because in the code line 387 the if statement uses a >= so the code line 395 timeDiff <= resetLiquidationTime is not possible to be executed:

File: MarginCallPrimaryFacet.sol
383:
384: uint256 timeDiff = LibOrders.getOffsetTimeHours() - m.short.updatedAt;
385: uint256 resetLiquidationTime = LibAsset.resetLiquidationTime(m.asset);
386:
387: if (timeDiff >= resetLiquidationTime) {
388: return false;
389: } else {
390: uint256 secondLiquidationTime = LibAsset.secondLiquidationTime(m.asset);
391: bool isBetweenFirstAndSecondLiquidationTime = timeDiff
392: > LibAsset.firstLiquidationTime(m.asset) && timeDiff <= secondLiquidationTime
393: && s.flagMapping[m.short.flaggerId] == msg.sender;
394: bool isBetweenSecondAndResetLiquidationTime =
395: timeDiff > secondLiquidationTime && timeDiff <= resetLiquidationTime;
396: if (
397: !(
398: (isBetweenFirstAndSecondLiquidationTime)
399: || (isBetweenSecondAndResetLiquidationTime)
400: )
401: ) {
402: revert Errors.MarginCallIneligibleWindow();
403: }
404:
405: return true;
406: }

Impact

The liquidaton won't be possible at the last hour specified in the resetLiquidationTime() time frame. The short should possible to be liquidated between the time frame from hour 11 to hour 16. Liquidators have less time to be able liquidate shorts.

Tools used

Manual review

Recommendations

Change the next line so liquidation is possible in the last hour of the resetLiquidationTime() time frame:

function _canLiquidate(MTypes.MarginCallPrimary memory m)
private
view
returns (bool)
{
...
...
uint256 timeDiff = LibOrders.getOffsetTimeHours() - m.short.updatedAt;
uint256 resetLiquidationTime = LibAsset.resetLiquidationTime(m.asset);
-- if (timeDiff >= resetLiquidationTime) {
++ if (timeDiff > resetLiquidationTime) {
return false;
} else {
uint256 secondLiquidationTime = LibAsset.secondLiquidationTime(m.asset);
bool isBetweenFirstAndSecondLiquidationTime = timeDiff
> LibAsset.firstLiquidationTime(m.asset) && timeDiff <= secondLiquidationTime
&& s.flagMapping[m.short.flaggerId] == msg.sender;
bool isBetweenSecondAndResetLiquidationTime =
timeDiff > secondLiquidationTime && timeDiff <= resetLiquidationTime;
if (
!(
(isBetweenFirstAndSecondLiquidationTime)
|| (isBetweenSecondAndResetLiquidationTime)
)
) {
revert Errors.MarginCallIneligibleWindow();
}
return true;
}
}
Updates

Lead Judging Commences

0xnevi Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-569

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.