Part 2

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

During _fillOrder with positive PnL, usdToken is minted less than trade account deposit amount

Summary

During SettlementBranch._fillOrder, profit adjustment is applied to positive PnL twice. This will lead market to loss funds gradually.

Vulnerability Details

In SettlementBranch._fillOrder, there is a usdToken deposit logic if user's PnL is positive

if (ctx.pnlUsdX18.gt(SD59x18_ZERO)) {
IMarketMakingEngine marketMakingEngine = IMarketMakingEngine(perpsEngineConfiguration.marketMakingEngine);
ctx.marginToAddX18 =
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()); // @audit profit adjusted twice
}

User's pnl is adjusted considering the market's ADL, like the following logic:

adjustedProfit = PnL * autoDeleverageFactor

autoDeleverageFactor is lower than one if market's debt ratio is between autoDeleverageStartThreshold and autoDeleverageEndThreshold.

So back to the codebase, ctx.marginToAddX18 = ctx.pnlUsdX18 * autoDeleverageFactor

And this amount is deposited to user's trading account.

We expect to withdraw the same amount marginToAddX18from the market making engine.

However, profit adjustment is done again in marketMakingEngine.withdrawUsdTokenFromMarket:

if (market.isAutoDeleverageTriggered(delegatedCreditUsdX18, marketTotalDebtUsdX18)) {
// if the market is in the ADL state, it reduces the requested USD
// Token amount by multiplying it by the ADL factor, which must be < 1
UD60x18 adjustedUsdTokenToMintX18 =
market.getAutoDeleverageFactor(delegatedCreditUsdX18, marketTotalDebtUsdX18).mul(amountX18);
amountToMint = adjustedUsdTokenToMintX18.intoUint256();
market.updateNetUsdTokenIssuance(adjustedUsdTokenToMintX18.intoSD59x18());
} else {
// if the market is not in the ADL state, it realizes the full requested USD Token amount
market.updateNetUsdTokenIssuance(amountX18.intoSD59x18());
}

Thus, final usdToken minted amount is ctx.marginToAddX18 * autoDeleverageFactor, which is lower than amount deposited to trading account (ctx.marginToAddX18).

Impact

  • Users will receive less USDz than expected

  • TradingAccount's deposit amount will be greater than user's USDz balance, which will lead to protocol insolvency and unexpected behavior

Tools Used

Manual Review

Recommendations

Should be fixed like the following:

- marketMakingEngine.withdrawUsdTokenFromMarket(marketId, ctx.marginToAddX18.intoUint256());
+ marketMakingEngine.withdrawUsdTokenFromMarket(marketId, ctx.pnlUsdX18.intoUint256());
Updates

Lead Judging Commences

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

`SettlementBranch._fillOrder` profit adjustment is applied to positive PnL twice.

Support

FAQs

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