Both pricing functions in DSCEngine assume every collateral token has 18 decimals, but real WBTC has 8 decimals. The error is a factor of 1e10 in both directions, and it breaks WBTC — one of only two collateral assets — completely.
_get_usd_value undervalues WBTC collateral 1e10x: 1 WBTC at $60,000 is valued at $0.000006, so a user can mint at most 0.000003 DSC against a whole BTC. WBTC is effectively unusable as collateral.
_get_token_amount_from_usd overstates the WBTC owed on liquidation 1e10x: liquidating a WBTC position tries to seize more WBTC than the user deposited, the balance subtraction underflows, and the liquidation reverts. WBTC-backed bad debt can never be cleared.
The repo's own mocks hide this: src/mocks/mock_token.vy gives every token 18 decimals, so the existing tests never exercise real 8-decimal WBTC.
Likelihood: High — the error is deterministic; any use of WBTC (a listed collateral token) triggers it, and the mock suite masks it.
Impact: High — half of the protocol's collateral system is broken. Users cannot mint DSC against WBTC at all, and WBTC-backed positions can never be liquidated, so bad debt in WBTC positions can never be cleared and the protocol can accrue unliquidatable undercollateralized debt.
The PoCs use a WBTC mock with real 8 decimals (src/mocks/mock_wbtc_8dec.vy) and a BTC feed at $60,000. The first test shows the 1e10x undervaluation and that minting 100 DSC against $60,000 of BTC reverts. The second test shows the 1e10x overstatement and that liquidating a WBTC position reverts because it would seize 1.1 WBTC from a 1 WBTC deposit.
Scale by each collateral token's actual decimals — read decimals() from the token (or store a per-token decimal factor at deploy time) — in both _get_usd_value and _get_token_amount_from_usd, instead of assuming 18 decimals. Also fix the mocks so the WBTC mock uses 8 decimals, so the test suite exercises the real configuration.
## Description The `_revert_if_health_factor_is_broken` function is responsible for ensuring that a user's health factor meets the minimum required standard. There is only implementation for WETH. ## Vulnerability Details In the function, there is only implementation for WETH. ```Solidity @internal def _revert_if_health_factor_is_broken(user: address): user_health_factor: uint256 = self._health_factor(user) assert ( user_health_factor >= MIN_HEALTH_FACTOR ), "DSCEngine__BreaksHealthFactor" ``` Value of the `MIN_HEALTH_FACTOR=10^18`is higher than the Satoshi factor which is 10^8. As a result, for WBTC, the `user_health_factor` can be inflated to more than 101010^{10} times its normal value. ## Impact Bigger value of MIN_HEALTH_FACTOR for WBTC allows on bigger value of `user_health_factor`and wrong value when function should revert. ## Recommendations Add MIN_HEALTH_FACTOR also for WBTC. ```Solidity @internal def _revert_if_health_factor_is_broken(user: address): user_health_factor: uint256 = self._health_factor(user) # Check if the user's token is WBTC and adjust health factor accordingly if user_health_factor >= (MIN_HEALTH_FACTOR * 10**10): # If user health factor is higher due to WBTC precision, still ensure it meets the minimum assert user_health_factor >= MIN_HEALTH_FACTOR, "DSCEngine__BreaksHealthFactor" else: assert user_health_factor >= MIN_HEALTH_FACTOR, "DSCEngine__BreaksHealthFactor" ```
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.