Skip to content
Protocol

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

BranchTierStatus at genesis
SPY1Active
QQQ1Active
VTI1Dormant
GLD1Dormant
SLV2Dormant

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):

#CriterionVTI (tier 1)GLD (tier 1)SLV (tier 2)Read from
1Datenow ≥ DEPLOY_TS + 90 d+90 d+150 dblock.timestamp
2Depthd2P25() ≥ 1.0 M USD≥ 500 k≥ 500 k7-day buffer
3Buffer filledbufferFill() ≥ 84 of 168samesameLiquidityOracle
4Liquidity diversityat least 2 reference pools with d2_now ≥ 250 ksamesamepoolDepths()
5On-chain market cap≥ 8 M USD≥ 4 M≥ 4 MtotalSupply × price
6Source historyadapter deployed ≥ 90 d ago and longestDegraded() ≤ 24 hsamesameOracleAdapter
7Current stateRegime LIVE24 and no pending corporate actionsamesameOracleAdapter
8Pool depth of active branchesΣ active SP.totalDeposits ≥ 40% × fyUSD.totalSupply()samepools

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's C_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