Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

Borrower is Forced to Pay Full Debt Despite Improved Health Factor During Volatile Market Conditions*

Summary

In the liquidation closure mechanism, the protocol forces borrowers to reduce their outstanding debt to an almost negligible “dust” level before they can exit liquidation. This rigidity means that even if a borrower makes a significant partial repayment thereby improving their collateral to debt ratio their debt must still drop below a very low threshold before liquidation is closed. In volatile market conditions, where collateral values can temporarily dip and then recover, this mechanism can force borrowers to pay off nearly their entire debt or face liquidation, even if their overall health factor has subsequently improved to safe levels.


Vulnerability Details

  • How It Should Work:
    In common system, a borrower whose risk profile has improved (i.e., a better collateral-to-debt ratio) should be able to exit liquidation once their health factor recovers above a safe threshold. Partial repayments that significantly lower the risk of default should allow the borrower to reclaim their collateral without having to clear nearly 100% of the outstanding debt.

  • Current Implementation Flaw:
    The liquidation closure logic is extremely rigid. The check:

    if (userDebt > DUST_THRESHOLD) revert DebtNotZero();

    requires that the borrower’s outstanding debt be reduced below a predetermined “dust” threshold. This means that even if the borrower’s collateral value recovers and their health factor improves they remain locked in liquidation until nearly all their debt is repaid.

  • Why This is Problematic in Volatile Markets:
    In periods of high market volatility, collateral values can fluctuate rapidly. An attacker could trigger liquidation during a temporary dip, forcing the borrower into liquidation. Even if the market later recovers and the borrower’s health factor improves, they remain trapped because their debt is still above the dust threshold. If the borrower lacks sufficient liquidity to clear the nearly full debt, they face a forced liquidation despite being, in a stabilized market, adequately collateralized.


Impact

  1. Unfair Penalization:
    Borrowers who work to improve their position through partial repayments remain locked in liquidation, as the protocol’s rigid debt threshold prevents them from exiting until their debt is almost entirely cleared.

  2. Forced Liquidations in Rebound Markets:
    In volatile markets, temporary dips may allow an attacker to trigger liquidation. Even if the market rebounds quickly and the borrower’s collateral recovers, the borrower is forced to repay nearly all their debt or risk losing their collateral, despite a healthy underlying position.


Tools Used

  • Manual Code Review: Analyzing the liquidation closure logic in the protocol.

  • Scenario Simulation: Constructing scenarios to illustrate how a borrower with an improved health factor remains in liquidation under volatile market conditions.

  • Comparative Analysis: Reviewing industry best practices for liquidation mechanisms to identify how more flexible, health factor-based exits can prevent forced liquidations.


Recommendations

  1. Implement a Health Factor-Based Closure Condition:
    Instead of solely checking that the remaining debt is below a dust threshold, allow liquidation closure when the borrower’s health factor recovers above a safe threshold:

    function closeLiquidation() external nonReentrant whenNotPaused {
    - uint256 userDebt = user.scaledDebtBalance.rayMul(reserve.usageIndex);
    - if (userDebt > DUST_THRESHOLD) revert DebtNotZero();
    + uint256 healthFactor = calculateHealthFactor(userAddress);
    + if (healthFactor < safeThreshold) revert HealthFactorTooLow();
    }
Updates

Lead Judging Commences

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

LendingPool::finalizeLiquidation() never checks if debt is still unhealthy

Support

FAQs

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