Treasury Floor

Documentation snapshot 18 October 2026. Deployment · Audit Status
On this page

The Treasury records a reserve value and provides a bounded NOTE redemption path. Marked reserve value is not the same as immediately available USDG or a market-price guarantee.

Reserves

fact The Treasury holds USDG, whitelisted Stock Tokens, COUPON legs and NOTE. reserveValueQuote() values everything except NOTE in USDG:

Asset listing checklist. Only plain ERC-20 tokens are eligible as series underlyings (NoteCore.createSeries, RollPolicy.setTemplate) and as Treasury stock assets (addStockAsset): 18 decimals, no fee on transfer, no rebasing or elastic supply, no transfer pause that can outlast a series, and a uiMultiplier-aware feed. NoteCore._burnAndPay delivers the nominal stockOut computed from the books without a recipient balance-delta check, so a token that deducts a fee on transfer would deliver less stock than the core reports and the shortfall would fall on later redeemers.

Robinhood Stock Tokens meet the checklist; any new listing must be verified against it before governance registers the asset.

fact Reserves
AssetValuation
USDGPar
Stock TokensChainlink-style feed (addStockAsset), reverts IncompleteRound(token) if the latest round has updatedAt == 0 or answeredInRound < roundId, or InvalidPrice if the answer is not positive. A print older than maxFeedStaleness (26 hours by default) is not used directly: the module asks the oracle adapter for the market-calendar reference price (the official close of the last completed session, at most seven days back), so a Friday close read at the weekend or over a holiday still values, and reverts StaleFeed only when neither is available. In reserveValueQuote a stale asset whose holding is worth at most staleDustQuote (one whole quote unit by default, a permissioned setting) is carried at zero instead of reverting, so a dust balance of a delisted asset cannot freeze redemptions or the floors. The same dust rule applies to COUPON leg holdings: a Live leg whose feed has no print under maxFeedStaleness and no reference close is carried at zero in reserveValueQuote when its unit balance is at most staleDustQuote (COUPON units are quote-denominated with a factor capped at 1, so units bounds the holding's value); a larger holding still reverts StaleFeed(feed). withdrawLegs moves such dust out against the daily budget at par, and isLegStaleDust(seriesId) mirrors isStaleDust(token). A feed whose latestRoundData reverts or returns malformed data is treated like a stale feed: the reference close is used when available, otherwise the asset is stale (StaleFeed, or zero when the holding is dust). The same checks apply wherever the asset is valued: valueOf, reserveValueQuote, previewRedeem, withdrawERC20 and purchaseStock. isReferencePriced(token) reports whether the reference close currently stands in for a stale or unreadable print; the bond depository uses it to refuse stock-quoted purchases while the market is shut
COUPON legslegValueFactorWad: par (1 USDG per unit) when Autocalled, Matured unbreached or Unwound; while Live with the barrier intact on both the last observation and the latest fresh print, mark + (1 − mark) × min(1, (mark − barrier) / legBufferBps) with mark = min(1, liveMark / s0), liveMark = min(last observed price, latest fresh print within 30 minutes) and legBufferBps default 15% (setLegBufferBps), so a leg reaches par only once it sits at least the buffer above the barrier; while Live with the barrier breached mark; Matured breached at min(1, min(lastPrice, usablePrice) / s0), where usablePrice is the latest print under maxFeedStaleness or, failing that, the market-calendar reference close; when neither is available the maturity print alone is used, so the mark follows the stock down between maturity and harvestSeries and never exceeds the maturity print; zero in any other status. Series must be registered with registerLegSeries (owner or BondDepository). Accrued coupons and terminal payouts are pulled by the permissionless harvestSeries(seriesId), which measures the Treasury's quote and stock balance deltas around the core call and reports those measured amounts in SeriesHarvested(seriesId, couponOut, unitsRedeemed, quoteOut, stockOut); indexers can rely on stockOut as delivered stock, not nominal stock. When harvestSeries receives stock from a breached maturity or an unwound series, the Treasury registers that token as a Stock asset with the series' price feed if it is not registered already (AssetAdded), so the received stock is part of the reserve value and the withdrawal budget from the next block on. NOTE is never registered. Stock arriving by direct transfer remains unregistered until addStockAsset
NOTEExcluded (it is the liability side)

Floor and Redemption

fact Floor and Redemption Calculation

circulatingSupply = NOTE.totalSupply − NOTE.balanceOf(Treasury) floor = reserveValueQuote / circulatingSupply previewRedeem(n) = n × (reserveValueQuote − retainedHaircutQuote) / circulatingSupply × (1e4 − redemptionHaircutBps) / 1e4 retainedHaircutQuote = haircutRetainedStored × (1 day − elapsedSinceLastRedemption) / 1 day (0 after a full window) dailyRedemptionRemaining = reserveValueQuote × dailyRedemptionCapBps / 1e4 − redeemedInWindow

redeem(noteAmount, minQuoteOut) burns the caller’s NOTE and pays previewRedeem in USDG, provided it does not exceed dailyRedemptionRemaining() and the Treasury holds enough USDG (InsufficientLiquidity otherwise). The limiter is a leaky bucket: redemptionUsed() is the usage recorded at the last redemption, amortised linearly to zero over REDEMPTION_WINDOW = 1 day, so over any span t the Treasury pays at most cap × (1 + t / 1 day) and never more than cap in one burst.

fact Floor and Redemption Reference
ParameterValueHard maximum
redemptionHaircutBps0 in DeployToken.s.sol5,000 (50%)
dailyRedemptionCapBps200 (2%) in DeployToken.s.sol, as PROTOCOL_SPEC.md specifies10,000
maxFeedStaleness26 hours-

fact DeployToken.s.sol passes the two values in the order initialize expects (daily cap, then haircut) and reverts with TreasuryParamMismatch if the deployed Treasury does not read them back, so redemption is live from deployment at a 2% daily cap with no haircut. Both can be changed by the owner with setRedemptionParams(haircutBps, dailyCapBps). The examples below use a 2% haircut and 2% daily cap and are illustrative.

Worked Example

illustrative Reserves: 3,000,000 USDG + 4,000 NVDA tokens at 180.00 = 720,000 + 280,000 USDG of COUPON legs at par. reserveValueQuote = 4,000,000. Total NOTE supply 12,000,000 of which 2,000,000 is held by the Treasury; circulating 10,000,000.

illustrative Worked Example Reference
QuantityValue
Floor4,000,000 / 10,000,000 = 0.400 USDG per NOTE
Redemption of 100,000 NOTE at 2% haircut100,000 × 0.400 × 0.98 = 39,200 USDG
Daily allowance2% × 4,000,000 = 80,000 USDG
Redemptions possible today80,000 / 0.392 ≈ 204,081 NOTE
After redeeming 100,000 NOTEreserves 3,960,800; circulating 9,900,000; floor 0.40008

Redemption at a haircut slightly raises the floor for remaining holders, because the redeemer leaves 2% of their share behind.

Why a Cap and a Haircut

  • The daily cap prevents a run from draining the Treasury’s liquid USDG in one block while Stock Tokens and legs, which are less liquid, remain. Redeemers queue across days and the Treasury can rebalance.
  • The haircut makes redemption a floor rather than an arbitrage engine: NOTE has to trade below floor × (1 − haircut) before burning beats selling.
  • Haircut recapture. The haircut a redemption leaves behind stays in the reserve and therefore accrues to the remaining holders, including a redeemer who keeps part of a position. Splitting a redemption into tranches recaptures a share of the haircut through the later tranches; the closed form for a holder redeeming a fraction f of the circulating supply in k equal tranches at haircut h is a saving that grows with k and is bounded by f × h of the redeemed value. The Treasury removes this within a window: every redeem adds gross − quoteOut to haircutRetainedStored, decayed linearly to zero over REDEMPTION_WINDOW from lastRedemptionAt (the same leaky-bucket shape as redemptionUsed), and previewRedeem values against reserveValueQuote() − retainedHaircutQuote(). Within one window tranche splitting recaptures nothing; across windows the recapture is bounded by the daily cap times the haircut. Reserve value per circulating NOTE is non-decreasing after every redemption.
  • Liquidity: redeem pays only USDG. If USDG on hand is insufficient the call reverts; governance can sell Stock Tokens or wait for legs to settle. The floor is a valuation, not a guarantee of same-day liquidity for all reserves.

Reserve Flows

fact Reserve Flows Reference
InflowSource
Bond proceedsBondDepository (USDG, Stock Tokens, COUPON legs)
10% of protocol feesRevenueRouter
Desk returnsCOUPON coupons and principal from Desk positions
Stock Tokens bought from the Desk at feed valueDesk.liquidateToTreasury → Treasury.purchaseStock
fact Reserve Flows Reference
OutflowAuthority
NOTE redemptionPermissionless, capped
USDG paid to the Desk for Stock TokenspurchaseStock (Desk only, at feed value, StockPurchased); the Treasury's balance change must equal the amount sent, else TransferShortfall(expected, received)
withdrawERC20(token, to, amount), withdrawLegs(seriesId, to, units)governance owner only (Withdrawn, LegsWithdrawn); every registered reserve asset, enabled or disabled, and COUPON legs are valued through the reserve mark and consume the daily redemption budget shared with redeem (DailyCapExceeded); only tokens never registered leave the budget untouched. Withdrawing a disabled asset therefore needs a working feed; setAssetFeed(token, feed) (owner, Stock assets only, else AssetNotEnabled; feed decimals at most 18, else InvalidFeed; AssetFeedUpdated(token, previous, current)) replaces a feed that no longer prices
mintNote(to, amount)governance owner, within epoch cap (NoteMinted)

Governance withdrawal authority is the main trust assumption on the floor; see Governance for governance and guardian arrangements.

A little more clarity?

Keep exploring, or revisit this topic from the navigation.