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

Charging Only Maintenance Margin Allows Users to Escape Losses by Getting Liquidated

Summary

The wrong amount is deducted from users' accounts upon liquidation. Because of this, users can escape large amounts of losses at the expense of other parties.

Vulnerability Details

A position is liquidated when its collateral value is less than the maintenance margin. The collateral value consists of the LTV balance of the collateral in the user's account, as well as the PnL and relevant fees.

When an account is liquidated, the user's positions are closed, and a portion of the collateral the user owns is deducted from their account.

The issue with the current design is that the amount deducted is always the maintenance margin, which in most cases would be less than the actual amount of loss the user incurred. This means that a user could save money by being liquidated instead of closing their position. While this might benefit traders, it puts all other parties at a severe disadvantage.

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

Take, for example:

Alice provides 200 USDC as collateral and opens a long position in the IMX market with a size of 1000. The index is currently $1.00.

Initial State:

  • Collateral LTV: 250 + 0 = 250

  • PnL: 0

  • Size: 1000

  • Index Price: 1.00

  • MM: 100

Then the price of the index goes down by 0.16 cents.

After Price Drop:

  • Collateral LTV: 250 + (-160) = 90

  • PnL: -160

  • Size: 1000

  • Index Price: 0.86

  • MM: 100

The position's collateral value is below the MM because it has incurred a loss of 160. This means that the position can be liquidated.

When being liquidated, the requiredMaintenanceMarginUsdX18 is used as the pnlUsdX18 in the deductAccountMargin function. Since the MM was only 100, only 100 dollars will be deducted (not counting fees).

It is important to note that Alice had a loss of 160. This means that Alice actually saved 60 dollars by being liquidated (160 - 100 = 60).

This issue is rated as high because by using the smaller maintenance margin as the deduction value, users will not pay for all of their losses. Users will often opt to be liquidated instead of closing their position, worsening the overall health of the protocol, significantly reducing yield for other parties, and leaving unresolved debt.

Impact

Users will not need to repay all their losses, leaving unresolved debt.

Tools Used

Manual analysis.

Recommendations

Amend the liquidation amount to take the greater of either requiredMaintenanceMarginUsdX18 or accountTotalUnrealizedPnlUsdX18 when accountTotalUnrealizedPnlUsdX18 is negative. Similar to the following:

if(accountTotalUnrealizedPnlUsdX18 < 0 && accountTotalUnrealizedPnlUsdX18.abs() > requiredMaintenanceMarginUsdX18) {
amountToDeduct = accountTotalUnrealizedPnlUsdX18;
} else {
amountToDeduct = requiredMaintenanceMarginUsdX18;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year 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.