Algo Ssstablecoinsss

AI First Flight #2
Beginner FriendlyDeFi
EXP
View results
Submission Details
Severity: high
Valid

MIN_HEALTH_FACTOR constant only calibrated for WETH, breaks WBTC health checks

Title: MIN_HEALTH_FACTOR constant only calibrated for WETH, breaks WBTC health checks
Severity: High
Impact: WBTC depositors cannot mint any meaningful DSC — one of two collateral tokens is completely unusable.
Likelihood: High — deterministic arithmetic mismatch affects every WBTC-backed position.
Reference Files: src/dsc_engine.vy:300-329, src/dsc_engine.vy:269-273

Description

The _calculate_health_factor function returns results with 8 decimals of precision for WBTC collateral because the upstream _get_usd_value fails to normalize WBTC's 8 decimals to the protocol's 18-decimal precision standard. MIN_HEALTH_FACTOR is set to 10**18 — appropriate for WETH's 18 decimals but 10^10 times larger than any health factor a WBTC position can achieve. The vulnerable check:

MIN_HEALTH_FACTOR: public(constant(uint256)) = 1 * (10**18)
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"

Since _get_usd_value undervalues WBTC collateral by 10^10, the health factor for a WBTC position minting even 0.001 DSC falls to ~3 * 10^8 — six orders of magnitude below MIN_HEALTH_FACTOR (10^18) — causing every WBTC-backed mint to revert.

Risk

Impact: High. WBTC, one of only two collateral tokens in the protocol, is rendered completely unusable for minting DSC. Users depositing WBTC cannot open positions of any practical size, breaking the protocol's multi-collateral design.
Likelihood: High. The bug is deterministic — no market conditions, manipulation, or special triggers required. Any WBTC deposit followed by a mint attempt always fails.
A user depositing 1 BTC ($60,000) can mint at most ~0.000003 DSC instead of the expected ~30,000 DSC, a 10^10 reduction in borrowing power.

Proof of Concept

PRECISION = 10**18
ADDITIONAL_FEED_PRECISION = 10**10
MIN_HEALTH_FACTOR = 10**18
LIQUIDATION_THRESHOLD = 50
# Simulate 1 BTC collateral at $60,000/BTC
price_btc = 60_000 * 10**8 # Chainlink 8 decimals
amount_btc = 10**8 # 1 BTC in satoshis
# _get_usd_value produces undervalued result:
collateral_usd = ((price_btc * ADDITIONAL_FEED_PRECISION) * amount_btc) // PRECISION
# = (60_000*10^8 * 10^10 * 10^8) / 10^18 = 60_000 * 10^8 = 6e12 → should be 6e22
# _calculate_health_factor for 1000 DSC mint:
minted_dsc = 1_000 * 10**18
collateral_adjusted = (collateral_usd * LIQUIDATION_THRESHOLD) // 100
health_factor = (collateral_adjusted * PRECISION) // minted_dsc
# = (3e12 * 10^18) / 10^21 = 3,000,000,000
# MIN_HEALTH_FACTOR check:
assert health_factor >= MIN_HEALTH_FACTOR # 3e9 < 1e18 → REVERTS "DSCEngine__BreaksHealthFactor"

This Python simulation reproduces the exact arithmetic path through _get_usd_value_calculate_health_factor_revert_if_health_factor_is_broken, demonstrating that any WBTC position with >0.000003 DSC minted fails the health factor assertion.

Recommended Mitigation

@internal
@view
def _get_usd_value(token: address, amount: uint256) -> uint256:
# ... oracle call ...
token_decimals: uint256 = 18
if token != COLLATERAL_TOKENS[0]:
token_decimals = 8 # WBTC
normalized_amount: uint256 = amount * (10**(18 - token_decimals))
return (
(convert(price, uint256) * ADDITIONAL_FEED_PRECISION) * normalized_amount
) // PRECISION

Normalizing amount to 18 decimals before the price multiplication fixes the upstream valuation error. This ensures _calculate_health_factor receives correctly-scaled collateral values for WBTC, making the result directly comparable to MIN_HEALTH_FACTOR (10^18) without any magnitude mismatch. The fix applies to all functions that depend on _get_usd_value including _get_token_amount_from_usd and _get_account_collateral_value.

Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 3 hours ago
Submission Judgement Published
Validated
Assigned finding tags:

[H-01] In the function \_revert_if_health_factor_is_broken constatnt variable MIN_HEALTH_FACTOR is only for WETH.

## 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" ```

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!