Series lifecycle
Status machine
Section titled “Status machine”fact INoteCore.Status { None, Subscription, Live, Autocalled, Matured, Cancelled }
1. Creation
Section titled “1. Creation”createSeries(SeriesParams p) is an onlyOwner call (pausable). _validateParams reverts InvalidParams(reason) unless:
underlyinghas 18 decimals andfeedhas 8 decimals; neither is the zero address.0 < barrierBps <= autocallBps(reason"barrier").couponFloorBps <= couponCapBps <= maxCouponCapBps(400) andcouponCapBps > 0("coupon bounds");refBps > 0("refBps").refBpsis not required to lie inside the floor/cap band; discovery clamps it.notionalCap > 0,minTicket > 0,minTicket <= notionalCap("notional").observationshas2 <= length <= maxObservations(64), is strictly ascending, and every timestamp passesMarketCalendar.isOfficialClose.subscriptionEnd > block.timestampandobservations[0] > subscriptionEnd.
Fees in force at creation (couponFeeBps, notionalFeeBps) are snapshotted into the series and never change afterwards. Status becomes Subscription.
2. Subscription
Section titled “2. Subscription”| Call | Who | Effect |
|---|---|---|
depositCoupon(seriesId, quoteAmount) | Anyone, >= minTicket (BelowMinTicket) | Transfers USDG into escrow, increments totalCouponDeposits (D) |
depositShield(seriesId, stockAmount, prefundQuote) | Anyone | Transfers 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 > D | Desk 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).
3. Strike
Section titled “3. Strike”finalizeStrike(seriesId) is permissionless once block.timestamp >= observations[0].
- If
D == 0orS == 0or the strike observation was cancelled by governance, or the computed matchN == 0: statusCancelled(SeriesCancelled), everything refundable. - Otherwise read
s0viaOracleAdapter.observe(feed, observations[0]). If the oracle returnsok == false, emitStrikeDeferredand stop; anyone may retry. - Compute
Sats0,N = min(D, S, notionalCap, prefundCapacity), andcouponBps(see Coupon discovery). - Book
matchedNotional,stockMatched,prefundRemaining, the notional fee intoaccruedFees, unmatched amounts as refundable. StatusLive,nextObs = 1. - Credit the keeper reward to the keeper (
keeperOwed), pulled later withclaimKeeperReward()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.
4. Live
Section titled “4. Live”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.
5. Autocalled or Matured
Section titled “5. Autocalled or Matured”- Autocalled (intermediate observation,
P >= autocall): coupon accrued,shieldUnusedPerUnitfixed fromprefundRemaining. - Matured (last observation): if barrier holds, coupon accrued and
breached = false; elsebreached = true. A maturity observation cancelled by governance settles atlastPrice.
6. Claims, redemptions, refunds
Section titled “6. Claims, redemptions, refunds”| Call | Available when | Pays |
|---|---|---|
claim(seriesId) | Live, Autocalled, Matured | Accrued net coupons on the caller’s COUPON units |
redeem(seriesId, units) | Autocalled, Matured | COUPON: 1 USDG per unit, or Stock Tokens at s0 if breached |
redeemShield(seriesId, units) | Autocalled, Matured | SHIELD: matched Stock Tokens back plus unused prefund per unit, or 1 USDG per unit if breached |
refund(seriesId) | Live, Autocalled, Matured, Cancelled | Unmatched COUPON USDG, unmatched Stock Tokens, unmatched prefund |
sweepFees(seriesId) | Any status | accruedFees to feeSink |
All accounting is pull-based. There is no deadline for redemption; escrowed assets wait for their holder.
Pausing
Section titled “Pausing”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.