The _mint_dsc() function in dsc_engine.vy is designed to mint DSC stablecoins to users after they deposit sufficient collateral. The function updates the internal accounting by incrementing user_to_dsc_minted[msg.sender], checks the health factor, and then calls DSC.mint() to create the tokens.
The critical issue is that the function does not check the return value of DSC.mint(). If the mint operation fails (returns False), the transaction continues without reverting. This creates an accounting mismatch where the protocol records the user as having minted DSC debt, but no actual DSC tokens are created or transferred to the user.
Likelihood:
The DSC token's mint() function can fail when called by an address that lacks the minter role, or due to potential future implementation changes in the ERC20 contract.
External factors such as gas limitations, contract upgrades, or edge cases in the snekmate ERC20 implementation could cause the mint operation to return False without reverting.
The likelihood increases if the DSC Engine is not properly set as the minter during deployment, which is a common misconfiguration scenario.
Impact:
Protocol Insolvency: Users accumulate debt in the protocol's accounting system without receiving corresponding DSC tokens, creating phantom debt that reduces the protocol's actual collateralization ratio.
Frozen Collateral: Users cannot redeem their collateral because the protocol believes they have outstanding DSC debt that must be burned first, but the user has no DSC tokens to burn.
Health Factor Manipulation: The user's health factor is calculated based on recorded debt that doesn't exist, potentially preventing legitimate liquidations or allowing users to over-leverage.
Total Supply Mismatch: The sum of all user_to_dsc_minted values exceeds the actual DSC.totalSupply(), breaking fundamental protocol invariants and making accurate accounting impossible.
Attack Scenario:
Attacker deposits 10 ETH as collateral (~$30,000 value)
Attacker calls mint_dsc(100 DSC)
Internal accounting: user_to_dsc_minted[attacker] = 100 DSC
DSC.mint() fails silently due to misconfigured minter role
Attacker's collateral is now frozen (protocol thinks debt = 100 DSC)
Attacker has no DSC to burn, cannot redeem the 10 ETH
Result: $30,000 in collateral permanently locked with 0 DSC issued
Scale of Impact:
If this affects 10 users with 10 ETH each: 100 ETH ($300,000) frozen
Protocol insolvency: Claims 1,000 DSC minted, actual supply = 0 DSC
All liquidations fail (liquidators cannot acquire DSC to repay)
Additional Recommendations:
Deployment Verification: Add a post-deployment check to verify DSC Engine is set as the minter:
Integration Test: Add test specifically for mint failure scenarios:
Invariant Check: Add protocol-wide invariant test:
Fix Priority: 🔴 CRITICAL - Must be fixed before any deployment. This vulnerability can lead to protocol insolvency and permanent loss of user funds.
Estimated Fix Time: 5 minutes (add 2 lines of code)
Testing Time: 30 minutes (add comprehensive tests for failure scenarios)
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.