05 · Defined-risk options Modeled · not deployed

Fully collateralised calls and puts on Stock Tokens.

The options stack is five contracts: a permissionless series factory, a physically settled European series with exact-lot collateral, an EIP-712 quote book for maker-signed sales of long claims, an atomic router that pairs a spot purchase with a quote fill in one transaction, and a treasury that writes protocol-owned options. A long position can never lose more than the premium paid. A short position is always backed by escrowed collateral. None of it is deployed on Robinhood Chain: the app models the pair and refuses to execute it.

  • Status Contracts verified locally · chain deployment not started
  • Source contracts/src/StockOptionFactory.sol · StockOptionSeries.sol · OptionQuoteBook.sol · AtomicStockOptionRouter.sol · ProtocolOptionTreasury.sol
  • Feature flag FEATURE_FLAGS.nativeOptions = false
  • Revision September 2026

5.1 Purpose

Two positions are offered per series. A long call is the right, not the obligation, to buy the Stock Token at the strike during the exercise window. A long put is the right to sell it at the strike. In both cases the most a holder can lose is what they paid for the claim: the premium. There is no margin, no mark-to-market, no liquidation and no assignment. If the holder does nothing, the claim simply expires.

Every long claim exists only because a writer escrowed the full obligation up front. A call writer deposits the exact amount of Stock Token that exercise would deliver; a put writer deposits the exact strike value in the settlement asset. The series contract cannot become undercollateralised, and every state-changing call re-checks that its token balances still cover its accounted pools before proceeding.

PositionRightMaximum lossBacking
Long callBuy amount Stock Token for strike × amount settlement asset, within [expiry, exerciseEnd)Premium paidamount Stock Token in underlyingPool
Long putSell amount Stock Token for strike × amount settlement asset, within [expiry, exerciseEnd)Premium paidstrike × amount settlement asset in settlementPool
Short (either)Pro-rata share of both pools after exerciseEndEscrowed collateral less premium receivedIts own escrow
Physical

Settlement is physical and voluntary. There is no settlement oracle and no automatic exercise. A long holder must call exercise() between expiry and exerciseEnd; an in-the-money claim left unexercised expires worthless and its collateral returns to the shorts.

5.2 Series definition

StockOptionFactory.createSeries() is permissionless. Any account may deploy any previously unused parameter set; the parameters are hashed into a CREATE2 salt, so each parameter set has exactly one canonical address, computable in advance with predictSeries(). The factory records isSeries[address] = true, which the quote book and router use to reject impostor series.

optionTypeCall or Put. Fixes which asset is escrowed and which is delivered on exercise.
underlyingThe Stock Token. Must be a contract with exactly 18 decimals.
settlementAssetThe asset the strike is paid in. Must be a contract with exactly 18 decimals and distinct from the underlying. The router only accepts series whose settlement asset is WETH.
strikeWadStrike price in settlement asset per whole underlying unit, 18-decimal fixed point. Non-zero.
lotSizeMinimum tradable amount in underlying base units. Non-zero. Every write, close and exercise amount must be a whole multiple of it. The constructor requires strikeWad × lotSize to be an exact multiple of 1e18, so the strike value of one lot has no rounding.
writeEndLast moment to write. Requires block.timestamp < writeEnd at deployment.
expiryExercise opens. Requires writeEnd ≤ expiry. Closing a matched long + short pair is allowed only before this.
exerciseEndExercise closes. Requires expiry < exerciseEnd. After this, expireLong() and redeemShort() open.
eligibilityPolicyAn IEligibilityPolicy contract. Gates the writer and both receivers on write(), and gates every non-burn transfer of the long and short claim tokens.
longToken · shortTokenTwo ClaimToken ERC-20s created by the series constructor: SY-CALL-L / SY-CALL-S or SY-PUT-L / SY-PUT-S. Only the series can mint or burn them.
Option series lifecycle: write window, closed-to-writing, exercise window, settled Write window write() escrows + mints close() unwinds a pair Closed to writing close() still allowed claims transfer freely Exercise window long holder exercises close() blocked Settled redeemShort() shares expireLong() burns deploy now < writeEnd writeEnd ≤ expiry expiry < exerciseEnd exerciseEnd pools unlock
Fig 5.1Series lifecycle. The constructor rejects any window that is not now < writeEnd ≤ expiry < exerciseEnd. Lime phases are where new value can enter the series; close() remains available in every phase before expiry.

Collateral and pools

collateralFor(amount) returns the escrow for a lot-aligned amount: for a call it is amount of the underlying; for a put it is (amount / lotSize) × strikePerLot of the settlement asset, where strikePerLot = strikeWad × lotSize / 1e18. The series keeps two counters, underlyingPool and settlementPool, and every transfer in or out is balance-checked on both sides (_pullExact, _pushExact) so a fee-on-transfer or blocked Stock Token transfer reverts rather than silently under-delivering.

ActionWhoWhenAsset movementClaim movement
write(amount, longRx, shortRx)Any eligible account; receivers eligible< writeEndCollateral in from callerMint amount long to longRx, amount short to shortRx
close(amount, rx)Holder of both long and short< expiryCollateral out to rxBurn amount long and short
exercise(amount, rx) · callLong holder[expiry, exerciseEnd)Strike in (settlement asset); amount underlying out to rxBurn amount long
exercise(amount, rx) · putLong holder[expiry, exerciseEnd)amount underlying in; strike out to rxBurn amount long
expireLong(amount)Long holder≥ exerciseEndNoneBurn amount long
redeemShort(amount, rx)Short holder≥ exerciseEndamount / shortSupply of both pools out to rxBurn amount short

After exerciseEnd the pools hold a mix: unexercised collateral plus whatever exercise paid in. redeemShort() pays each short its pro-rata share of both pools; the final redeemer receives the exact remainder. Exercise is not subject to the eligibility policy, and burns are never gated, so an address later marked ineligible can always exit.

Flow of collateral and claims through a StockOptionSeries StockOptionSeries underlyingPool settlementPool call: escrows underlying put: escrows strike × lots solvency checked per call Writer · write() eligibility-gated in: collateral out: long+short Both legs · close() before expiry in: long+short out: collateral Long · exercise() [expiry, exerciseEnd) in: long, strike* out: underlying* Short · redeemShort() after exerciseEnd in: short out: pool share
Fig 5.2Value flow for one series. Lime edges carry escrowed assets. *Shown for a call; a put mirrors it: exercise delivers the underlying and receives the strike. Shorts redeem a pro-rata share of both pools once exercise has closed.

5.3 Quote book

OptionQuoteBook is permissionless settlement for maker-signed offers to sell long claims. A maker who already holds long tokens (from writing, or from buying) signs a Quote offchain; any buyer can fill it onchain, in whole or in parts, until the deadline, the lot cap or a cancellation stops it. The contract never custodies anything: premium moves buyer → maker and long tokens move maker → receiver in the same call.

EIP-712 domainname "StockYield OptionQuoteBook", version "1", chain ID and verifying contract from the deployment.
Type stringQuote(address maker,address series,address premiumToken,uint256 premiumPerLot,uint256 maxLots,uint64 deadline,uint256 nonce)
makerSigner and seller of the long tokens. Signature checked with SignatureChecker, so both EOAs and ERC-1271 contracts can make.
seriesMust have code and be registered in the factory (factory.isSeries). Its longToken().series() must point back to it.
premiumTokenAny ERC-20 contract. The router additionally requires WETH.
premiumPerLotPremium per lot in premiumToken base units. premiumFor(quote, lots) = lots × premiumPerLot.
maxLotsCumulative fill cap. filledLots[digest] tracks partial fills; a fill that would exceed the cap reverts.
deadlineUnix seconds. block.timestamp > deadline reverts.
nonceMust be ≥ minimumNonce[maker]. Makers raise the floor with invalidateNoncesBefore(n) to void every older quote at once; cancel(quote) voids one digest.

fill(quote, lots, receiver, signature) checks, in order: non-zero receiver that is neither the maker nor the book; non-zero lots; deadline; nonce floor; digest not cancelled; signature; series has code and is registered; premium token has code; series terms (non-zero lotSize, exercise not ended, long token backlink, series solvency); fill cap. It then reserves the fill, transfers lots × premiumPerLot from msg.sender to the maker and lots × lotSize long tokens from the maker to the receiver, verifying exact balance deltas on both legs. Any failure reverts the whole fill, including the reservation.

Eligibility

ClaimToken.approve() requires the approver to be eligible, and every non-burn transfer requires both sender and receiver to be eligible. A quote can therefore only settle to an eligible receiver. The book itself is never a holder and needs no eligibility.

5.4 Atomic router

AtomicStockOptionRouter buys spot and option together. The caller sends ETH equal to spotAmountIn + premium; the router wraps the premium, fills the quote through the book, then swaps the spot input through a Router02-compatible swap router. Both legs land in one transaction or neither does. The contract holds no custody and has no administration surface: it is bound at construction to one swap router, one WETH, one Stock Token, one quote book, one series factory, one pool fee and the chain ID it was deployed on.

Before spending anything the router checks: current chain equals deploymentChainId; receiver is non-zero and not the router; spot input, minimum stock out and option lots are non-zero; sqrtPriceLimitX96 is zero; transaction and quote deadlines have not passed; the quote's premium token is WETH; the quote's series is registered with the factory and has code; the series' underlying is the bound Stock Token and its settlement asset is WETH. It then computes the premium via quoteBook.premiumFor and requires msg.value to equal it plus the spot input exactly.

Sequence of an atomic spot plus option purchase through AtomicStockOptionRouter Buyer / receiver sends ETH AtomicStockOption Router OptionQuoteBook EIP-712 · nonces Maker holds long claims Router02 (spot) WETH → Stock Token Before the fill write() → long+short signs EIP-712 Quote pairNative{value} spot + premium _validatePair() chain · deadlines WETH · isSeries() wrap premium → WETH approve quote book fill(quote, lots, sig) verify quote sig · nonce · expiry maxLots · solvency premium WETH → maker long claims → receiver exactInputSingle{spot} Stock Tokens → receiver no residual allowance = 0 WETH · ETH unchanged Any revert on any leg unwinds the whole transaction
Fig 5.3One pairNative() call. The maker's collateral was escrowed and the long + short claims minted earlier by write(); the router only moves the premium and the already-minted long claims, then buys spot. Lime edges are asset transfers. The router ends with zero allowance and unchanged WETH and ETH balances or it reverts.

The spot leg goes through exactInputSingle with tokenIn = WETH, tokenOut = stockToken, the bound fee tier, and the caller's minimumStockOut. The router verifies the swap router's native balance did not change, that the receiver's Stock Token balance rose by at least the minimum, and that the reported output equals the observed delta. The struct passed in is PairParams { spotAmountIn, minimumStockOut, sqrtPriceLimitX96, transactionDeadline, quote, optionLots, receiver }.

5.5 Payoff at expiry

Because the long claim is a right with no further obligation, its P&L at expiry is bounded below by the premium. The break-even is the strike plus the premium for a call, the strike minus the premium for a put. Upside for a call is uncapped; upside for a put is capped at strike minus premium, reached only if the Stock Token goes to zero.

Payoff at expiry for a long call and a long put; maximum loss equals the premium Long call 0 breakeven = K + premium max loss = premium K Stock Token price at expiry Long put 0 breakeven = K − premium max loss = premium K Stock Token price at expiry
Fig 5.4Long-claim P&L at expiry versus the Stock Token price. The dashed floor is the premium paid; the payoff never goes below it. K is the strike.

The short side is the mirror: it keeps the premium if the claim expires out of the money and otherwise delivers the escrowed asset at the strike. Its loss is bounded by the collateral it already posted, which is why no liquidation path exists.

5.6 Treasury writer

ProtocolOptionTreasury is an owner-operated (Ownable2Step) vault that writes protocol-owned options and accumulates Stock Tokens. It is the one contract in the stack with an administrator.

wethImmutable. The asset harvested to stakers and spent on spot purchases.
distributorSettable by the owner. A contract exposing notifyRewardAmount(uint256); the launch plan names RealYieldDistributor.
writeOption(series, lots)Owner only. Reads collateralFor, approves the underlying (call) or settlement asset (put) to the series, and calls write with the treasury as both long and short receiver. The argument is forwarded unchanged as the series amount, so it must be a lot-aligned base-unit quantity.
buyStockToken(router, token, fee, wethIn, minOut)Owner only. exactInputSingle WETH → Stock Token on a Router02-compatible router, output to the treasury.
harvestToStakers(amount)Owner only. Approves and pushes up to the WETH balance into the distributor.

Because the treasury receives both claim legs, writing does not by itself create a market position; the treasury becomes a maker by signing quotes on the long tokens it holds, or it closes the pair. The treasury and the token-launch plan that would fund it are not deployed; see $SYIELD token & fees.

5.7 Current status

Modeled

The options stack is not deployed on Robinhood Chain. No factory, series, quote book, router or treasury address exists in the app's registry, and Deployed contracts lists none. The interface's Pair Lab is labelled Modeled, not traded; its status strip reads Execution: disabled and Contracts: verified locally. The feature flag FEATURE_FLAGS.nativeOptions is false and the Learn page reports native option execution as disabled.

Pipeline stepScopeState
Core contractsFactory, Series, ClaimToken, QuoteBook, AtomicRouter verified locallyComplete
Chain deploymentRobinhood Chain 4663 deterministic contract deploymentNot started
Series & collateralEuropean call/put series with fully escrowed writer collateralWaiting
Maker liquidityEIP-712 signed maker quotes with nonces, deadlines and lot limitsWaiting
Atomic executionOne-transaction spot swap + quote fill; both legs settle or neither doesWaiting

What the Pair Lab does today is arithmetic: it takes a Stock Token entry price, a strike and a premium the user types, and draws the modeled P&L of the pair against a stock-only benchmark, labelling the floor Modeled max loss. It signs nothing, approves nothing and submits nothing. Any figure it shows is a scenario, not a quote, and no series exists to fill one.

5.8 Risks

  • Not deployed. Everything above describes source that has only been exercised locally. Behaviour on chain, gas, and interaction with the live Stock Tokens have not been observed in production.
  • Voluntary exercise. There is no oracle and no auto-exercise. A long holder who misses [expiry, exerciseEnd) forfeits an in-the-money claim; its collateral is redeemed by the shorts.
  • Issuer controls. The underlying is an issuer-controlled Stock Token that can be paused, blocked, burned or upgraded. A blocked transfer makes write, close, exercise and redeemShort revert through the exact-transfer checks; escrowed collateral stays in the series until transfers succeed again.
  • Eligibility. Writing and every non-burn claim transfer are gated by an IEligibilityPolicy chosen at series creation. If it is an owner-managed policy, the owner decides who can enter; exits (burns) are never gated.
  • Mixed pools at settlement. Shorts redeem a pro-rata share of both the underlying and settlement pools, so a short's payout is a blend of unexercised collateral and paid-in strike, not a fixed asset.
  • Maker signatures. A signed quote is live until it expires, is cancelled, or the maker raises the nonce floor. A maker who loses key control must call invalidateNoncesBefore from that key.
  • Treasury owner. The treasury writer is owner-operated; its owner can direct collateral into series, buy spot, and route WETH to the distributor it sets.
  • Native only. The router accepts only ETH value and only series settled in WETH on the Stock Token it was bound to; other pairs need another router deployment.