Algo Ssstablecoinsss

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

Unchecked Return Value on DSC.burn_from Allows Debt to Be Erased Without Token Destruction

Unchecked Return Value on DSC.burn_from Allows Debt to Be Erased Without Token Destruction

Description

  • When DSC is burned — either directly or as part of a redemption or liquidation — the engine decrements the user's recorded debt and calls DSC.burn_from to destroy the tokens.

  • The return value of DSC.burn_from is not checked. If the burn fails silently, the protocol decrements the debt record as though the burn succeeded, allowing a user to eliminate their debt while retaining their DSC tokens.

@internal
def _burn_dsc(
amount_dsc_to_burn: uint256, on_behalf_of: address, dsc_from: address
):
self.user_to_dsc_minted[on_behalf_of] -= amount_dsc_to_burn # @> debt erased before confirmation
extcall DSC.burn_from(dsc_from, amount_dsc_to_burn) # @> return value not checked

Risk

Likelihood:

  • The burn_from call fails due to insufficient allowance or a DSC token-level access control check — debt is erased but tokens remain in circulation.

  • A liquidator calls liquidate using a dsc_from address that has not approved the engine to spend its DSC — the burn fails silently, the target user's debt is cleared, and the liquidator keeps their DSC.

Impact:

  • A user's debt is wiped from the ledger without destroying the corresponding DSC tokens, inflating the circulating supply relative to collateral — breaking the USD peg.

  • A malicious actor can engineer a failed burn to free their collateral while retaining both the collateral and the DSC, draining the protocol.

Proof of Concept

A malicious actor can setup the failed brn on purpose and free the collateral without destroying the DSC causing unbacked DSC tokens in circulation and breaking the USD peg.

# 1. User mints DSC against collateral
dsc_engine.deposit_collateral_and_mint_dsc(weth.address, 10**18, 100 * 10**18, sender=user)
# 2. User calls burn_dsc but has revoked the engine's allowance on the DSC token
dsc_token.approve(dsc_engine.address, 0, sender=user)
# 3. burn_from fails silently — no success check exists
dsc_engine.burn_dsc(100 * 10**18, sender=user)
# 4. user_to_dsc_minted[user] == 0, but user still holds 100 DSC
# 5. User redeems their full collateral — health factor check passes (0 debt minted)
dsc_engine.redeem_collateral(weth.address, 10**18, sender=user)
# User now holds both their ETH collateral and 100 unbacked DSC

Recommended Mitigation

Capture and assert the return value of the burn call:

@internal
def _burn_dsc(
amount_dsc_to_burn: uint256, on_behalf_of: address, dsc_from: address
):
self.user_to_dsc_minted[on_behalf_of] -= amount_dsc_to_burn
- extcall DSC.burn_from(dsc_from, amount_dsc_to_burn)
+ success: bool = extcall DSC.burn_from(dsc_from, amount_dsc_to_burn)
+ assert success, "DSCEngine__BurnFailed"
Updates

Lead Judging Commences

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