The engine scales the 8-decimal Chainlink price to 18 decimals, but it also assumes every collateral amount is already expressed with 18 token decimals. That is true for WETH and false for the explicitly supported zkSync WBTC, whose decimals() value is 8.
For 1 WBTC, amount is 1e8. At a BTC/USD price of 60,000e8, get_usd_value() returns 60,000e8 instead of 60,000e18, underpricing collateral by exactly 1e10. The incorrect value feeds directly into _calculate_health_factor() and is compared against MIN_HEALTH_FACTOR=1e18, so economically safe WBTC positions revert when minting.
The inverse formula has the same root cause:
It returns a token amount in 1e18 scale even for 8-decimal WBTC. A WBTC liquidation therefore requests 1e10 too many base units and reverts against the user's recorded collateral.
Likelihood: High
The mismatch occurs deterministically in every WBTC valuation.
WBTC is one of only two supported collateral assets and the configured token has 8 decimals.
Impact: High
Deposited WBTC cannot support an economically meaningful DSC mint because its health factor is scaled incorrectly.
WBTC liquidation amounts are also wrong by 1e10, breaking the protocol's liquidation and solvency mechanism for a primary collateral.
The following Titanoboa test deploys the exact scoped contracts and uses 1e8 base units to represent 1 WBTC:
Observed result: PASS. The read-only zkSync RPC check also returns decimals()=8 for 0xBBeB516fb02a01611cBBE0453Fe3c580D7281011.
Normalize both the feed price and token amount using per-asset decimals. Conceptually:
Store validated decimals alongside each collateral/feed pair or query them immutably during deployment. Do not create a different MIN_HEALTH_FACTOR per token; the correct fix is to normalize every collateral value into the same 1e18 USD unit before calculating health factor. Add regression tests for WETH 18 decimals and WBTC 8 decimals in both conversion directions and liquidation.
## 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.