Algo Ssstablecoinsss

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

[H-02] Missing Return Value Check on DSC burn_from() Allows Silent Burn Failures

Root + Impact

Description

  • Similar to the mint issue, the _burn_dsc() function calls extcall DSC.burn_from() without checking the return value. The comment again acknowledges "Note, we are not checking success here".

  • When the burn fails silently, the user's debt is decreased in the protocol's accounting, but the DSC tokens are never actually burned, allowing users to effectively clear their debt for free and withdraw collateral while keeping the DSC tokens.

// Root cause in the codebase with @> marks to highlight the relevant section
@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
@> # Note, we are not checking success here
@> extcall DSC.burn_from(dsc_from, amount_dsc_to_burn)

Risk

Likelihood: Medium

  • Reason 1 // Depends on DSC token implementation behavior

  • Reason 2 // Could be exploited if burn_from returns false instead of reverting

Impact: Critical

  • Impact 1 // Users can clear debt without burning tokens

  • Impact 2 // Free collateral extraction

  • Impact 3 // Protocol becomes insolvent

  • Impact 4 // DSC loses its peg as unbacked tokens circulate

Proof of Concept

The following scenario shows how an attacker can exploit silent burn failures to extract collateral while keeping their DSC tokens. This creates unbacked DSC in circulation and drains the protocol of collateral, leading to complete insolvency.

def test_burn_failure_allows_free_collateral():
# User has collateral and DSC debt
with boa.env.prank(user):
weth.approve(dsce, COLLATERAL_AMOUNT)
dsce.deposit_collateral_and_mint_dsc(weth, COLLATERAL_AMOUNT, AMOUNT_TO_MINT)
# If burn_from fails silently
dsc.approve(dsce, AMOUNT_TO_MINT)
dsce.redeem_collateral_for_dsc(weth, COLLATERAL_AMOUNT, AMOUNT_TO_MINT)
# User's debt is cleared in protocol accounting
dsc_minted, _ = dsce.get_account_information(user)
assert dsc_minted == 0 # Debt cleared
# But user still has DSC tokens!
assert dsc.balanceOf(user) == AMOUNT_TO_MINT # Tokens not burned
# User extracted collateral AND kept DSC = free money

Recommended Mitigation

Check the return value of the burn_from call and revert if it fails. This ensures the debt reduction only occurs when tokens are actually burned, maintaining protocol solvency and correct accounting.

@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
- # Note, we are not checking success here
- 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 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!