DittoETH

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

Margin callers can drain the TAPP during liquidation by willingly increase gas costs with the shortHintArray

Summary

During primary liquidation the TAPP (Treasury Asset Protection Pool) pays the gas costs of force bids, so that margin callers are even motivated to liquidate shorters, if gas costs are high. To liquidate a shortRecord margin, callers must provide a parameter called shortHintArray to the function call. The purpose of this array is to save gas, it should contain id hints where the protocol should look for shorts in the order book which are currently above the oracle price, since users can’t match against shorts under the oracle price. As the protocol loops through this shortHintArray, an array with wrong hints could increase gas and as the length of the array is never checked, it could even increase the gas costs to an amount that would fully drain the TAPP. As the TAPP is an important security mechanism of the protocol, draining the funds of it could lead to a shutdown of the market and therefore to a big loss of user funds.

Vulnerability Details

The liquidate function takes the shortHintArray as parameter:

function liquidate(
address asset,
address shorter,
uint8 id,
uint16[] memory shortHintArray
)
external
isNotFrozen(asset)
nonReentrant
onlyValidShortRecord(asset, shorter, id)
returns (uint88, uint88)
{
...
}

This array is then used to create a forceBid:

(m.ethFilled, ercAmountLeft) = IDiamond(payable(address(this))).createForcedBid(
address(this), m.asset, _bidPrice, m.short.ercDebt, shortHintArray
);

And during these process, the protocol loops over this array:

function _updateOracleAndStartingShort(address asset, uint16[] memory shortHintArray)
private
{
...
uint16 shortHintId;
for (uint256 i = 0; i < shortHintArray.length;) {
shortHintId = shortHintArray[i];
unchecked {
++i;
}
{
O shortOrderType = s.shorts[asset][shortHintId].orderType;
if (
shortOrderType == O.Cancelled || shortOrderType == O.Matched
|| shortOrderType == O.Uninitialized
) {
continue;
}
}
...
}

In the end, the TAPP pays for the gas costs in the _marginFeeHandler function:

function _marginFeeHandler(MTypes.MarginCallPrimary memory m) private {
STypes.VaultUser storage VaultUser = s.vaultUser[m.vault][msg.sender];
STypes.VaultUser storage TAPP = s.vaultUser[m.vault][address(this)];
// distribute fees to TAPP and caller
uint88 tappFee = m.ethFilled.mulU88(m.tappFeePct);
uint88 callerFee = m.ethFilled.mulU88(m.callerFeePct) + m.gasFee;
m.totalFee += tappFee + callerFee;
//@dev TAPP already received the gasFee for being the forcedBid caller. tappFee nets out.
if (TAPP.ethEscrowed >= callerFee) {
TAPP.ethEscrowed -= callerFee;
VaultUser.ethEscrowed += callerFee;
} else {
// Give caller (portion of?) tappFee instead of gasFee
VaultUser.ethEscrowed += callerFee - m.gasFee + tappFee;
m.totalFee -= m.gasFee;
TAPP.ethEscrowed -= m.totalFee;
}
}

Therefore, if the user provides a big shortHintArray with wrong hints the gas costs will drastically increase to a point which drains the funds of the TAPP.

Impact

As the TAPP does no longer has enough funds to pay for liquidation, if shortRecords are under collateralized. A lot of problems like the increment of the ercDebtRate and the shutdown of the market can occur. This leads to a big loss of user funds.

Tools Used

Manual Review

Recommendations

Check the length of the shortHintArray.

Updates

Lead Judging Commences

0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-240

cosine Submitter
almost 2 years ago
0xnevi Lead Judge
almost 2 years ago
0xnevi Lead Judge almost 2 years ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-240

Support

FAQs

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