Skip to main content

Interest model

This page covers how trove debt accrues over time, how the borrow rate is composed, and how troves with Redemption Shield enabled are priced. The contracts involved are InterestEngine (per branch), Aggregator (per-branch delta-rate offsets), and RateParControl (the system rate).

Try it live

The per-branch utilization controller runs on the production parameters in the interactive rates-vs-utilization simulator — all eight collaterals, their bands, and the shutdown rescale.

The two accumulators​

Each branch's InterestEngine maintains two per-second accumulators with 1e27 precision:

AccumulatorWhat it accruesWho pays it
accumulatedRateThe branch's effective interest rateAll troves on the branch
accumulatedShieldRateAn additional component for troves with Redemption Shield enabledTroves with Redemption Shield enabled only

A trove stores its debt as a normalized value (debt at deployment time, in 1e18 RD units) plus a snapshot of the accumulator(s) at the moment of its last interaction. Actual current debt is:

base:
actualDebt = normalizedDebt × accumulatedRate / 1e27

Redemption Shield cohort:
actualDebt = normalizedDebt × (accumulatedRate + premium component) / 1e27

The exact composition is in InterestEngine.sol; the user-relevant fact is that interest accrues continuously, and drip() simply moves the accumulator forward by the elapsed seconds × current rate.

The effective per-branch rate​

The branch's per-second rate accumulator is composed additively in the per-second-delta domain, then clamped to a final per-branch range. Working in per-second-delta units (i.e., subtracting the multiplicative identity RATE_PRECISION = 1e27):

branch_delta = (system_rate − 1) + branch_offset
branch_rate = 1 + clamp(branch_delta, MIN_FINAL_DELTA, MAX_FINAL_DELTA)

In APR-thinking shortcut:

branch_APR  ≈  clamp(system_APR + branch_offset_APR,  0.1%,  70%)

This is the exact composition in Aggregator._calcCollateralRate:

int256 deltaInterestRate = int256(_interestRate - RATE_PRECISION) + _offset;
return RATE_PRECISION + _clipFinalDelta(deltaInterestRate);

The components:

  • system_rate: output of RateParControl. Same number across the protocol. Bounded [0.25% APY, 50% APY], biased at 2%. See Peg control.
  • branch_offset: signed output of the Aggregator's per-branch PI loop on debt utilization. Positive when the branch is above its issuance target (hot); can be negative when below target, subject to the final-rate clamp keeping branch_APR ≥ 0.1%.
  • Final-rate clamps: MIN_FINAL_DELTA ≈ 0.1% APY, MAX_FINAL_DELTA ≈ 70% APY. These bind on the final branch rate after the offset is added: the upper clamp (70%) sits above the system rate's 50% ceiling, giving stressed branches extra headroom; the lower clamp (0.1%) sits below the system rate's 0.25% floor, so a strongly-negative branch offset can pull the effective rate under the system floor, but never below 0.1%.

The Aggregator updates the offset based on a 72-hour-half-life EMA of branch debt vs. a target. When the branch is utilizing more than its target share, the offset climbs. The drip interval is 3 hours (with up to 8 hours of catch-up).

System rate2.00%+Collateral rate offset+1.70%=Branch rate (clamped 0.1%–50%)3.70%Branch rate3.70%±Redemption Shield adjustment+ surcharge · shield on− discount · shield off=Your borrow APR3.70% ± shield
Three additive components (example at a 2% system rate). The system rate and the collateral rate offset are summed and clamped to 0.1%–50% APY; the signed Redemption Shield adjustment is then applied on top — positive for troves with Redemption Shield enabled, negative for troves without it.

Realized APR ranges​

A few representative slices. Numbers from the protocol-repo reference (docs/collateral-rate-and-borrow-rate-reference.md).

At system_rate = 2% APR base, 6-hour update window:

Debt-utilization errorbase APR
−100% to 0%clamped at minimum (≈ 0.1%)
0% (neutral)~2.00%
+5%~2.43%
+10%~2.86%
+20%~3.73%
+50%~6.37%
+100%~10.94%

At system_rate = 2.5% APR base, same window: starts ~2.5% at neutral, peaks ~14% at +100% utilization.

At a higher base (controller responding to RD trading below par), all rows shift up.

issuance target← below targetabove target →0 pp+9 ppunder-utilized → negativeover-utilized → offset climbs
Each branch adds an offset from its own utilization-band PI controller: 0 at the branch's issuance target, rising as debt runs above target, and negative below (down to the 0.1% floor).

Shield pricing​

Shield pricing produces one signed adjustment off the branch rate, applied with opposite signs to the two books. From InterestEngine._calcRates:

unShieldedRate = _collRate.sub(v);   // base pays branch_rate − v
shieldedRate = _collRate.add(u); // Redemption Shield cohort pays branch_rate + u

u and v are both non-negative quantities derived from the same shield-share curve. So:

  • A trove with Redemption Shield enabled pays a positive shield-share adjustment (+u). Call this the shield surcharge.
  • A trove without Redemption Shield receives a negative shield-share adjustment (−v). Call this the base discount.

The two are halves of the same mechanism, the Redemption Shield cohort book's surcharge funds the base book's discount. If you prefer to call the whole thing the "shield premium," that's fine, but note the premium is signed: positive for troves with Redemption Shield enabled, negative for troves without Redemption Shield.

Redemption Shield rate↑ higherhigher Redemption Shield share →base rate↓ lowerhigher Redemption Shield share →
Both rates are priced off the branch's Redemption Shield share. The more of a branch that has Redemption Shield enabled, the further the two rates spread — the Redemption Shield rate rises and the base rate falls, with the surcharge funding the discount.

The curve is designed so that:

  1. The Redemption Shield cohort book subsidizes the base book. Every Redemption Shield surcharge corresponds to a base discount.
  2. The base rate can't go below a floor. Even at full subsidy, the base APR is bounded; the floor is the _V_MAX endpoint, equivalent to −50% APR.
  3. Both sides scale with Redemption Shield share. A branch with 5% Redemption Shield share has tiny u and v; a branch at 80% has much larger ones.

Constants​

_S1 = 0.85e27                    // shield share inflection
_ALPHA = 0.5e27 // curve steepness
_U_MULT = 2e27 // utilization multiplier
_V_MAX = 21979552668138406918 // full-shield base cap (-50% APR)
_SHIELD_PRICING_CONSTANT_MIN = 50,000e18 // anti-grief floor on TVL

The shape of the curve: roughly linear at low Redemption Shield share, accelerating once Redemption Shield share crosses _S1. Troves with Redemption Shield enabled pay more per unit of shield protection in more-crowded branches.

Why this shape​

If shield pricing were flat:

  • A branch with 1% Redemption Shield share would charge the same surcharge as one at 90%, even though the second case offers far less protection because almost every trove has Redemption Shield enabled.
  • The base book could be redeemed out faster than redemption demand actually creates, leaving every remaining trove with Redemption Shield enabled.

The curve makes the surcharge expensive precisely when Redemption Shield is overcrowded, which is the right economic signal. The matching base discount on high-share branches makes leaving Redemption Shield disabled attractive, which keeps a redeemable book available.

What the dashboard shows​

A user reading the dashboard sees their effective APR as a branch rate plus one signed shield-share adjustment:

branch_APR    = clamp(system_APR + branch_offset_APR,  0.1%,  70%)
your_APR = branch_APR + shield_adjustment

where shield_adjustment depends on the branch's Redemption Shield share:
trove with Redemption Shield enabled → +u (a surcharge; you pay extra)
trove without Redemption Shield → −v (a discount funded by the Redemption Shield surcharge)

Both u and v are non-negative and grow with Redemption Shield share. On a high-share branch, troves without Redemption Shield can borrow at a meaningfully lower APR than the branch rate, sometimes substantially less.

Why interest accrues continuously​

Liquity v1 had a one-time issuance fee plus a decaying base rate. The continuously-accruing model is closer to a standard variable-rate loan and gives the controller a smoother knob to turn. There's no fee shock at borrow time.

The downside is that ICR moves over time even if collateral price is constant, interest accrues. A trove sitting at 110% with debt growing 5% APR will hit the MCR in a few months without any external event. Borrowers should plan for this.

How drip actually fires​

Branches drip on three triggers:

  1. TroveManager.drip(): called from the Aggregator's drip-all sweep, or directly by any state-changing borrower op on the branch.
  2. InterestEngine.drip(): internal; called by TroveManager.
  3. Aggregator.drip() / dripWithRewardCap(): sweeps every non-shutdown branch. Pays a keeper reward scaled by elapsed time cubed over [DRIP_INTERVAL, DRIP_INTERVAL_2 = 8 hours], sized from the KeeperRewards balance — there is no fixed RD cap.

The reward structure pulls some keeper attention without making the drip a profitable game by itself. Any user op already does the drip for free as part of its work.

Deep dive​