Skip to content
Protocol

Interest distribution

Interest is minted at accrual, split by formula between the Stability Pool, the backstop, the treasury and liquidity incentives, and the rate floor is read from an on-chain reference.

Every borrower sets their own annual rate. Interest accrues linearly on the branch's interest clock and capitalises at each touch. At every accrual the pending amount is minted in fyUSD to the InterestRouter and immediately split. There is no yield index, no rebasing, no claim to be made later.

That design choice is what makes the global supply invariant true by construction rather than by convention: fyUSD.totalSupply == Σ debt + Σ badDebt + minted[PSM] (Rule R-2.1.3, principle P5).

Accrual

Between two touches of a position (Rule R-5.5.1):

How it is calculated

debt(u) = recordedDebt[u] × (RAY + rateRay[u] × (interestClock − lastAccrualClock[u]) / 365 d) / RAY

At branch level (Rule R-5.5.2):

How it is calculated

pending = aggWeightedDebt × (interestClock − clockAtLastAggAccrual) / (365 d × RAY), where aggWeightedDebt = Σ recordedDebt[u] × rateRay[u]

Every branch operation, and the permissionless accrue(), begins by advancing the clock, then computing pending, minting it to the router, calling route(pending), and adding it to the recorded aggregate debt. If a position is then touched, its own interest capitalises and the weighted aggregate is updated (Rule R-5.5.3).

Repaying exactly getDebt(u) burns exactly getDebt(u) and leaves the debt at zero. This is a mandatory test, because getting it wrong is one of the two most common CDP bugs (Rule R-5.5.4).

The interest clock

Interest accrues in every state, including out of session, with three exceptions (Rule R-5.5.5):

ConditionEffect on the clock
BLIND (sequencer down, oracle paused, circuit, missed heartbeat) for up to 24 hoursRuns normally. A short halt earns nobody a discount.
Degraded state continuously beyond 24 hoursStops, for the portion of the interval beyond the 24-hour mark. Resumes on return to a calendar state. Events InterestClockPaused and InterestClockResumed.
After shutdownAtFrozen permanently.
90 days after Sunset beginsFrozen permanently, across all branches.

Guarantee

Nobody decides to suspend interest. The clock is a monotone counter that stops on a condition and restarts on a condition. There is no vote, no key, and no discretion anywhere in this mechanism (Rule R-5.5.5).

The clock is monotone and never exceeds elapsed time since deployment; this is a fuzzed invariant (Rule R-16.3 item 10).

Mint fee and rate-change fee

Opening a position, and every subsequent increase of debt, carries a fee equal to seven days of interest at the position's chosen rate (Rule R-5.6.1):

How it is calculated

fee = Δdebt × annualRate × 7 / 365

The fee is added to the debt, minted to the router, and routed. It counts toward minDebt, the caps and the post-operation ICR. At the Tier 1 floor of 1.5%, the fee is 0.0288% of the amount borrowed.

Changing the rate within seven days of the last change carries the same fee on the whole debt at the new rate (Rule R-5.7.1). Lowering risk carries no fee and no cooldown, and a delegate gets no exemption.

The economic point of both fees is the same: they price the option to churn. A borrower who sets a rate and leaves it pays nothing; a borrower who games the redemption queue by moving their rate every day pays for it.

The four recipients

Everything minted outside principal — accrued interest, mint fees, rate-change fees, PSM fees, repayWithUSDG fees — reaches the InterestRouter and is split in a single call (Rule R-10.1):

RecipientWhat it is
StabilityPoolThe pool of the branch that produced the amount; for PSM swap fees, the pools of all activated branches pro rata their uncovered debt
BackstopThe fyUSD reserve that absorbs bad debt
EndowmentAn immutable address belonging to the development company
PoolIncentivePays in-range liquidity providers on at most two canonical pools

Rounding dust goes to the pool. No recipient and no share can be modified, ever.

Regimes

Shares are a formula of state, read at every route(), with D_total = fyUSD.totalSupply() − minted[PSM] (Rule R-10.2):

RegimeConditionPoolBackstopTreasuryIncentives
1Until the latch90% (85% if the backstop is under target)0, or 5 points taken from the pool share0%10%
2D_total has once exceeded 25 M USD75%0, or 5 points taken from the treasury share15% (10% if the backstop is under target)10%
SunsetsunsetAt != 0100%00%0%

The move from regime 1 to regime 2 is a one-way permissionless latch, not a decision (Rule R-10.2):

function latchFeeSwitch() external;   // permissionless, one-way, when D_total exceeds 25 M

Guarantee

The fee switch is a state formula, not a governance action. Anyone can trigger it once the threshold is met; nobody can trigger it before, and nobody can reverse it. Its cost to depositors — about 1.35 points of yield the day it activates — is stated at launch rather than discovered later (Rule R-10.2).

Two guardrails on the split (Rule R-10.3, R-10.4):

  • The treasury share never exceeds 20%. This is a constructor assertion on the regime table and a fuzzed invariant; the formula does not approach it.
  • If the total of all Stability Pools falls below 30% of the fyUSD supply, the treasury share is redirected to the pools until the ratio returns above 40%. The hysteresis is evaluated at every route.

Backstop funding, by formula

How it is calculated

While Backstop.balance < 2% × D_total, 5 percentage points of every routed amount go to the backstop — taken from the pool share in regime 1, from the treasury share in regime 2. Above target, zero (Rule R-10.8).

The backstop is also funded by a permissionless fund() open to anyone, by an initial endowment from the founders, and by the proceeds of selling residual collateral on-chain (Rule R-6.8.3).

Invariant (Rule R-16.3 item 24). The four shares sum to one at every route; the treasury share never exceeds 20%; the backstop share is non-zero exactly when the balance is below target.

Pool incentives

10% goes to PoolIncentive, an immutable contract that pays in-range liquidity providers on at most two canonical pools whose addresses are fixed at construction (Rule R-10.5):

  • A fyUSD/USDG pool concentrated at ±0.5% — mandatory.
  • A rate-provider sfyUSD pool — optional at genesis, and if its hook is not audited in time, it does not exist in version 1 and cannot be added.

Providers stake their position NFT, checkpoint() accumulates in-range liquidity multiplied by time, and claim() is permissionless for one's own positions. After 180 days with no staked position at all, purge() routes the accumulated balance to the pools of activated branches pro rata their uncovered debt. There is never a fyUSD/ETH pool.

Rate floor

The floor is per tier and indexed to an on-chain reference, so it moves with the cost of dollars without anyone posting a number (Rule R-5.7.2):

How it is calculated

floor(tier) = max(floorFixed[tier], k[tier] × rRef()) with floorFixed = 1.5% (A) / 3.0% (B) and k = 0.5 (A) / 0.9 (B)

How it is calculated

rRef() = clamp(30-day TWA of the reference borrow rate, 2%, 6%), or 0 when the reference is unavailable

The reference is a Morpho Blue market whose loan token is USDG, selected seven days before deployment as the one with the largest borrowed assets, then frozen as an immutable address and market id. The rate is read through IIrm.borrowRateView. RateFloor.poke() writes one sample per day into a 30-slot buffer, permissionlessly, and is also called by open and setRate when the day's slot is empty.

The reference counts as unavailable — so rRef is zero and the fixed floors apply — when the call reverts, when the market's supplied assets fall below 10 M USD, when its last update is more than 30 days old, or when fewer than 15 slots are filled.

Worked values: at rRef = 3.6%, the floors are 1.8% (A) and 3.24% (B). At rRef = 6%, they are 3.0% and 5.4%. Below rRef = 3% the fixed floors dominate.

Guarantee

Failure of the reference is restrictive for nobody: the floor falls back to its fixed value. Manipulation of the reference is unprofitable: pushing that market's utilisation for thirty days costs real capital and only raises a bounded floor, with no gain to the attacker (Rule R-5.7.2).

Scope. The floor applies to choicesopen, an increase of debt, setRate and setRateFor — never retroactively. A position whose rate has fallen below the current floor keeps its rate, and its place at the front of the redemption queue, until its owner next increases risk or changes the rate. SortedTroves is never reordered by a change in the floor (Rule R-5.7.3).

An increase of debt on a position whose rate is under the floor reverts RateOutOfBounds: the borrower must call setRate first.

What the yield actually looks like

In regime 1, a pool depositor's yield is approximately (Rule R-10.7):

How it is calculated

0.90 × weighted average borrower rate × debt / pool, plus liquidation gains, plus mint fees, plus routed PSM fees

At a 5% average rate with a debt-to-pool ratio of 1.8, that is roughly 9.1%, of which about 0.23 points come from mint fees and about 0.40 from PSM fees, with liquidation gains adding 0.2 to 0.5 points in a calm year and 1 to 2 points in a crash. In regime 2 the same inputs give roughly 8.0%. With the pool cap at 100% of the debt, the structural floor is roughly 0.9 × average rate.

Risk

Every one of those points has an identifiable payer, and the number moves with them. It falls when the average rate falls, and it falls when the pool grows relative to the debt. Nobody promises it and no mechanism defends it.

Last reviewed: 2026-09-07 · Spec v0.4