The engine converts between token amounts and USD using only the price-feed's decimals, never the collateral token's own decimals:
ADDITIONAL_FEED_PRECISION (1e10) scales an 8-decimal Chainlink answer up to 18 decimals. The formula then multiplies by amount and divides by PRECISION (1e18) — which is only correct when amount is expressed in 18 decimals. It never reads ERC20.decimals() for the collateral token.
The protocol's own scope names WETH and WBTC as collateral. WETH has 18 decimals (fine), but WBTC has 8 decimals. For WBTC, amount is 1e8-scaled, not 1e18-scaled, so every conversion is off by a factor of 1e10.
Verified numerically:
_get_usd_value(WBTC, 1e8) (one whole WBTC at $60,000) returns 6_000_000_000_000 wei ≈ $0.000006, instead of 60_000e18. WBTC collateral is undervalued by 1e10×.
_get_token_amount_from_usd(WBTC, 60_000e18) (the WBTC worth $60k) returns 1e18 raw units = 1e10 whole WBTC, instead of 1e8 (one WBTC). The seize amount is overstated by 1e10×.
Consequences:
WBTC is unusable as collateral. A depositor who posts real WBTC gets essentially zero collateral value, so _revert_if_health_factor_is_broken blocks them from minting any meaningful DSC against it. Their WBTC is locked dead weight while the health factor treats it as worthless.
WBTC liquidations are broken. In liquidate, token_amount_from_debt_covered = _get_token_amount_from_usd(collateral, debt_to_cover) is 1e10× too large, so _redeem_collateral attempts to subtract an astronomically large amount from user_to_token_address_to_amount_deposited[user][WBTC] and reverts on underflow — a healthy liquidation path cannot execute for WBTC-denominated debt.
Both the account-value accounting and the liquidation seize math are wrong for any collateral whose token decimals differ from 18. The system's advertised WETH/WBTC basket is only half-functional.
Impact: Medium. One of the two supported collateral assets (WBTC) is mis-priced by ten orders of magnitude: it cannot back minted DSC, and any position involving it cannot be liquidated normally. It does not enable theft or over-minting (WBTC is under-valued, never over-valued), but it breaks core protocol accounting for a first-class asset.
Likelihood: High. Deterministic for every WBTC interaction; triggers the moment anyone uses the second listed collateral token, with no special conditions.
Flow: deposit_collateral(WBTC, 1e8) then mint_dsc(1e18) reverts with DSCEngine__BreaksHealthFactor, because the engine values the deposited WBTC at ~$0.000006 even though it is worth $60,000.
Normalise every collateral amount to 18 decimals using the token's own decimals() before applying the feed math. Store each token's decimals (or scale factor) at construction and use it in both conversion functions, e.g.:
Only after normalising can WETH (18) and WBTC (8) — or any "basket of assets" the README invites forkers to swap in — be valued and liquidated correctly.
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.