Algo Ssstablecoinsss

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

DSCEngine has no reentrancy guard and violates CEI in redeem/liquidate, drainable by a malicious collateral token

DSCEngine has no reentrancy guard and violates CEI in redeem/liquidate, allowing a malicious collateral token to drain the protocol

Description

_redeem_collateral updates state, logs, then makes an external transfer to an arbitrary _to, with no reentrancy guard anywhere in the engine. If a collateral token has a transfer hook (ERC-777-style, or a malicious token the constructor accepted), the recipient re-enters during transfer.

# dsc_engine.vy:253-256 (_redeem_collateral)
success: bool = extcall IERC20(token_collateral_address).transfer(
_to, amount_collateral # @> external call, no nonreentrant guard
)
assert success, "DSCEngine_TransferFailed"

liquidate (dsc_engine.vy:124-130) redeems collateral to msg.sender before burning the user's DSC, so the health-factor accounting is mid-update when the external transfer fires.

Risk

Likelihood:
Medium. Requires a collateral token with a callback. The two collateral tokens are fixed at deploy, but standard tokens like WBTC/WETH variants, rebasing/hook tokens, or a deliberately chosen token expose this.

Impact:
High. Re-entering redeem_collateral / liquidate before the burn completes lets an attacker pull collateral multiple times against one debt reduction, draining other users' deposits and rendering the protocol insolvent.

Proof of Concept

Deploy a collateral token whose transfer re-enters redeem_collateral.

# MaliciousToken.transfer() calls engine.redeem_collateral(token, amount) again
engine.deposit_collateral(mal_token, 10 * 10**18)
engine.redeem_collateral(mal_token, 10 * 10**18) # re-enters before final HF check, double-withdraws

Recommended Mitigation

Add Vyper's reentrancy guard to every state-changing external entrypoint.

+from snekmate.utils import reentrancy_guard as rg
...
@external
+@nonreentrant
def liquidate(collateral: address, user: address, debt_to_cover: uint256):
+ # also reorder liquidate: burn DSC before redeeming collateral (CEI)
Updates

Lead Judging Commences

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