Algo Ssstablecoinsss

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

`burn_dsc` reverts unless the burn single-handedly restores the health factor to >= 1, so underwater users cannot partially repay or deleverage in installments

Summary

burn_dsc first reduces the caller's debt and then asserts that the caller's health factor is at least MIN_HEALTH_FACTOR; if the repayment improves the position but does not fully cure it, the entire burn is rolled back. A user who is underwater (HF < 1) therefore cannot repay in installments, cannot repay with whatever DSC they currently hold unless it is enough to cure the position in one transaction, and cannot gradually deleverage — their only exits are one full cure-sized repayment, adding more collateral first, or being liquidated at a 10% bonus to someone else. redeem_collateral_for_dsc has the same all-or-nothing shape.

Vulnerability Details

In src/dsc_engine.vy:

@external
def burn_dsc(amount_dsc_to_burn: uint256):
self._burn_dsc(amount_dsc_to_burn, msg.sender, msg.sender)
self._revert_if_health_factor_is_broken(msg.sender) # reverts the burn if HF still < 1
​
@internal
def _revert_if_health_factor_is_broken(user: address):
user_health_factor: uint256 = self._health_factor(user)
assert user_health_factor >= MIN_HEALTH_FACTOR, "DSCEngine__BreaksHealthFactor"

Burning DSC strictly reduces debt and can only improve the health factor; a post-burn HF check serves no protective purpose for a burn (unlike for mint/redeem), yet it is applied anyway and reverts the improvement.

Impact

Concrete case from the PoC (the repository's own underwater setup): 10 WETH deposited, 100 DSC minted, price falls to $18 → collateral value $180, HF 0.9. The user holds DSC and tries to repay 5 DSC, which would improve HF to 0.947368421052631578 — the transaction reverts with DSCEngine__BreaksHealthFactor and the debt stays at 100. Only a single burn of at least 10 DSC (restoring HF to exactly 1.0) is accepted. A user who can only source part of the cure amount is forced to wait for liquidation and pay the 10% liquidation bonus on top, even though they were willing and able to reduce their debt.

Proof of Concept

Boa/pytest test (passes locally):

def test_poc_partial_repay_below_cure_amount_reverts(dsce_minted, dsc, eth_usd, some_user):
eth_usd.updateAnswer(18 * 10**8)
assert dsce_minted.health_factor(some_user) == to_wei(0.9, "ether")
​
with boa.env.prank(some_user):
dsc.approve(dsce_minted, to_wei(100, "ether"))
with boa.reverts("DSCEngine__BreaksHealthFactor"):
dsce_minted.burn_dsc(to_wei(5, "ether")) # would improve HF to 0.947...; rolled back
dsce_minted.burn_dsc(to_wei(10, "ether")) # cure-sized burn works
assert dsce_minted.health_factor(some_user) == to_wei(1, "ether")
user_debt, _ = dsce_minted.get_account_information(some_user)
assert user_debt == to_wei(90, "ether")

Recommended Mitigation

Do not apply the health-factor check to burn_dsc (and to the burn leg of redeem_collateral_for_dsc): burning debt is always health-factor-improving. If a check is desired, require only that the post-burn health factor is not worse than the pre-burn one, which permits installment repayments while still blocking any abuse.

Updates

Lead Judging Commences

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