Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

[H-01] Strict health factor improvement check makes liquidation revert for all positions below 110% collateral ratio, creating permanent bad debt

Description

liquidate() requires ending_user_health_factor > starting_user_health_factor (strict greater-than). Combined with the 10% LIQUIDATION_BONUS, a mathematical impossibility arises when the collateral-to-debt ratio falls below 110%.

Vulnerability Details

self._redeem_collateral(
collateral,
token_amount_from_debt_covered + bonus_collateral, # @> 110% of debt value taken
user,
msg.sender,
)
self._burn_dsc(debt_to_cover, user, msg.sender)
ending_user_health_factor: uint256 = self._health_factor(user)
assert (
ending_user_health_factor > starting_user_health_factor # @> strict inequality
), "DSCEngine__HealthFactorNotImproved"

For any partial liquidation amount d against a position with collateral value C and debt D:

  • Post-liquidation ratio: (C - 1.1d) / (D - d)

  • For HF to improve: (C - 1.1d) / (D - d) > C / D

  • Simplifying: C * d > 1.1 * d * D, so C > 1.1 * D

When C/D <= 1.1, no partial liquidation amount can improve the health factor. The assert always reverts.

Full liquidation (d = D) would set debt to 0 and HF to max_value(uint256). But full liquidation requires redeeming 1.1 * D in collateral. When C < 1.1 * D, the subtraction in _redeem_collateral underflows (Vyper safe math) and reverts before the HF check is reached.

Two separate failure modes:

  1. 110% > C/D > 100% (partial): HF doesn't improve, reverts at the assert

  2. C/D < 110% (full): Requires more collateral than exists, underflows at _redeem_collateral

This is distinct from the known insolvency issue in the README ("If the protocol ever becomes insolvent, there is almost no way to recover"). Insolvency means C < D (ratio < 100%). This bug triggers at C/D < 1.1 (ratio < 110%) where positions still have positive equity but cannot be liquidated.

Risk

Likelihood:

  • Any ETH/BTC price drop that pushes a position below 110% CR triggers this. Happens routinely in crypto markets.

  • The existing test test_must_improve_health_factor_on_liquidation confirms the revert.

Impact:

  • Positions between 100%-110% CR become permanent zombies. No one can liquidate them.

  • Further price drops push these positions below 100%, creating irrecoverable bad debt.

  • Protocol solvency invariant breaks as underwater positions accumulate.

Proof of Concept

Mathematical proof:
User: 10 ETH at $1,050/ETH = $10,500 collateral, 10,000 DSC debt
Ratio: 105%, HF = 0.525e18 — liquidatable (HF < 1e18)
Partial liquidation of $1,000 debt:
Removes $1,100 collateral (1.1x bonus)
New: $9,400 / $9,000 = 104.4%, HF = 0.522e18
HF went DOWN, not up → reverts "DSCEngine__HealthFactorNotImproved"
Full liquidation of $10,000 debt:
Requires $11,000 in collateral
User only has $10,500 → underflow in _redeem_collateral, reverts
Result: Position is permanently stuck. No liquidation possible.

Recommendations

Allow full liquidation to bypass the HF improvement check. When debt goes to 0, HF becomes max_value(uint256), so the special case is easy to detect:

ending_user_health_factor: uint256 = self._health_factor(user)
- assert (
- ending_user_health_factor > starting_user_health_factor
- ), "DSCEngine__HealthFactorNotImproved"
+ if ending_user_health_factor != max_value(uint256):
+ assert (
+ ending_user_health_factor > starting_user_health_factor
+ ), "DSCEngine__HealthFactorNotImproved"

This allows full liquidation (debt zeroed, HF = max) while still requiring partial liquidations to improve the position. Additionally, consider reducing or eliminating the bonus for deeply undercollateralized positions so partial liquidations can also succeed below 110%.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!