Algo Ssstablecoinsss

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

Liquidation can revert because the bonus makes the borrower's health factor worse

Root + Impact

Description

  • Liquidation should allow unhealthy positions to be reduced when the liquidator repays debt and receives collateral plus the liquidation bonus.

  • liquidate requires the borrower's ending health factor to be greater than the starting health factor. However, the protocol removes collateral worth more than the debt repaid because of the liquidation bonus. For sufficiently unhealthy positions, repaying debt while removing debt + bonus collateral lowers the borrower's health factor, so the transaction reverts even when the borrower is not yet insolvent.

# src/dsc_engine.vy
token_amount_from_debt_covered: uint256 = self._get_token_amount_from_usd(
collateral, debt_to_cover
)
bonus_collateral: uint256 = (
token_amount_from_debt_covered * LIQUIDATION_BONUS
) // LIQUIDATION_PRECISION
@> self._redeem_collateral(
@> collateral,
@> token_amount_from_debt_covered + bonus_collateral,
@> 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
@> ), "DSCEngine__HealthFactorNotImproved"

Risk

Likelihood:

  • This occurs when a borrower's health factor is below the point where the 10% liquidation bonus still allows health factor improvement.

  • A normal collateral price drop can create this state before the position is fully insolvent.

Impact:

  • Liquidators cannot liquidate affected unhealthy positions because every liquidation reverts with DSCEngine__HealthFactorNotImproved.

  • The protocol keeps risky debt outstanding even though the borrower still has raw collateral available to repay part of the debt.

Proof of Concept

def test_liquidation_bonus_can_make_health_factor_worse():
liquidation_threshold = 50
liquidation_precision = 100
starting_collateral = 1090 * 10**18
starting_debt = 1000 * 10**18
debt_repaid = 100 * 10**18
collateral_removed = 110 * 10**18 # 100 debt plus 10% bonus
starting_hf = ((starting_collateral * liquidation_threshold) // liquidation_precision) * 10**18 // starting_debt
ending_collateral = starting_collateral - collateral_removed
ending_debt = starting_debt - debt_repaid
ending_hf = ((ending_collateral * liquidation_threshold) // liquidation_precision) * 10**18 // ending_debt
assert starting_hf < 10**18
assert ending_hf < starting_hf

The borrower starts with:

health factor = ($1,090 * 50%) / $1,000 = 0.545

After a liquidator repays 100 DSC, the protocol removes $110 of collateral because of the bonus:

health factor = ($980 * 50%) / $900 = 0.5444

The health factor is lower, so liquidate reverts.

Recommended Mitigation

- assert (
- ending_user_health_factor > starting_user_health_factor
- ), "DSCEngine__HealthFactorNotImproved"
+ # Allow partial liquidation when debt is repaid and collateral is available.
+ # Alternatively cap collateral seized or compute the maximum debt_to_cover
+ # that keeps the borrower's health factor improving.

Compute a maximum repay amount/collateral seizure that guarantees health factor improvement, or remove the strict improvement check and rely on solvency and collateral-availability checks designed for partial liquidation.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 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!