DeFiFoundry
60,000 USDC
View results
Submission Details
Severity: high
Valid

Liquidation doesn't recover loss from trader

Summary

In LiquidationBranch, in function liquidateAccounts there is a critical flaw in the liquidation process where the protocol recovers only the requiredMaintenanceMarginUsdX18 and not the full loss incurred by the tradingAccount.

Vulnerability Details

Example Scenario

  1. Trader's Position:

    • Initial collateral: $10,000.

    • Position value: $50,000 (leveraged position 5x).

    • Maintenance margin requirement: 5% of position value

  2. Adverse Market Movement:

    • The position incurs a loss of $7905.

    • The current collateral value drops to $10,000 - $7905 = $2095.

    • Maintenance margin requirement: (50000 - 7905) * 5% = $2104.75

    • Margin balance becomes negative, triggering liquidation since it is below the maintenance margin requirement.

The protocol only deducts requiredMaintenanceMarginUsdX18and not the loses by the trader. Due to this, trader would still have lot of collateral unaffected even with liquidation.

https://github.com/Cyfrin/2024-07-zaros/blob/d687fe96bb7ace8652778797052a38763fbcbb1b/src/perpetuals/branches/LiquidationBranch.sol#L152-L161

ctx.liquidatedCollateralUsdX18 = tradingAccount.deductAccountMargin({
feeRecipients: FeeRecipients.Data({
marginCollateralRecipient: globalConfiguration.marginCollateralRecipient,
orderFeeRecipient: address(0),
settlementFeeRecipient: globalConfiguration.liquidationFeeRecipient
}),
pnlUsdX18: requiredMaintenanceMarginUsdX18,
orderFeeUsdX18: UD60x18_ZERO,
settlementFeeUsdX18: ctx.liquidationFeeUsdX18
});

Impact

This leads to a scenario where a trader retains most of their collateral even after liquidation. Hence, due to liquidation, only small part of user's collateral will be seized while all losing positions of trader will be closed.

Tools Used

Manual analysis

Recommendations

The liquidation process should be updated to recover both the required maintenance margin and the actual trading losses incurred by the trader.

// Calculate the total amount to recover, including maintenance margin and actual losses
SD59x18 totalRecoveryAmountUsdX18 = actualLossesUsdX18;
ctx.liquidatedCollateralUsdX18 = tradingAccount.deductAccountMargin({
feeRecipients: FeeRecipients.Data({
marginCollateralRecipient: globalConfiguration.marginCollateralRecipient,
orderFeeRecipient: address(0),
settlementFeeRecipient: globalConfiguration.liquidationFeeRecipient
}),
pnlUsdX18: totalRecoveryAmountUsdX18, // Recover both maintenance margin and actual losses
orderFeeUsdX18: UD60x18_ZERO, // Ensure order fee is not impacting the recovery
settlementFeeUsdX18: ctx.liquidationFeeUsdX18
});
Updates

Lead Judging Commences

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

deductAccountMargin() treats `required maintenance margin` as the `unrealized PnL` because it uses `requiredMaintenanceMarginUsdX18` in place of `pnlUsdX18`

Support

FAQs

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