Algo Ssstablecoinsss

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

[M-02] Checks-Effects-Interactions (CEI) Violation and Missing Reentrancy Guard in `liquidate()`

[M-02] Checks-Effects-Interactions (CEI) Violation and Missing Reentrancy Guard in liquidate()

Summary

The liquidate() function in dsc_engine.vy transfers collateral tokens to the liquidator before burning the liquidator's DSC debt tokens and before performing the health factor monotonicity assertion. Additionally, dsc_engine.vy does not implement a reentrancy lock (@nonreentrant). If an ERC20 token with transfer hooks (or ERC777 token) is supported, an attacker can reenter the engine mid-liquidation.

Vulnerability Details

In src/dsc_engine.vy:

# Lines 173-188
# 1. External Call / Interaction: Seizes collateral and sends to msg.sender
self._redeem_collateral(
token_collateral_address, token_amount_from_debt_covered, user, msg.sender
)
# 2. State Mutation / Effect: Burns DSC from liquidator and reduces debt
self._burn_dsc(debt_to_cover, user, msg.sender)
# 3. Post-Condition / Invariant: Checks health factor improvement
ending_user_health_factor: uint256 = self._health_factor(user)
assert (
ending_user_health_factor > starting_user_health_factor
), "DSCEngine__HealthFactorNotImproved"

In _redeem_collateral:

# Lines 260-264
self.s_balances[_from][token_collateral_address] -= amount_collateral
log CollateralRedeemed(token_collateral_address, amount_collateral, _from, _to)
assert extcall IERC20(token_collateral_address).transfer(_to, amount_collateral)

The contract performs an external call via transfer(_to, amount_collateral) to msg.sender before:

  1. Burning the liquidator's DSC tokens in _burn_dsc.

  2. Reducing the borrower's debt mapping self.s_dsc_minted[user].

  3. Evaluating the ending health factor invariant.

Because no reentrancy modifier (@nonreentrant) is attached to liquidate() or other vault functions, any collateral token that implements recipient transfer callbacks (e.g. ERC777 tokens, ERC1363 tokens, or tokens with hooks) allows the liquidator to reenter the protocol while the borrower's debt has not yet been deducted.

Impact

  • Inconsistent Intermediate State: The recipient receives collateral while the protocol state still reflects full outstanding debt.

  • Cross-Function Reentrancy Risk: An attacker can invoke view or state-modifying functions (e.g. depositing or redeeming other collateral) while internal state is inconsistent.

Tools Used

  • Manual Code Review & FREI-PI Analysis

  • The Tincho Method CEI Deconstruction

Recommended Mitigation

  1. Reorder operations to adhere strictly to Checks-Effects-Interactions (burn DSC first, transfer collateral second).

  2. Add @nonreentrant protection to liquidate and all external state-changing vault entrypoints:

- self._redeem_collateral(
- token_collateral_address, token_amount_from_debt_covered, user, msg.sender
- )
- self._burn_dsc(debt_to_cover, user, msg.sender)
+ self._burn_dsc(debt_to_cover, user, msg.sender)
+ self._redeem_collateral(
+ token_collateral_address, token_amount_from_debt_covered, user, msg.sender
+ )
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!