DittoETH

Ditto
DeFiFoundryOracle
55,000 USDC
View results
Submission Details
Severity: high
Invalid

Flagger's are not incentivised to flag/liquidate shorts

Vulnerability Details

After a flag, firstLiquidationTime amount of time (initially set to 10hrs) has to pass before a liquidation can be made.

function _canLiquidate(MTypes.MarginCallPrimary memory m)
private
view
returns (bool)
{
// more code
uint256 timeDiff = LibOrders.getOffsetTimeHours() - m.short.updatedAt;
uint256 resetLiquidationTime = LibAsset.resetLiquidationTime(m.asset);
// @audit checks firstLiquidationTime has passed
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;
}
}
}

https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/MarginCallPrimaryFacet.sol#L351-L408

Within the 10hr period, there are multiple ways the collateralRatio could increase above primaryLiquidationCR. The shorter can add more collateral, combine with other shorts having high collateralRatio or the price of collateral could increase. The ability of shorter's to reliably increase the collateral ratio within 10hrs could deter most of the flaggers as they would have no rewards for the gas spent on calling the flagShort function.

function increaseCollateral(address asset, uint8 id, uint88 amount)
external
isNotFrozen(asset)
nonReentrant
onlyValidShortRecord(asset, msg.sender, id)
{
// more code
STypes.ShortRecord storage short = s.shortRecords[asset][msg.sender][id];
short.updateErcDebt(asset);
uint256 yield = short.collateral.mul(short.zethYieldRate);
short.collateral += amount;
uint256 cRatio = short.getCollateralRatio(asset);
if (cRatio >= Constants.CRATIO_MAX) revert Errors.CollateralHigherThanMax();
//@dev reset flag info if new cratio is above primaryLiquidationCR
if (cRatio >= LibAsset.primaryLiquidationCR(asset)) {
short.resetFlag(); // @audit resets flag
}
// more code
}

https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/ShortRecordFacet.sol#L38-L60

function combineShorts(address asset, uint8[] memory ids)
external
isNotFrozen(asset)
nonReentrant
onlyValidShortRecord(asset, msg.sender, ids[0])
{
// more code
if (c.shortFlagExists) {
if (
firstShort.getCollateralRatioSpotPrice(
LibOracle.getSavedOrSpotOraclePrice(_asset)
) < LibAsset.primaryLiquidationCR(_asset)
) revert Errors.InsufficientCollateral();
// Resulting combined short has sufficient c-ratio to remove flag
firstShort.resetFlag(); // @audit resets flag
}
emit Events.CombineShorts(asset, msg.sender, ids);
}

https://github.com/Cyfrin/2023-09-ditto/blob/a93b4276420a092913f43169a353a6198d3c21b9/contracts/facets/ShortRecordFacet.sol#L117-L189

The above method of flagging before liquidation has to be followed until the collateral ratio reaches secondaryLiquidationCR (initially set to 150%). But since secondary liquidation offers neither any fees nor any gas refunds to the margin caller, the liquidation is again not incentivized.

Impact

The system could end up with debt having not enough underlying collateral, leading to loss of value of the pegged asset.

Recommendations

Offer gas reimbursements to a flagger on flagging.

Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: User experience and design improvement

Support

FAQs

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