Skip to main content
The single number the controller uses to decide whether an account is solvent, may borrow, or can be liquidated. Read first: Risk parameters.

Definition

Health factor (HF) compares an account’s liquidation-threshold-weighted collateral against its total debt, both valued in USD-WAD (10^18):
HF = liquidation_threshold_weighted_collateral_usd / total_debt_usd
The controller computes it in calculate_health_factor (contracts/controller/src/helpers/math.rs:49). The division floors (div_floor), so rounding always reports the more conservative value. An account with no debt returns i128::MAX — an effectively infinite health factor. An account is liquidatable when HF < 1e18 (one WAD). Each term is built from a scaled position, its market index, and the asset price:
position_value_usd = to_wad(scaled_amount_ray · index_ray) · price_wad        [WAD]
weighted_collateral = Σ position_value(supply_i) · liquidation_threshold_i / BPS
total_debt          = Σ position_value(borrow_i)
The liquidation threshold applied to each collateral is the value snapshotted onto that position at supply time (AccountPositionRaw) from the account’s spoke asset config. The health factor is always evaluated post-execution on cached indexes, so a borrow or withdraw is checked against the state it would produce.
Health factor is separate from the LTV gate. Borrow and withdraw additionally require Σ position_value(supply_i) · loan_to_value_i / BPS ≥ total_debt_usd. Because LTV is always below the liquidation threshold, an account reaches its borrow limit before it becomes liquidatable.

Worked example

An account supplies 1,000ofcollateralwhoseliquidationthresholdis0.78(7800BPS)andborrows1,000 of collateral whose liquidation threshold is `0.78` (`7800` BPS) and borrows 500. All USD values are WAD-scaled, so 1,000is10001018=1000e18and1,000 is `1000 · 10^18 = 1000e18` and 500 is 500e18.
weighted_collateral = 1000e18 · 7800 / 10000 = 780e18      ($780)
total_debt          = 500e18                                ($500)
HF                  = 780e18 / 500e18 = 1.56e18             (1.56)
The health factor is 1.56e18, comfortably above the 1e18 boundary, so the account is solvent and not liquidatable. If the collateral price halved, weighted_collateral would fall to 390e18 and HF to 0.78e18 — below 1e18, and the account would become liquidatable. Recompute after any supply, borrow, repay, withdraw, price change, or liquidation.

Decision table

ConditionMeaningWhat it allows
HF ≥ 1e18Solvent.Borrow or withdraw, subject to the LTV gate.
HF < 1e18Liquidatable.A liquidator may repay eligible debt and seize collateral.
No debt (HF = i128::MAX)No borrow-side risk.Withdraw freely; supply and borrow checks still apply per market.

Edge cases

CaseBehavior
Account has no debtcalculate_health_factor returns i128::MAX; treat as unbounded, never as a finite large number.
Price unavailable, risk-increasing flowThe oracle is fail-closed: a missing primary price under a strict policy reverts the whole operation rather than pricing the position at zero. See Oracles.
Near-boundary roundingThe floor division and integer math can flip an account just over 1e18 to just under after a price tick; keep a buffer in any UI or simulation.
Dust positionsA partial withdraw or repay leaving sub-floor residue is expanded to a full close before HF is finalized; see Risk parameters.
On-chain health-factor values are WAD-scaled (10^18). The view health_factor(account_id) -> i128 returns this raw WAD integer, and i128::MAX when the account is debt-free. Divide by 10^18 only for display; never approve a borrow or withdraw from cached account or price data — re-read state before building the transaction.

Next

Liquidations

What happens once an account crosses below HF = 1.

Risk parameters

The liquidation thresholds and LTVs that weight the formula.

Accounts and risk

Position snapshotting, spoke risk, and risk gates.

Oracles

The prices that feed every position value.