Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: low
Invalid

Unintended Profit Elimination on Auto-Deleverage Trigger and Panic Error 0x11

Summary


If marketDebtRatio is equals to the autoDeleverageStartThresholdx18 then adjusted factor will be zero.

Vulnerability Details

function getAdjustedProfitForMarketId in creditDelegationBranch calls market.getAutoDeleverageFactor() in this function deleverage calculated based on the market debt ratio and delegated credit .

Function getAdjustedProfitForMarketId() calls market.getAutoDeleverageFactor()

// we don't need to add `profitUsd` as it's assumed to be part of the total debt
// NOTE: If we don't return the adjusted profit in this if branch, we assume marketTotalDebtUsdX18 is positive
if (market.isAutoDeleverageTriggered(delegatedCreditUsdX18, marketTotalDebtUsdX18)) {
// if the market's auto deleverage system is triggered, it assumes marketTotalDebtUsdX18 > 0
adjustedProfitUsdX18 =
market.getAutoDeleverageFactor(delegatedCreditUsdX18, marketTotalDebtUsdX18).mul(adjustedProfitUsdX18);
}

In function getAutoDeleverageFactor()

function getAutoDeleverageFactor(
Data storage self,
UD60x18 delegatedCreditUsdX18,
SD59x18 totalDebtUsdX18
)
internal
view
returns (UD60x18 autoDeleverageFactorX18)
{
SD59x18 sdDelegatedCreditUsdX18 = delegatedCreditUsdX18.intoSD59x18();
if (sdDelegatedCreditUsdX18.lte(totalDebtUsdX18) || sdDelegatedCreditUsdX18.isZero()) {
return UD60x18_UNIT;
}
// calculates the market ratio
UD60x18 marketDebtRatio = totalDebtUsdX18.div(sdDelegatedCreditUsdX18).intoUD60x18();
// cache the auto deleverage parameters as UD60x18
UD60x18 autoDeleverageStartThresholdX18 = ud60x18(self.autoDeleverageStartThreshold);
UD60x18 autoDeleverageEndThresholdX18 = ud60x18(self.autoDeleverageEndThreshold);
UD60x18 autoDeleverageExponentZX18 = ud60x18(self.autoDeleverageExponentZ);
// first, calculate the unscaled delevarage factor
UD60x18 unscaledDeleverageFactor = Math.min(marketDebtRatio, autoDeleverageEndThresholdX18).sub(
autoDeleverageStartThresholdX18
).div(autoDeleverageEndThresholdX18.sub(autoDeleverageStartThresholdX18));
// finally, raise to the power scale
autoDeleverageFactorX18 = unscaledDeleverageFactor.pow(autoDeleverageExponentZX18);
}

scenarion like

Parameter Value
Total Delegated Credit $1,000,000$
Total Market Debt $700,000$
Auto-Deleverage Start Threshold 0.75
Auto-Deleverage End Threshold 0.95

market debt Ratio = 700000 / 1000000 = 0.75

Unscaled Deleverage Factor=0.95−0.75 min(0.75,0.95)−0.75 = 0

Ajustedprofitx18 = someValue * 0 = 0

Impact
If the market debt ratio is lesser than the autoDeleverageEndThyresholdx18 then adjusted profit will be always zero after exponentiation which loss to the market which are connected.

Panic underflow/overflow error CODE: 0x11

POC

Add this code in test/integration/market-making/credit-branch/delegation-/getAdjustedProfitForMarketId/getAdjustedProfitForMarketId.t.sol

function test_get_auto_deleverage() public {
UD60x18 marketDebtRatioUsdX18 = ud60x18(2e18); // Set to the same as autoDeleverageStartThreshold
UD60x18 autoDeleverageStartThreshold = ud60x18(2e18); // Use ud60x18 for conversion
UD60x18 autoDeleverageEndThreshold = ud60x18(4e18); // Use ud60x18 for conversion
UD60x18 result = (ud60x18(marketDebtRatioUsdX18.intoUint256()) - autoDeleverageStartThreshold) /
(autoDeleverageEndThreshold - autoDeleverageStartThreshold);
UD60x18 expected = ud60x18(0); // Use ud60x18 for conversion
// Ensure assertEq is compatible with UD60x18
assertEq(result.intoUint256(), expected.intoUint256());
}

Recommedation

  1. Add Different logic to calculate and handle zero like auto deleverage to not apply zero leverage in cases where the market ratio is below the threshold.

  2. Add Lower bound for deleveraged Factor like Math.max(deleveragedFactor , ConstantMiniValue). So that some profit remains.

  3. In function isAutoDeleverageTriggered() make change to gte() to gt() trigger only happens at greater than start thershold.

    // trigger ADL if marketRatio > ADL start threshold
    triggered = marketDebtRatio.gt(ud60x18(self.autoDeleverageStartThreshold));

Updates

Lead Judging Commences

inallhonesty Lead Judge
6 months ago
inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement
Assigned finding tags:

[INVALID] The equality case in `CreditDelegationBranch::isAutoDeleverageTriggered` (market ratio is == the start threshold) causes a small dos.

Support

FAQs

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