Skip to main content
How borrower interest accrues, where protocol revenue goes, and how the pool socializes bad debt. Driven by supply_index_ray and borrow_index_ray. Read first: Markets. The pool never iterates accounts. It stores supply and debt as scaled amounts and applies interest to everyone at once by advancing two indexes, supply_index_ray and borrow_index_ray. A position’s actual balance is always derived from its scaled amount and the current index.

Scaled balances

Every supply and debt amount is stored as a scaled RAY value. The actual amount is the scaled value multiplied by the relevant index:
amount = scaled_amount_ray · index_ray / RAY
Supply positions and totals use supply_index_ray; debt positions and totals use borrow_index_ray. RAY is 10^27. Both indexes start at RAY (1.0) when a market is created and only ever rise from interest — except the supply index, which a bad-debt cleanup can lower (see below). Views reconstruct actual supplied and borrowed amounts on demand from the current index and the asset’s decimals.

Utilization

Utilization is the fraction of supplied liquidity that is currently borrowed, in RAY:
U = borrowed_actual / supplied_actual          [RAY]
When supplied is zero, utilization is defined as zero (utilization, common/src/rates.rs:142). Utilization drives the borrow rate and, through it, the supply rate.

Borrow-rate model

The annual borrow rate is a piecewise-linear curve with two kinksmid_utilization and optimal_utilization — producing three regions. It starts at base_borrow_rate and adds slope1, then slope2, then slope3 as utilization climbs (calculate_borrow_rate, common/src/rates.rs:9):
U < mid:            base + slope1 · U / mid
mid ≤ U < optimal:  base + slope1 + slope2 · (U − mid) / (optimal − mid)
U ≥ optimal:        base + slope1 + slope2 + slope3 · (U − optimal) / (1 − optimal)
rate = min(rate, max_borrow_rate)
The final rate is capped at max_borrow_rate (≤ 2·RAY). Parameters must satisfy base ≤ slope1 ≤ slope2 ≤ slope3 ≤ max and 0 < mid < optimal < 1 RAY, so the curve is non-decreasing.
max_utilization is a separate hard cap, not a rate kink. When a borrow or withdraw would push post-state utilization above max_utilization, the call reverts with #127 UtilizationAboveMax (unless max_utilization ≥ 1 RAY). It bounds how much of the pool can be borrowed; it does not change the rate curve. On every current mainnet market max_utilization = 95%, while optimal is 80% (or 65% for BTC and ETH).

Accrual

On every mutating pool call and on update_indexes, the pool advances the borrow index by compounding the rate over the elapsed time. Compounding is per millisecond, accrued in chunks of at most one year, using an 8-term Taylor approximation of e^x (compound_interest, common/src/rates.rs:59):
new_borrow_index = old_borrow_index · e^(rate_per_ms · Δt_ms)
The borrow index is capped at 1e36 (MAX_BORROW_INDEX_RAY, common/src/constants/pool.rs:11); exceeding it raises #33 MathOverflow. The Taylor remainder is bounded because each per-chunk exponent is small.

Splitting interest

The interest accrued on outstanding debt is split by reserve_factor_bps into a protocol fee and supplier rewards (calculate_supplier_rewards, common/src/rates.rs:123):
accrued_interest = borrowed · (new_borrow_index − old_borrow_index)
protocol_fee     = reserve_factor_bps / BPS · accrued_interest
supplier_rewards = accrued_interest − protocol_fee
The supplier share lifts supply_index, so every supplier’s position appreciates without any per-account write. The supply index is floored at WAD (SUPPLY_INDEX_FLOOR_RAW = WAD, common/src/constants/pool.rs:4); if it is already at or below that floor, the reward step is skipped. The protocol fee is added to revenue_ray and held as a scaled supply claim. The display supply APR follows directly (calculate_deposit_rate, common/src/rates.rs:37):
supply_apr = utilization · borrow_rate · (1 − reserve_factor_bps / BPS)
Worked example: at U = 80% (0.8e27), a borrow rate of 5% (0.05e27), and a reserve factor of 1000 BPS (10%), the supply APR is 0.8 · 0.05 · 0.9 = 0.036 — a 3.6% supply rate against a 5% borrow rate.

Protocol revenue

Protocol revenue is not a separate token balance. It is held as a scaled supply claim in revenue_ray, bounded by total supply (0 ≤ revenue_ray ≤ supplied_ray), so it appreciates with the supply index until claimed. On claim_revenue the pool syncs indexes, reconstructs the claimable amount, caps it by live reserves, burns the proportional scaled revenue, and transfers to its owner — the controller — which forwards it to the configured accumulator. See Controller ABI.

Rewards

add_rewards(asset, amount) lifts supply_index so every supplier benefits proportionally, exactly as the supplier-interest share does. It raises #37 NoSuppliersToReward if the market has no supply, because there is no scaled base to distribute against.

Bad debt

When an account’s total collateral is ≤ $5 WAD (BAD_DEBT_USD_THRESHOLD = 5·WAD) and its debt exceeds that collateral, the position is unrecoverable. Cleanup — inline during a liquidation, or via the keeper’s clean_bad_debt — seizes all of the account’s supply and debt, removes the account, and socializes the shortfall by lowering each affected market’s supply index proportionally:
new_supply_index = supply_index · (1 − capped_bad_debt / total_supplied_value)
The result is floored at WAD (SUPPLY_INDEX_FLOOR_RAW), so the index can never reach zero and divide-by-near-zero is impossible. The shortfall is shared across that market’s suppliers; the event CleanBadDebtEvent records the cleared totals. See Liquidations.

Fixed-point units

UnitRawDecimalsUsed for
BPS10_0004Reserve factor, LTV, liquidation threshold / bonus / fee, tolerances.
WAD10^1818USD prices, USD position values, health factor, dust floors.
RAY10^2727Indexes, utilization, rates, scaled supply and debt balances.
Multiply and divide helpers use half-up rounding by default, flooring where it protects the protocol and ceiling for amounts a user owes.

Next

Markets

What a market is and how its rate model is configured.

Risk parameters

Reserve factor, caps, dust floors, and the rate-model constraints.

Liquidations

How bad debt is triggered and socialized into the supply index.