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

Liquidation Fee is Not Included in Liquidation Check, Leading to Insolvent Positions

Summary

The isLiquidatable check compares a user's margin balance to the required maintenance margin based on the market they are in and their position size. The check does not take into consideration the liquidationFeeUsdX18, which means that all liquidations would be late. In an environment where you can leverage up to 100x, a late liquidation can quickly mean liquidating an insolvent position.

Vulnerability Details

The isLiquidatable check effectively includes almost all needed variables to determine if a position is liquidatable, including the funding fee, PnL, and margin across all collateral types. However, it is missing one crucial factor: the liquidationFeeUsdX18.

function isLiquidatable(
UD60x18 requiredMaintenanceMarginUsdX18,
SD59x18 marginBalanceUsdX18
)
internal
pure
returns (bool)
{ //@audit missing liquidationFee
return requiredMaintenanceMarginUsdX18.intoSD59x18().gt(marginBalanceUsdX18);
}

This fee is paid to keepers to ensure a liquidation is profitable. When a keeper liquidates a position, part of that collateral is transferred to the keeper as a fee for performing the liquidation.

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

Because the liquidation fee is not removed from the marginBalanceUsdX18, the isLiquidatable function will fail until the user's true margin balance is below the maintenance margin by an amount equal to or more than the liquidation fee.

For example:

  • Alice opens a position with a size of 1000 and the asset is $1.

  • Alice has 100 USDC as collateral.

  • Alice's maintenance margin is $91.

  • The price drops to $0.99 (1% * 1000 = 10).

  • Alice's collateral balance (10 loss) is $90.

  • Alice is now able to be liquidated.

  • Upon liquidation, Alice has to pay both the liquidation fee (say 15) and the maintenance maring (91)

First, the liquidation fee is paid:

  • 15 = $85.

Then, the maintenance margin is paid:

  • 91 = -$6.

At the time Alice was liquidated, she was insolvent by $6.

With a high likelihood and medium impact, rating this as a high severity issue makes the most sense. The high likelihood comes from the liquidation fee never being considered when determining liquidation status, leading to every liquidation being a late liquidation. The medium impact comes from the fact that positions will be able to remain open while being insolvent, jeopardizing the health of the protocol.

Impact

  • Insolvent positions will remain open.

  • All liquidations will be late.

Tools Used

  • Manual analysis.

Recommendations

Modify the isLiquidatable function to include the liquidation fee as shown below:

function isLiquidatable(
UD60x18 requiredMaintenanceMarginUsdX18,
SD59x18 marginBalanceUsdX18,
UD60x18 liquidationFeeUsdX18
)
internal
pure
returns (bool)
{
return requiredMaintenanceMarginUsdX18.intoSD59x18().gt(marginBalanceUsdX18 - liquidationFeeUsdX18);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Liquidation doesn't take the liquidation fee in consideration inside the isLiquidatable check

Support

FAQs

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