How AfterHours works
A fully collateralized, cash-settled downside-protection market (European puts) for Robinhood Chain Stock Tokens, priced entirely onchain by a Rust/Stylus engine.
The weekend-gap thesis
Tokenized stocks like TSLA, AMZN and NVDA trade onchain 24/7, but their Chainlink price feeds follow the equity session: they print 24/5 and go dark from Friday 20:00 ET until Sunday 20:00 ET. Anyone holding a Stock Token over the weekend is exposed to whatever happens between the last Friday print and the first print after the reopen, with no venue to hedge it. That gap is exactly what AfterHours prices and pays out.
The market treats Saturday 00:00 UTC to Monday 01:00 UTC as its closed window. That is the union of the dark period in US daylight time (Sat 00:00 to Mon 00:00 UTC) and in standard time (Sat 01:00 to Mon 01:00 UTC), so it stays conservative across DST changes. No protection is sold inside the window and no series may expire inside it, so every expiry is followed by a live print.
In practice the feeds stop printing at the Friday regular-session close, around 20:00 UTC, and the next print is the Monday reopen. A series expiring on Friday evening would be priced with no closed-market time yet settle on that Monday print, so this app treats Friday 20:00 UTC to Monday 01:00 UTC as dark and never offers an expiry inside it. Its presets land on a listed grid (weekday 19:00 UTC, or Monday 13:30 UTC) so buyers join the same series instead of opening a new one on every purchase; you can also join any open series directly from the Protect tab.
Lifecycle
- 1. Buy. Pick an underlying, a whole-dollar strike (e.g. 90% of spot on a $1 tick) and an expiry outside the closed window. The market checks that the feed is fresh, valid and not paused, then asks the Stylus pricer for a premium. The pricer must price off the same spot as the feed. The market pulls the premium in tUSD and has the underlying's writer vault lock strike × tokens of collateral, booking the premium as unearned. You receive an ERC-1155 position keyed by (underlying, strike, expiry); one unit protects one Stock Token.
- 2. Settle. After expiry, anyone calls settle(id). The settle price is the feed's first valid print at or after expiry, found by walking back from the latest round. If the feed has not printed since expiry yet, settle reverts with AwaitingPostExpiryPrint. Once the series' own grace period has passed (snapshotted when the series is created, 5 days by default), the latest valid price is used so collateral never strands. If the walk back takes more than 300 rounds or hits a gap, settle reverts with SettleWalkTooLong and the keeper settles with settleAt(id, roundId), a round hint the market verifies. At settlement the vault unlocks all of the series' collateral, earns its premium and sends the payout owed to holders into the market's escrow.
- 3. Claim. Burn your units and receive your pro-rata share of the escrow, which is max(strike − settle, 0) per token. The last claimant receives the remainder, so claims add up to exactly what was escrowed. Out-of-the-money positions have nothing to claim: their collateral already went back to the writers at settlement.
Pricing
The pricer reads the underlying's own round history from the feed, computes realized volatility over the lookback window, clamps it to [volFloor, volCap], and prices a Black-Scholes put with an effective variance that weights closed-market time by a multiplier m (the closed-market surcharge):
The writer spread is added on top of fair value, and the market floors the premium at intrinsic value plus 5 bps of spot, whatever the pricer returns. A weekend-spanning quote therefore carries more variance per calendar hour than a mid-week one, which is what the quote card shows as "closed-market time". Everything the quote depends on (spot, vol, closed seconds) is returned by the contract so it can be verified. Replacing the pricer goes through a 2-day timelock (proposePricer, then acceptPricer).
- Spot / strike · 8 decimals, from the feed; strikes on a $1 tick
- Vol · annualized, 1e18 = 100%
- Premium / collateral · tUSD, 6 decimals
- Units · 1e18 = protection on one Stock Token
The feed price is per token and already includes the Stock Token's uiMultiplier, so splits and other corporate actions do not change what a unit protects.
Writers and the vault
Each underlying has its own ERC-4626 vault. Its accounting keeps writers fair to each other:
- Unearned premium. Premium sits in the vault but is not writer equity until its series settles (capital = balance − unearned premium), so a deposit made just before or after a sale does not share in its premium up front.
- Mark-to-market. totalAssets = capital − liability, where liability is the intrinsic value of the open puts beyond each series' own premium. A loss hits every writer's share price at once instead of whoever exits last, and a sale never moves the share price.
- Settlement escrow. At settlement the payout moves to the market's escrow and the rest of the collateral unlocks immediately, so writers never wait on holders to claim.
- Gating. Deposits and withdrawals pause (max = 0) while the vault has open exposure and the feed is dark, stale, invalid or paused, and while an expired series awaits settlement, because the share price cannot be marked fairly then. Sales and exits keep utilization (locked / capital) at or below 90%.
A 6-decimal virtual share offset makes first-depositor inflation attacks uneconomic. The invariant: vault balance ≥ locked collateral + unearned premium, always.
Safety rails
- No sales and no expiries in the closed window (Sat 00:00 to Mon 01:00 UTC); the app also refuses expiries from Friday 20:00 UTC, when the feeds have already gone quiet.
- Quotes reject stale feeds (older than maxPriceAge, 26h) and invalid answers.
- Answers of 0 or less, or of $1,000,000 or more, are invalid and skipped, never rescaled. Early mainnet rounds carry 16-decimal answers (e.g. 3964149999900000000 for $396.41); settlement walks past them.
- oraclePaused() on the feed or the Stock Token (corporate actions) blocks buys, and blocks settlement until the series' grace period ends.
- Strike on a $1 tick within [minStrikeBps, maxStrikeBps] of spot; tenor within [minTenor, maxTenor].
- maxPremium slippage guard on every buy (the UI uses +2%).
- At most 32 open series per underlying, which keeps mark-to-market bounded.
- Pricer changes wait out a 2-day timelock.
- The owner can pause the market or disable an underlying; both block new buys only. settle, settleAt and claim always remain open.
Contracts · Robinhood Chain Testnet (chainId 46630)
On testnet, FeedMirror replays the mainnet Chainlink rounds verbatim (same roundId, answer, updatedAt), so the market sees the real 24/5 session including frozen weekend prices and the invalid genesis-era answers it must skip. On mainnet the market points straight at the Chainlink proxy.
Links
- github.com/bchuazw/afterhours · contracts, Stylus pricer, keeper, this app
- Robinhood Chain docs
- Robinhood Chain Testnet explorer
- Arbitrum Stylus