DittoETH

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

Griefing Yield of the user's short record if it goes to primary liquidation and can be liquidated immediately

Summary

A liquidator can grieve the short record's yield if the short record goes to primary liquidation by calling flagShort() and then doing primary liquidation.

Vulnerability Details

The issue arises when the collateralization ratio (cRatio) of a short record reaches a certain threshold, making it eligible for immediate liquidation without the need for flagging. This condition is determined by the following code snippet from _canLiquidate():

if (m.cRatio < m.minimumCR) return true;

In this situation, a liquidator can proceed with the immediate liquidation of the short record. However, if the liquidator decides to call flagShort() before initiating the liquidation, all of the yield that should rightfully belong to the short position holder ends up being directed to the TAPP (Vault). This unintended consequence allows a malicious liquidator to disrupt the yield distribution and deprive the legitimate short position holder of their earnings.

The reason is that the liquidation calls disburseCollateral, which distributes yield to the short record owner. When a short record is liquidated, the yield is supposed to be distributed to the short record owner. However, if the short record is flagged, the yield is instead diverted to the TAPP (Vault) due to the flagging process updating the short.updatedAt variable.

Proof of Concept

The following test with comments simulate the certain malicious behavior.

function test_primaryLiquidationNoNeedToFlagButFlagToGrief() public {
fundLimitBidOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, receiver);
fundLimitShortOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, sender);
skip(10 hours);
generateYield(1 ether);
//@dev give tapp eth to avoid black swan
assert(diamond.getYield(asset, sender) > 0); // We see that the user has claim for YIELD
// returns 900000000000000000
depositEth(tapp, 100 ether);
uint vaultBalanceBeforeLiq = diamond.getZethBalance(1, tapp);
//@dev set cRatio below 1.1
setETH(700 ether);
fundLimitAskOpt(DEFAULT_PRICE, DEFAULT_AMOUNT, extra);
diamond.flagShort(asset, sender, Constants.SHORT_STARTING_ID, Constants.HEAD);
diamond.liquidate(
asset, sender, Constants.SHORT_STARTING_ID, shortHintArrayStorage
);
uint vaultBalanceAfterLiq = diamond.getZethBalance(1, tapp);
assert(vaultBalanceBeforeLiq < vaultBalanceAfterLiq);
// 105994928604000000000 -- with flagShort
// 105094928604000000000 -- without flagShort
// Difference = 900000000000000000 - that is the Yield a shorter(sender) should get but if the attacker flag it - Yield goes to TAPP
}

Impact

The primary impact of this vulnerability is the potential griefing of the legitimate short position holder's yield. When a short record is liquidated in this manner, the yield that should rightfully belong to the short position holder is rerouted to the TAPP (Vault), effectively depriving the user of their earnings.

Tools Used

Manual review

Recommendations

To mitigate this vulnerability, it is recommended to prevent the flagging of a short record if it is eligible for immediate liquidation. This can be achieved by implementing a check similar to the following within the flagShort() function:

As something like this:

function flagShort(address asset, address shorter, uint8 id, uint16 flaggerHint)
external
isNotFrozen(asset)
nonReentrant
onlyValidShortRecord(asset, shorter, id)
{
STypes.ShortRecord storage short = s.shortRecords[asset][shorter][id];
+ if (m.cRatio < m.minimumCR) {
+ revert Errors.CRTooLowCanLiquidateImmediately}
...
Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Other
talfao Submitter
almost 2 years ago
0xnevi Lead Judge
almost 2 years ago
talfao Submitter
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

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