Skip to content

Series lifecycle

fact INoteCore.Status { None, Subscription, Live, Autocalled, Matured, Cancelled }

Subscriptiondeposits open Liveobserving Autocalledprincipal + coupon Maturedcash or physical Cancelledall refundable Settledescrow = 0 finalizeStrike observe: P ≥ autocall observe: last index D = 0 or S = 0 redeem / refund
"Settled" is not an on-chain status. It is the state in which every claim, redemption and refund has been pulled and escrow is zero. Autocalled and Matured are terminal.

createSeries(SeriesParams p) is an onlyOwner call (pausable). _validateParams reverts InvalidParams(reason) unless:

  • underlying has 18 decimals and feed has 8 decimals; neither is the zero address.
  • 0 < barrierBps <= autocallBps (reason "barrier").
  • couponFloorBps <= couponCapBps <= maxCouponCapBps (400) and couponCapBps > 0 ("coupon bounds"); refBps > 0 ("refBps"). refBps is not required to lie inside the floor/cap band; discovery clamps it.
  • notionalCap > 0, minTicket > 0, minTicket <= notionalCap ("notional").
  • observations has 2 <= length <= maxObservations (64), is strictly ascending, and every timestamp passes MarketCalendar.isOfficialClose.
  • subscriptionEnd > block.timestamp and observations[0] > subscriptionEnd.

Fees in force at creation (couponFeeBps, notionalFeeBps) are snapshotted into the series and never change afterwards. Status becomes Subscription.

CallWhoEffect
depositCoupon(seriesId, quoteAmount)Anyone, >= minTicket (BelowMinTicket)Transfers USDG into escrow, increments totalCouponDeposits (D)
depositShield(seriesId, stockAmount, prefundQuote)AnyoneTransfers Stock Tokens and USDG prefund; prefundQuote >= requiredPrefund(seriesId, stockAmount) (valued at the provisional price, else InsufficientPrefund)
Desk.fillCoupon(seriesId, amount)Governance via Desk, only when S > DDesk subscribes to COUPON under its caps

Deposits are final until strike. There is no withdrawal during Subscription; unmatched amounts are refundable after strike. Deposits revert after subscriptionEnd (SubscriptionClosed).

finalizeStrike(seriesId) is permissionless once block.timestamp >= observations[0].

  1. If D == 0 or S == 0 or the strike observation was cancelled by governance, or the computed match N == 0: status Cancelled (SeriesCancelled), everything refundable.
  2. Otherwise read s0 via OracleAdapter.observe(feed, observations[0]). If the oracle returns ok == false, emit StrikeDeferred and stop; anyone may retry.
  3. Compute S at s0, N = min(D, S, notionalCap, prefundCapacity), and couponBps (see Coupon discovery).
  4. Book matchedNotional, stockMatched, prefundRemaining, the notional fee into accruedFees, unmatched amounts as refundable. Status Live, nextObs = 1.
  5. Credit the keeper reward to the keeper (keeperOwed), pulled later with claimKeeperReward() so a rejecting keeper address can never block the observation.

Legs are minted lazily: settle(seriesId, account) (permissionless, callable for any account) computes the account’s pro-rata match, mints COUPON or SHIELD units to it and books its refund. claim, redeem and refund settle the caller first.

observe(seriesId) processes observations[nextObs] once block.timestamp >= closeTs. Outcomes are described in Observation. Each successful observation increments nextObs and pays the keeper. A Deferred observation leaves nextObs unchanged.

  • Autocalled (intermediate observation, P >= autocall): coupon accrued, shieldUnusedPerUnit fixed from prefundRemaining.
  • Matured (last observation): if barrier holds, coupon accrued and breached = false; else breached = true. A maturity observation cancelled by governance settles at lastPrice.
CallAvailable whenPays
claim(seriesId)Live, Autocalled, MaturedAccrued net coupons on the caller’s COUPON units
redeem(seriesId, units)Autocalled, MaturedCOUPON: 1 USDG per unit, or Stock Tokens at s0 if breached
redeemShield(seriesId, units)Autocalled, MaturedSHIELD: matched Stock Tokens back plus unused prefund per unit, or 1 USDG per unit if breached
refund(seriesId)Live, Autocalled, Matured, CancelledUnmatched COUPON USDG, unmatched Stock Tokens, unmatched prefund
sweepFees(seriesId)Any statusaccruedFees to feeSink

All accounting is pull-based. There is no deadline for redemption; escrowed assets wait for their holder.

Each module has a guardian that can pause but not unpause, and an owner (timelock multisig) that can unpause. createSeries, depositCoupon, depositShield, finalizeStrike and observe are pausable; claim, redeem, redeemShield, refund, settle and sweepFees are not, so holders can always exit; note that pausing observation delays coupons and may push an observation past settlementWindow, in which case the OracleAdapter’s walk-back still selects the same closing print as long as fewer than maxRoundWalk rounds were published since. See Governance.