dsc_engine.__init__ registers whatever two token/feed pairs it is given into the fixed COLLATERAL_TOKENS array without checking that the two tokens are different. _get_account_collateral_value computes a user's collateral value by looping over that array and summing _get_usd_value(token, deposited[token]) per array entry — so if the same token is registered twice, every deposit is valued twice. Health factors, minting capacity and liquidation thresholds are all computed from the doubled figure.
In src/dsc_engine.vy:
The project's own solvency invariant (in the fuzz suite) cannot catch this: it values the engine's holdings with the same double-counting get_usd_value path.
With WETH registered twice (a one-argument deployment mistake, with no guard to catch it), a user who deposits 10 WETH ($20,000) is credited with $40,000 of collateral value and can mint 20,000 DSC at a displayed health factor of exactly 1.0, while the real backing is $20,000 for $20,000 of debt — 100% collateralized in a system whose safety model requires 200%. The position does not become liquidatable until the real price falls by more than half (at $1,500 the engine still reads HF 0.75, by which point the real shortfall is already $5,000 per such position). Every duplicate-registered deployment silently halves the protocol's collateralization requirement.
Boa/pytest test (passes locally):
Reject duplicate registrations in the constructor (assert token_addresses[0] != token_addresses[1]), and more generally assert each token is non-empty and not already registered before writing its price feed. A uniqueness check is the only line of defense, because the valuation loop trusts the array blindly.
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.