Stability Pool
Product-sum accounting, offsets, the ERC-4626 wrapper, the collateral sale that keeps shareholders in fyUSD, and the one-transaction USDG route.
Each branch has its own Stability Pool. Depositors put fyUSD in; the pool absorbs liquidated debt from that branch by burning deposits and receiving the seized collateral plus its bonus; and the interest paid by that branch's borrowers is routed to it. There is one pool per branch, never a shared one, because the risk of a branch is not a risk its depositors should be able to spread onto other branches' depositors.
Deposit and withdrawal
function provide(uint256 amount) external;
function withdraw(uint256 amount) external; // never pausable
function claimCollateral() external;Three routes into the pool exist (Rule R-1.2.1): StabilityPool.provide with fyUSD; sfyUSD.deposit with fyUSD, receiving ERC-4626 shares; or Router.depositUSDG, which does PSM.swapIn and sfyUSD.deposit in one transaction. A dormant branch accepts deposits — they sit idle, earning nothing, until the branch activates.
Guarantee
Withdrawal from the Stability Pool is never blocked, in any market state, under any Closer freeze. The only revert condition is being called in the same block as an offset, which prevents an accounting race, and it clears in the next block (Rule R-9.1.2, Rule R-9.2.4).
provide and sfyUSD.deposit have one extra condition, added in version 0.4: they revert while a Closer liquidation freeze is running on the branch (Rule R-9.1.1, decision D87). A freeze is the protocol saying it does not currently know whether liquidating is correct, and new capital should not be drawn into that. The same decision spaced two liquidation freezes on one branch at least 7 days apart, so the three a branch may ever receive can no longer be chained across a single weekend while depositors are leaving. Withdrawal is untouched, as always.
Collateral claims are equally unconditional. A direct depositor receives seized stock tokens in kind through claimCollateral(), with no obligation to sell them (Rule R-1.2.3).
Caps
cap() = max(spSeed = 500 000 USD, 1.0 × branch.getEntireDebt())
capPerAddress() = 100 000 USD until DEPLOY_TS + 90 days, then unlimitedThe pool cap is 100% of the branch's debt, floored at a seed value so a branch can be bootstrapped and a dormant branch can accept deposits (Rule R-9.1.1). This is an economic choice as much as a risk one: a pool equal to the debt absorbs 100% of a liquidation event, and the cap guarantees a structural floor under the yield, because the yield is the pool's share of interest divided by the pool size.
Compounded yield may carry totalDeposits above the cap; only new deposits are refused. The per-address cap during the first 90 days makes early concentration visible and is a formula of time, not something anyone lifts.
Product-sum accounting
The pool uses Liquity-style product-sum accounting with epochs and scales, which lets thousands of depositors share a proportional loss and a proportional gain without iterating over them (Rule R-9.2.1 to R-9.2.5).
Three running values: P, the deposit factor; S, cumulative collateral gain per unit; B, cumulative fyUSD gain per unit. Each depositor stores a snapshot of (P, S, B, epoch, scale) at every touch.
An offset of debt against coll:
S += coll × P / totalDeposits_before
P *= (1 − debt / totalDeposits_before)
totalDeposits -= debt
if P falls below 1e9 → scale change
if totalDeposits reaches 0 → epoch++, P = 1e18A yield trigger of amount:
B += amount × P / totalDeposits // if totalDeposits is 0, the amount is held and paid at the next provideReading a depositor's position:
compounded deposit = d × P / P_snap
collateral gain = d × (S − S_snap) / P_snap
fyUSD gain = d × (B − B_snap) / P_snap // compounded into the deposit at each touchInvariants (Rule R-9.2.6). The pool's fyUSD balance is at least totalDeposits plus pending yield; its token balance is at least the sum of unclaimed collateral gains; an offset reduces the branch's debt and collateral by exactly the amounts burned and transferred.
What a depositor gains and loses
A depositor's fyUSD balance falls by the debt absorbed and rises by the collateral received, valued at the price used for the liquidation plus the bonus. In an ordinary liquidation at a 5% bonus, the pool comes out roughly 5% ahead on the amount absorbed, in stock token rather than in fyUSD.
Risk
A depositor can end up holding stock token instead of fyUSD, and can end up worse off if the
seizure was capped by the position's collateral. When a position's ICR falls below 1 + bonus,
the seizure is capped at the collateral available and the pool absorbs the shortfall. Below 100%,
the pool takes an outright loss on that position. This is the risk the yield pays for (Rule
R-6.2.2).
There is no cap on how much the pool can absorb in 24 hours. A cap would leave positions unliquidatable while the market moved further away (Rule R-9.3.2).
sfyUSD: the ERC-4626 wrapper
One wrapper per pool, deployed at genesis for all five branches, symbol sfyUSD-<TICKER>. Underlying asset: fyUSD. No management fee, no performance fee, no lock (Rule R-2.2.1, Rule R-9.5.1).
The wrapper exists so that a depositor can hold a single appreciating token instead of managing a deposit and a collateral claim. fyUSD yield compounds into the share price. The wrapper's share of seized collateral is sold rather than distributed, so a shareholder never has to handle a stock token unless they want one.
function totalAssets() external view returns (uint256);
function redeemInKind(uint256 shares, address receiver) external;
function harvest() external; // permissionless, and implicit in deposit, withdraw and redeemtotalAssets() is the wrapper's pool deposit, plus its pending fyUSD gain, plus the inventory sitting in CollateralSale valued conservatively:
How it is calculated
p_val = min(view.pRef, view.pLiq) × (1 − discount), where the discount equals the tier's
B_BASE: 5% on tier 1 and 5% on tier 2.
The minimum of the two prices is pRef, and taking it there rather than at pLiq alone is a version 0.4 correction (decision D82). pLiq sits at the official print until a fall is confirmed, so on a Saturday where the on-chain price had dropped 15% without confirmation the share price was overstated by 1.8%. That is a premium paid to whoever redeems first and a loss to whoever redeems last, and the race it starts drains the PSM reserve as well. pRef recognises a visible fall without waiting for confirmation, which is exactly what a share price should do (invariant 23).
The wrapper is a single depositor in the pool, so the per-address cap does not apply to it; until day 90, an equivalent 100 000 USD per-address limit is applied at the share level by the same dated formula (Rule R-9.5.2).
harvest() is permissionless and also runs at the head of every deposit, withdrawal and redemption: it claims the fyUSD gain and redeposits it, hands the collateral gain to CollateralSale, and redeposits the fyUSD the sale has produced. Its caller is not paid, because the call is implicit in every interaction anyway (Rule R-9.5.3).
CollateralSale
One per wrapper. It sells the wrapper's seized collateral for fyUSD at a discount that ramps up and then stops (Rule R-9.5.4):
function start(uint256 amount) external; // onlyWrapper
function buy(uint256 collAmount, uint256 maxFyIn) external; // permissionless
function discount() external view returns (uint256);
function inventory() external view returns (uint256);- The discount ramps linearly from 0 to 3% over six hours from the start of a lot, then stays at 3% until the inventory clears. There is no fallback to a DEX swap, so the sale never depends on a thin pool and can never be sandwiched into a bad price.
- A new contribution to a non-empty lot does not restart the ramp.
- Sales run only when the branch has an official price fresh enough to be
LIVE. A wrapper never sells collateral on an estimate. - Throughput: at most
1.0 × d2_effper hour, in a bucket distinct from the liquidation bucket, and0.25 × d2_effper transaction. - The proceeds go to the wrapper and are redeposited at the next harvest.
The buyer is a keeper whose only compensation is the discount. The product is supply-neutral: the protocol creates no selling pressure on the stock token beyond the buyer's own decisions.
Redeeming while collateral is being sold
A wrapper redemption delivers fyUSD as long as the inventory in CollateralSale is at most 20% of totalAssets(). Above that, the excess portion is delivered in kind pro rata — fyUSD plus stock token, valued at p_val. redeemInKind(shares) always delivers the exact pro-rata share for anyone who wants the collateral (Rule R-9.5.6).
Guarantee
Wrapper redemption is never blocked. In the worst case it delivers a mixture of fyUSD and stock token instead of fyUSD alone, but it always delivers.
Router: USDG in, USDG out
Stateless, feeless, roleless (Rule R-9.6):
function depositUSDG(address branch, uint256 usdg, address receiver, uint256 minShares)
external returns (uint256 shares);
function withdrawToUSDG(address branch, uint256 shares, address receiver, uint256 minUsdg)
external returns (uint256 usdg, uint256 fyLeft);depositUSDG runs PSM.swapIn at 0.05% and then sfyUSD.deposit. If the PSM is at its cap, it reverts with a clear message — there is no DEX fallback, no hidden slippage.
withdrawToUSDG runs sfyUSD.redeem and then PSM.swapOut at 0.20%. If the PSM reserve is insufficient, it converts what it can and returns the remaining fyUSD to the receiver; any stock token from an in-kind delivery is transferred as is. The round trip costs 0.25%.
Yield, stated with its formula
Two numbers are shown, never one (Rule R-9.4.1):
Current, prospective. pool share × weighted average rate (7-day TWA) × debt / pool, read on-chain and displayed with the arithmetic spelled out.
Realised, 30-day. (pricePerShare_t / pricePerShare_{t−30d} − 1) × 365/30 on the wrapper share, which includes interest, routed fees and liquidation gains actually sold. Collateral still held is valued at the discount and flagged separately.
Both are accompanied by the statement that the number falls if borrowers pay less or if the pool grows, and that nobody promises it. External incentives, if any ever exist, are shown in a separate block and never folded into these figures.
A third block is mandatory, on the depositor's own risk (Rule R-9.4.1, decision D90). At the weekend the pool buys nothing. On Sunday at 20:10 ET it absorbs, in hourly slices, everything the weekend made liquidatable, at the first official price of the evening — one provider, roughly 1% of a session's volume. On a −20% weekend that can be more than half the branch's debt inside a few hours, converted into stock tokens that CollateralSale then sells at up to a 3% discount. If the on-chain market sits below that, the inventory does not sell and a withdrawal above 20% of assets is delivered in kind.
Guarantee
"No cliff" is a guarantee to the borrower, whose threshold never moves and who is never swept. It is not a guarantee to the depositor, who holds the other side of exactly that. The protocol is sized as though the open sale did not exist: buckets, threshold tolerance and pool size all assume the pool absorbs the whole weekend on Sunday evening (Rule R-6.5.4).
What a depositor may expect, by phase
Three phases, with what carries each and what nobody guarantees (Rule R-15.5.2). These are expectations, not targets and not promises, and the interface never displays them in place of the two computed numbers above.
| Phase | Conditions | Expected | What carries it | What is not guaranteed |
|---|---|---|---|---|
| Launch, day 0 to about month 3 | Pool small, debt over pool 2 to 2.5, average rate 3 to 4% | 6% to 10%, volatile | 90% of interest to the pool, coded; mint and PSM fees routed, coded; no emissions, coded | That debt over pool stays above 1.5. Every deposit lowers it. That the average rate clears 3%. |
| Cruise, month 3 to month 12 | Debt over pool 1.1 to 1.4, average rate 3 to 4%, liquidation gains 0.2 to 0.5 points | 3% to 5% | Indexed rate floors, coded; pool cap at 100% of debt, giving a floor of 0.9 × rate, coded; routed PSM fees, coded | The average rate, which is a market outcome. That 45% of supply lives outside the pool. Absence of bad debt. |
| Saturation, pool at its cap | Debt over pool approaching 1, average rate at the floor | 1.5% to 3% | The same structural floor of 0.9 × rate, coded; exit at par through the PSM while the reserve holds, coded | Nothing. This is the regime where depositors leave for USDG and the pool shrinks on its own. |
What does not depend on any future decision: the pool's share of interest, the rate floors, the pool cap formula, the fee routing, and the absence of emissions or subsidy. What is a market outcome: the average borrower rate, which is the only lever that really moves the number (one point of average rate is 0.9 to 1.6 points here), the share of fyUSD used outside the pool, and how often liquidations happen.
Six things are not guaranteed, and are stated rather than implied (report 05 §8.3):
- The level. None of the three ranges is promised. The two-number display stays the only permitted form.
- How long the launch phase lasts. It ends when depositors arrive, so the better the return the sooner it ends.
- The average rate. Below 3% the product is merely adequate for a depositor; at 1.5 to 2% it is a good deal for the borrower and 2 to 3% for the depositor. Both sides are served together only in a narrow band around 3.5 to 4%, which nothing in the protocol targets.
- That fyUSD finds use outside the pool. Without it, saturation is the natural resting state.
- The risks that sit outside the number. A contract with no history; up to 30% exposure to the USDG issuer through the PSM; collateral deliverable in kind above 20% of assets; and the Sunday-evening absorption described above. The share price is computed on
min(pRef, pLiq), which sees a fall the on-chain market shows and nothing it does not. - Anything paid outside the protocol. Third-party rewards, if they ever exist, sit outside this figure and are shown separately.
Risk
Every unit of this yield is paid by a borrower on the same branch, out of interest they owe at a rate they set themselves. When borrowing demand falls, the yield falls with it, immediately and by construction. The figure of about 9% that circulated in earlier drafts describes a launch with a small pool, and not a regime.
Last reviewed: 2026-09-07 · Spec v0.4