Part 2

Zaros
PerpetualsDEXFoundrySolidity
70,000 USDC
View results
Submission Details
Severity: high
Valid

Incorrect `AutoDeleverageFactor`.

Summary

The calculation of AutoDeleverageFactor is incorrect.

Vulnerability Details

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/market-making/leaves/Market.sol#L174

function getAutoDeleverageFactor(
...
) ...
{ ...
UD60x18 unscaledDeleverageFactor = Math.min(marketDebtRatio, autoDeleverageEndThresholdX18).sub(
autoDeleverageStartThresholdX18
).div(autoDeleverageEndThresholdX18.sub(autoDeleverageStartThresholdX18));
// finally, raise to the power scale
174: autoDeleverageFactorX18 = unscaledDeleverageFactor.pow(autoDeleverageExponentZX18);
}

As we can see, when marketRatio >= ADL start threshold, the smaller debt, the smaller autoDeleverageFactor.
For example:
When marketDebtRatio = autoDeleverageStartThresholdX18, the calcuatated AutoDeleverageFactor is zero.
When marketDebtRatio = autoDeleverageEndThresholdX18, the calcuatated AutoDeleverageFactor is one.

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/market-making/branches/CreditDelegationBranch.sol#L159

function getAdjustedProfitForMarketId(
...
if (market.isAutoDeleverageTriggered(delegatedCreditUsdX18, marketTotalDebtUsdX18)) {
// if the market's auto deleverage system is triggered, it assumes marketTotalDebtUsdX18 > 0
adjustedProfitUsdX18 =
159: market.getAutoDeleverageFactor(delegatedCreditUsdX18, marketTotalDebtUsdX18).mul(adjustedProfitUsdX18);
}

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/perpetuals/branches/SettlementBranch.sol#L499

if (ctx.pnlUsdX18.gt(SD59x18_ZERO)) {
IMarketMakingEngine marketMakingEngine = IMarketMakingEngine(perpsEngineConfiguration.marketMakingEngine);
ctx.marginToAddX18 =
499: marketMakingEngine.getAdjustedProfitForMarketId(marketId, ctx.pnlUsdX18.intoUD60x18().intoUint256());
tradingAccount.deposit(perpsEngineConfiguration.usdToken, ctx.marginToAddX18);
// mint settlement tokens credited to trader; tokens are minted to
// address(this) since they have been credited to the trader's margin
marketMakingEngine.withdrawUsdTokenFromMarket(marketId, ctx.marginToAddX18.intoUint256());
}

As a result, Traders receive their full pnls when marketDebtRatio = autoDeleverageEndThresholdX18,but none when marketDebtRatio = autoDeleverageStartThresholdX18.

Impact

Traders do not receive their PnLs at all when the state enters the ADL state.

Recommendations

https://github.com/Cyfrin/2025-01-zaros-part-2/blob/main/src/market-making/leaves/Market.sol#L174

function getAutoDeleverageFactor(
...
) ...
{ ...
UD60x18 unscaledDeleverageFactor = Math.min(marketDebtRatio, autoDeleverageEndThresholdX18).sub(
autoDeleverageStartThresholdX18
).div(autoDeleverageEndThresholdX18.sub(autoDeleverageStartThresholdX18));
// finally, raise to the power scale
-174: autoDeleverageFactorX18 = unscaledDeleverageFactor.pow(autoDeleverageExponentZX18);
+174: autoDeleverageFactorX18 = UD60x18_UNIT.sub(unscaledDeleverageFactor).pow(autoDeleverageExponentZX18);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Validated
Assigned finding tags:

getAutoDeleverageFactor inverts the ADL behavior causing wrong profit reductions when market enters deleveraging state

Support

FAQs

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

Give us feedback!