Branches and versions
A closed registry, twenty branches deployed at genesis, eighteen dormant ones that activate on measured criteria, and how a version 2 replaces a version 1 without trapping anybody.
The set of collateral Fyber accepts is fixed at the deployment transaction and cannot be extended afterwards. BranchRegistry holds ten immutable slots; five are filled at genesis and five stay empty forever in version 1. There is no addBranch, no delist, no removeBranch — the functions do not exist in the bytecode (Rule R-12.3.1).
This is the same shape as Liquity V2's CollateralRegistry, and for the same reason: a key that can list a new collateral is a power of expansion over the issuance of the stablecoin, and that is precisely what a protocol claiming to have no identifiable issuer cannot hold.
The registry
| Branch | Tier | Status at genesis |
|---|---|---|
| SPY | 1 | Active |
| QQQ | 1 | Active |
| VTI | 1 | Dormant |
| GLD | 1 | Dormant |
| SLV | 2 | Dormant |
A dormant branch is fully deployed: its Branch, its OracleAdapter, its LiquidityOracle, its SortedTroves, its StabilityPool, its sfyUSD wrapper and its CollateralSale all exist and function. It is a registered minter of fyUSD from day zero. What it cannot do is mint, because D_max(t) is zero while activatedAt == 0 (Rule R-2.1.2, Rule R-5.4.1).
It does accept Stability Pool deposits, up to the seed cap. Those deposits sit idle, earning nothing, and remain withdrawable at any moment (Rule R-12.3.3).
Activation criteria
function activate() external; // permissionless
function canActivate() external view returns (bool, uint8 failingCriterion);activate() succeeds if and only if all eight criteria hold at the moment of the call, and every one of them is read on-chain (Rule R-12.3.2):
| # | Criterion | VTI (tier 1) | GLD (tier 1) | SLV (tier 2) | Read from |
|---|---|---|---|---|---|
| 1 | Date | now ≥ DEPLOY_TS + 90 d | +90 d | +150 d | block.timestamp |
| 2 | Depth | d2P25() ≥ 1.0 M USD | ≥ 500 k | ≥ 500 k | 7-day buffer |
| 3 | Buffer filled | bufferFill() ≥ 84 of 168 | same | same | LiquidityOracle |
| 4 | Liquidity diversity | at least 2 reference pools with d2_now ≥ 250 k | same | same | poolDepths() |
| 5 | On-chain market cap | ≥ 8 M USD | ≥ 4 M | ≥ 4 M | totalSupply × price |
| 6 | Source history | adapter deployed ≥ 90 d ago and longestDegraded() ≤ 24 h | same | same | OracleAdapter |
| 7 | Current state | Regime LIVE24 and no pending corporate action | same | same | OracleAdapter |
| 8 | Pool depth of active branches | — | Σ active SP.totalDeposits ≥ 40% × fyUSD.totalSupply() | same | pools |
canActivate() returns the first failing criterion, which is what a front end displays alongside the criterion's current value and its threshold (Rule R-15.1.13).
The criteria that cannot be read on-chain — "is this a leveraged or inverse ETF", "is this a pre-IPO name", "what is its 30-day volatility" — do not appear here. They are settled by the choice of the five tokens at genesis, which is itself frozen.
Guarantee
No key can force an activation and no key can prevent one. A branch whose metrics are never met stays dormant indefinitely, its pool deposits stay withdrawable, and nothing breaks. There is no deadline and no fallback (Rule R-12.3.4).
During dormancy, somebody has to call LiquidityOracle.poke() to fill the depth buffer — a front end, a watcher, or a prospective depositor. If nobody does, activation simply waits.
What activation changes
At activate() (Rule R-12.3.3):
activatedAt = now, and the branch'sC_abs(t)tiers start counting from that moment — 100 k USD for the first fourteen days.- The branch enters
branches(), which means it joins redemption routing, the aggregate TCR, the pro-rata share of routed PSM fees, and the pool-depth alert. - Dormant pool deposits begin receiving interest.
- Event
Activated(at).
The parameters of the dormant branches were frozen without twelve months of data, which is why the least-measured of them sits in tier 2: more collateral required, a lower depth multiple, a smaller absolute cap, a higher rate floor. That is the accepted price of a closed registry.
Why not a listing key
Three options were considered, and two rejected (Rule R-12.3.1):
A single-use listing key, bounded by a template, a minimum tier and a delay. Rejected: it is a power of expansion over issuance even when bounded, and the tier itself — "is this token an ETF or a leveraged product" — is not verifiable on-chain, so the bound is not mechanical.
Permissionless creation, Morpho-style. Rejected: in Morpho the risk of a market is borne by that market's own lenders. In Fyber the risk of a branch is borne by every holder of fyUSD. A branch with a complacent oracle would mint stablecoin with nothing behind it up to its cap, and every holder would pay for it.
A closed, pre-deployed set with automatic activation. Retained.
Version 2
Fyber v2 is a complete new deployment: a new stablecoin, new branches — including on the same collateral, with recalibrated parameters and possibly a different price source — a new PSM and a new Closer. It is exactly what Liquity did on 19 May 2025 (Rule R-12.4.1).
Guarantee
Version 1 does not die when version 2 launches. It stays solvent and redeemable, its caps stop rising beyond their final tier, its PSM keeps working, its interest keeps going to its own Stability Pools. A user may stay in version 1 indefinitely. There is no automatic migration of any position, because a position is a contract between a user and frozen code (Rule R-12.4.2).
Migration is by choice, in three shapes:
- Holders swap fyUSD v1 for fyUSD v2.
- Borrowers close in v1 and open in v2 — two transactions, or one through a third-party zap.
- Pool depositors withdraw and redeposit.
The PSM bridge
The mechanism that makes the holder swap liquid is specified for v2 and is permissionless (Rule R-12.4.3):
The version 2 PSM accepts fyUSD v1 as a reserve asset, at par, with zero entry fee, capped at a share of the v2 reserve — 20% for example. Version 2 then holds fyUSD v1, which is itself redeemable against v1 collateral and convertible into USDG at the v1 PSM.
The property that makes this acceptable: version 2 extends trust only to a contract that is immutable and solvent, whose behaviour it can verify by reading, not to an operator or a promise.
Risk
The bridge inherits every risk of version 1. A v2 reserve holding v1 fyUSD is exposed to v1's collateral, v1's oracle and v1's bad debt, bounded by the reserve share. The cap is what limits it.
Accelerating a migration
Urgent redemptions on version 1 open only through a shutdown. If the company wants to accelerate migration, it must call shutdownAll() before the Closer expires, or never (Rule R-12.4.4).
After month twelve, a live version 1 closes only through its own automatic triggers — two activated branches shut down, or an aggregate TCR below 130% for an hour — and migration becomes purely a matter of incentive: better parameters in v2, and a v1 whose caps no longer grow.
What can never happen
Verifiable by the absence of the functions in the deployed bytecode, and asserted by the deployment test (Rule R-12.2.3, Rule R-16.5.1):
- No sixth collateral, ever, on version 1.
- No relisting of a shut branch.
- No replacement of an oracle, a parameter or a minter.
- No new minter of fyUSD: the set is exactly the twenty branches and the PSM, checked at deployment and fuzzed.
- No Tier 3, no single-name collateral, in version 1.
Last reviewed: 2026-09-07 · Spec v0.4
Caps and liquidity
How much the protocol lends is a function of measured on-chain exit depth, not of a number anybody sets. The d2 measurement, the four-term ceiling, the ratchet, and the dated tiers.
Parameters
The complete table of constants, by tier, each marked as fixed at construction, computed by formula, or opened by a dated tier.