Skip to Content
Protocol ConceptsInterest Rates

Interest Rate Model

Veil uses a two-slope (kink) interest rate model — the same design used by Aave and Compound. Rates are low below the target utilization and increase steeply above it.

Borrow Rate Formula

utilization = total_borrows / total_deposits if utilization ≤ optimal_utilization: borrow_rate = base_rate + slope1 × (utilization / optimal_utilization) else: excess = utilization - optimal_utilization borrow_rate = base_rate + slope1 + slope2 × (excess / (1 - optimal_utilization))

Supply Rate Formula

supply_rate = borrow_rate × utilization × (1 - reserve_factor)

The reserve factor determines the fraction of interest that goes to the protocol treasury rather than to depositors.

Default Parameters

All rates are WAD-scaled (WAD = 1e18). The table shows human-readable percentages.

ParameterDefault ValueWAD value
base_rate1% per year10_000_000_000_000_000
optimal_utilization80%800_000_000_000_000_000
slope14% per year40_000_000_000_000_000
slope275% per year750_000_000_000_000_000
reserve_factor10%100_000_000_000_000_000

At 80% utilization (kink): borrow rate = 1% + 4% = 5%

At 100% utilization: borrow rate = 1% + 4% + 75% = 80%

Index-Based Accrual

Interest is accrued lazily at the start of every state-mutating instruction. Two global indices are maintained:

borrow_index(t) = borrow_index(t₀) × (1 + borrow_rate × Δt / SECONDS_PER_YEAR) supply_index(t) = supply_index(t₀) × (1 + supply_rate × Δt / SECONDS_PER_YEAR)

Both indices start at WAD = 1e18 when the pool is initialised.

Depositor balance:

balance = shares × supply_index / WAD

Borrower current debt:

debt = principal × (borrow_index_current / borrow_index_at_borrow_time)

This means individual positions don’t need to be touched to stay up-to-date — the global index does the work. Depositors and borrowers earn/owe the correct amount regardless of when they last interacted.

Computing Rates Off-Chain

const WAD = 1_000_000_000_000_000_000n; function utilizationRate(borrows: bigint, deposits: bigint): bigint { if (deposits === 0n) return 0n; return (borrows * WAD) / deposits; } function borrowRate( util: bigint, baseRate: bigint, optimalUtil: bigint, slope1: bigint, slope2: bigint, ): bigint { if (util <= optimalUtil) { return baseRate + (slope1 * util) / optimalUtil; } const excess = util - optimalUtil; return baseRate + slope1 + (slope2 * excess) / (WAD - optimalUtil); } function supplyRate(borrowRate: bigint, util: bigint, reserveFactor: bigint): bigint { return (borrowRate * util * (WAD - reserveFactor)) / WAD / WAD; }
Last updated on