liquidate() in src/dsc_engine.vy pays liquidators a 10% bonus while requiring the user's health factor to strictly improve after liquidation, else the whole call reverts with DSCEngine__HealthFactorNotImproved. For any user with collateral ratio C/D in (100%, 110%], every liquidation strictly decreases the health factor (the bonus destroys more collateral value than the debt reduction saves), so liquidation always reverts. Once a price crash pushes a position into this dead zone, it can never be liquidated — permanent bad debt with no recovery path. The code comments admit the design flaw: "A known bug would be if the protocol was only 100% collateralized".
Math: HF = (C × 50%) / D. After covering d (USD): C' = C − 1.1d, D' = D − d. HF' > HF ⟺ C/D > 1.1. So liquidation can only pass the assert when C/D > 1.1 — users at 100%–110% (underwater, HF < 1, exactly who should be liquidated) revert on every attempt, at any cover amount.
A price crash pushing users into (100%, 110%] creates permanently unliquidatable positions: bad debt sits in the protocol forever, DSC becomes effectively undercollateralized.
No alternative recovery path exists (no global settlement, no bad-debt auction).
Needs no attacker — ordinary market volatility manufactures these positions.
Likelihood: Medium — requires a large-enough price drop to push users into the band (common in crypto collateral markets; reproduced here with ETH $2000→$200). Once inside, no on-chain action can rescue the position.
Python/titanoboa test against the actual Vyper contracts (ran green):
Output: victim1 liquidate REVERTED as expected (DSCEngine__HealthFactorNotImproved) / control OK: victim2 liquidated successfully / ALL ASSERTIONS PASSED.
The bonus (paid from the user's collateral) and the strict-improvement assert are incompatible. Two options:
Option A (minimal): accept liquidation that reaches a healthy HF instead of demanding strict monotonic improvement. Option B (cleaner): pay the bonus from a protocol insurance fund rather than the user's collateral, making C' = C − d so every liquidation of a C/D > 1 user strictly improves HF and the dead zone disappears. Rationale: the assert exists to stop griefing liquidations, but in the dead zone the real risk is no one being able to liquidate at all — bricking insolvent positions leaves DSC permanently undercollateralized.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.