liquidate()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.
In src/dsc_engine.vy:
In _redeem_collateral:
The contract performs an external call via transfer(_to, amount_collateral) to msg.sender before:
Burning the liquidator's DSC tokens in _burn_dsc.
Reducing the borrower's debt mapping self.s_dsc_minted[user].
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.
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.
Manual Code Review & FREI-PI Analysis
The Tincho Method CEI Deconstruction
Reorder operations to adhere strictly to Checks-Effects-Interactions (burn DSC first, transfer collateral second).
Add @nonreentrant protection to liquidate and all external state-changing vault entrypoints:
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.