在 Thetanuts Finance 交易加密期权——订单簿成交、RFQ 生命周期、多行权价结构、实时 WebSocket、钱包管理、提前结算……
On a new conversation the agent should:
text
Step 1: Check wallet status (read-only, safe to run automatically)
└─> node scripts/wallet-discover.js
Step 2: Interpret the result:
- configured: false → Enter ONBOARDING MODE
- configured: true → Greet user with their address, proceed normally
The agent must NEVER run update.sh automatically. Updates change code that signs
transactions, so they require explicit user consent each time.
When the user asks to check for or apply updates:
bash scripts/update.sh and surface the full output to the user.When no wallet is configured, guide the user through setup in this order:
text
No wallet detected
│
▼
┌─────────────────────────────────────────┐
│ "Welcome to Thetanuts Options Trading! │
│ Let's get you set up in 3 steps." │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Step 1: Create or import a wallet │
│ • New user → wallet-create.js │
│ • Have a seed? → wallet-import.js │
│ │
│ WARN: Use a DEDICATED wallet. │
│ Never reuse your primary wallet seed. │
│ Custody stays LOCAL on your machine. │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Step 2: Verify & fund the wallet │
│ • Run wallet-discover.js → show address │
│ • Run wallet-balance.js --chain │
│ base-mainnet │
│ • If zero balance: explain funding │
│ - Need ETH on Base for gas fees │
│ - Need USDC for PUT trades │
│ - Need WETH for CALL trades │
│ - Bridge via bridge.base.org │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Step 3: Market overview & first trade │
│ • Fetch prices: get-prices.ts │
│ • WebSearch for market news │
│ • Ask risk preference (LOW/MED/HIGH) │
│ • Show first strategy recommendation │
└─────────────────────────────────────────┘
Once wallet is configured, the agent MUST automatically:
wallet-discover.js + wallet-balance.js)get-prices.ts)Present this without the user asking:
text
Welcome to Thetanuts Options Trading!
Your wallet: 0x...
Chain: Base Mainnet
Balances: X ETH (gas) | Y USDC | Z WETH
Live Market:
• ETH: $X,XXX (24h: +X.X%)
• BTC: $XX,XXX (24h: +X.X%)
Available options: PUT, CALL, spreads, butterflies, condors
What would you like to do?
• "Show me ETH puts" → View available strikes and prices
• "Recommend a strategy" → I'll check market news and suggest based on your risk preference
• "Check my positions" → View open trades and P&L on expired ones
New to options? Just say "teach me about options"
.thetanuts-keys/ directory — lost keys cannot decrypt past offers.TNUT Agent is your AI-powered assistant for trading crypto options on Thetanuts Finance.
What it does:
Local-first design:
Capabilities:
CategoryWhat You Can DoWalletCreate, import, check balancesMarket DataLive prices, MM quotes, orderbook ordersTradingFill orderbook orders, submit RFQs, approve tokensPositionsView open positions, calculate payoffs
Options are contracts that give you the right (but not obligation) to buy or sell an asset at a specific price by a specific date.
A PUT gives the holder the right to SELL at the strike price.
ActionYou...Profit WhenRiskBuy PUTPay premium upfrontPrice drops below strikeLose premium if price stays upSell PUTReceive premiumPrice stays above strikeLose if price drops below strike
Example:
A CALL gives the holder the right to BUY at the strike price.
ActionYou...Profit WhenRiskBuy CALLPay premium upfrontPrice rises above strikeLose premium if price stays downSell CALLReceive premiumPrice stays below strikeLose if price rises above strike
Example:
TermDefinitionStrikeThe price at which the option can be exercisedExpiryWhen the option expires (settlement date, 8:00 UTC)PremiumThe price paid for the optionSettlementCash payment based on price difference at expiryCollateralFunds locked to back the option (for sellers)
IMPORTANT: Different option types require different collateral tokens:
Option TypeImplementationCollateral RequiredPUTPUTUSDC (quote asset)CALLINVERSE_CALLWETH (base asset)
(collateral × 1e8) / strikecollateral / 1e12 (1 WETH = 1 contract)The build-rfq.ts script automatically selects the correct collateral based on option type.
Beyond vanilla PUT and CALL options, Thetanuts supports advanced multi-strike structures for sophisticated trading strategies.
TypeStrikesCollateralDescriptionPUT1USDCStandard cash-settled putINVERSE_CALL1WETHCash-settled call (base collateral)CALL_SPREAD2USDCBuy lower strike call, sell higher strike callPUT_SPREAD2USDCBuy higher strike put, sell lower strike putCALL_FLY3USDCCall butterfly - profit near middle strikePUT_FLY3USDCPut butterfly - profit near middle strikeCALL_CONDOR4USDCCall condor - wider profit rangePUT_CONDOR4USDCPut condor - wider profit rangeIRON_CONDOR4USDCPut spread + call spread (neutral)PHYSICAL_CALL1WETHPhysically settled callPHYSICAL_PUT1USDCPhysically settled put
CRITICAL: Strikes must be ordered correctly or the RFQ will fail.
StructureTypeStrike OrderExampleVanillaPUT/CALLN/A (1 strike)--strike 1900SpreadPUTDescending (high→low)--strikes 1900,1800SpreadCALLAscending (low→high)--strikes 2000,2100ButterflyPUTDescending--strikes 1900,1850,1800ButterflyCALLAscending--strikes 2000,2050,2100CondorALLAlways ascending--strikes 1800,1900,2100,2200
These are simplified formulas for quick estimation. For exact on-chain values, use previewFillOrder() or client.utils.calculateCollateral().
StructureCollateralSimplified FormulaExampleVanilla PUTUSDCcollateral / strike$1900 USDC at $1900 strike = 1 contractINVERSE_CALLWETHcollateral (1:1)1 WETH = 1 contractCALL_SPREADUSDCcollateral / (upper - lower)$100 USDC / $100 width = 1 contractPUT_SPREADUSDCcollateral / (upper - lower)$100 USDC / $100 width = 1 contractButterflyUSDCcollateral / (middle - lower)$50 USDC / $50 wing = 1 contractCondorUSDCcollateral / (strike2 - strike1)$100 USDC / $100 inner width = 1 contractIRON_CONDORUSDCcollateral / max(K2-K1, K4-K3)Based on wider wingPHYSICAL_CALLWETHSame as INVERSE_CALL1 WETH = 1 contractPHYSICAL_PUTUSDCSame as Vanilla PUTcollateral / strike
Key rule: Only INVERSE_CALL and PHYSICAL_CALL use WETH (base asset). Everything else uses USDC — including CALL_SPREAD, CALL_FLY, and CALL_CONDOR.
StructureMarket ViewRisk LevelRisk/RewardBest ForPUTBearishHIGHLarge profit (up to strike - premium), lose premiumStrong downside convictionCALLBullishHIGHUnlimited profit, lose premiumStrong upside convictionPUT_SPREADModerately bearishMEDIUMCapped profit/lossCheaper than vanilla putCALL_SPREADModerately bullishMEDIUMCapped profit/lossCheaper than vanilla callPUT_FLYNeutral/range-boundLOWHigh reward if pinned, tiny costLow cost, precise targetCALL_FLYNeutral/range-boundLOWHigh reward if pinned, tiny costLow cost, precise targetCONDORNeutral/range-boundMEDIUMLower reward, wider rangeMore forgiving than flyIRON_CONDORNeutralLOWCollect premium both sidesRange-bound marketsPHYSICAL_CALLBullish (want delivery)HIGHReceive underlying assetPhysical settlement neededPHYSICAL_PUTBearish (want delivery)HIGHDeliver underlying assetPhysical settlement needed
When a user asks for a strategy recommendation, FIRST ask about their risk preference, then match to appropriate strategies within that tier.
Present these three options:
Risk LevelWhat It MeansMax LossProfit PotentialLOWDefined max loss, premium collection, high probability of small winsCapped & known upfrontCapped but consistentMEDIUMSpreads with balanced risk/reward, moderate probabilityCapped at spread widthCapped at spread widthHIGHFull directional exposure, low probability of large winsFull premium (buyer) or very large (seller)Unlimited (buyer)
text
User wants a strategy recommendation
│
▼
┌──────────────────────────────────┐
│ Step 1: Check market news │
│ (WebSearch - see News section) │
└──────────────────────────────────┘
│
▼
┌──────────────────────────────────┐
│ Step 2: Ask risk level │
│ LOW / MEDIUM / HIGH │
└──────────────────────────────────┘
│
├── LOW RISK
│ │
│ ├── Neutral view ──────► IRON_CONDOR (sell)
│ │ Collect premium both sides, profit if range-bound
│ │ Max loss: spread width - premium
│ │ Max gain: premium collected
│ │ Win prob: ~60-70% (price stays in range)
│ │
│ ├── Slightly bullish ──► PUT_FLY (buy) or Sell PUT via orderbook
│ │ Butterfly: tiny cost, big payout if pinned
│ │ Max loss: premium paid (very small)
│ │ Max gain: wing width - premium
│ │
│ ├── Slightly bearish ──► CALL_FLY (buy) or Sell CALL via orderbook
│ │ Same as above, opposite direction
│ │
│ └── Range-bound ───────► Butterfly (CALL_FLY / PUT_FLY)
│ Cheapest defined-risk play
│ Max loss: premium paid (~$3-5/contract)
│ Max gain: wing width (~$47-50/contract)
│
├── MEDIUM RISK
│ │
│ ├── Bullish ──────────► CALL_SPREAD (buy)
│ │ Cheaper than vanilla call, capped both sides
│ │ Max loss: premium paid
│ │ Max gain: spread width - premium
│ │ Risk/Reward: typically 1:1 to 1:3
│ │
│ ├── Bearish ─────────► PUT_SPREAD (buy)
│ │ Cheaper than vanilla put, capped both sides
│ │ Max loss: premium paid
│ │ Max gain: spread width - premium
│ │
│ └── Range-bound ─────► CONDOR (CALL_CONDOR / PUT_CONDOR)
│ Wider profit range than butterfly
│ Max loss: wing width - premium collected
│ Max gain: premium collected (sell) or inner width (buy)
│
└── HIGH RISK
│
├── Bullish ──────────► CALL (vanilla buy) / INVERSE_CALL
│ Unlimited profit potential if price rises
│ Max loss: full premium paid
│ Max gain: unlimited
│
├── Bearish ─────────► PUT (vanilla buy)
│ Unlimited profit if price crashes
│ Max loss: full premium paid
│ Max gain: strike - premium (if price → 0)
│
└── Selling premium ──► Naked PUT sell or CALL sell
Collect premium, very large risk if wrong
Max loss: strike price (PUT) or unlimited (CALL)
Max gain: premium collected
Every strategy recommendation MUST include this card:
text
┌────────────────────────────────────────────────────┐
│ Strategy: [STRATEGY NAME] │
│ Risk: [LOW / MEDIUM / HIGH] │
│ Direction: [BULLISH / BEARISH / NEUTRAL] │
├────────────────────────────────────────────────────┤
│ Max Loss: $[amount] ([description]) │
│ Max Gain: $[amount] ([description]) │
│ Break-even: $[price] │
│ Win Zone: Price between $[low] and $[high] │
│ Collateral: [amount] [token] │
└────────────────────────────────────────────────────┘
Different structures give you different exposure per dollar of collateral:
Structure$100 Collateral Gets YouRelative EfficiencyRisk LevelVanilla PUT (at $2000 strike)~0.05 contracts1x (baseline)HIGHPUT_SPREAD ($100 width)1 contract20x moreMEDIUMButterfly ($50 wing width)2 contracts40x moreLOWCondor ($100 inner width)1 contract20x moreMEDIUMIron Condor ($100 inner width)1 contract20x moreLOW
Key Insight: For users with small collateral (under $100), LOW and MEDIUM risk strategies (spreads, butterflies) provide significantly more market exposure than HIGH risk vanilla options.
When recommending a strategy, consider these inputs in order of priority:
PriorityInputHow to DetermineImpact on Recommendation1****Risk LevelAsk user: low, medium, or high?PRIMARY filter — determines available strategies2****Market ViewAsk user: bullish, bearish, or neutral?Determines direction within risk tier3****News/EventsWebSearch for recent market eventsInforms conviction level and timing4****Collateral SizeCheck wallet balanceSmall = prefer spreads/butterflies for efficiency5****Conviction LevelAsk: strong view or moderate?High conviction may justify higher risk tier6****Expiry TimeframeCheck available expiriesShort-term neutral = butterfly, Longer = condor
Before EVERY strategy recommendation, the agent MUST search for recent market-moving events. This ensures context-aware recommendations, not purely technical ones.
Use WebSearch with these queries (run at least 2):
QueryPurpose"crypto market news today"General market sentiment"ethereum price catalyst" or "bitcoin price catalyst"Asset-specific events"FOMC meeting crypto" or "CPI inflation crypto"Macro events affecting crypto"crypto options expiry this week"Options-specific events (pin risk, gamma squeeze)"ethereum upgrade" or "bitcoin ETF flows"Protocol/regulatory catalysts
If WebSearch returns a relevant article, use WebFetch to get deeper details.
Event TypeExpected ImpactSuggested DirectionRisk NoteRate cut / dovish FedBullishCALL-side strategiesMEDIUM — market may "sell the news"Rate hike / hawkish FedBearishPUT-side strategiesMEDIUM — may already be priced inMajor exchange hack/exploitBearish (short-term)PUT-side strategiesHIGH — volatility spikeETF approval / large inflowsBullishCALL-side strategiesMEDIUM — catalyst may be priced inProtocol upgrade (successful)Bullish for that assetCALL-side strategiesLOW-MEDIUMRegulatory crackdownBearishPUT-side or neutralHIGH — uncertainty and fearLarge options expiry (high OI)Neutral → pin riskButterflies / Condors (LOW risk)LOW — defined riskNo significant newsNeutral / range-boundPremium selling (Iron Condor)LOW — collect theta decayConflicting signalsUncertainNeutral structures or waitReduce size, use LOW risk
When presenting a strategy recommendation, always include market context:
text
Market Context (from recent news):
• [Event 1]: [brief description] → [bullish/bearish/neutral]
• [Event 2]: [brief description] → [bullish/bearish/neutral]
Overall market bias: [BULLISH / BEARISH / NEUTRAL / UNCERTAIN]
Based on [RISK LEVEL] risk preference and current market conditions:
[Strategy recommendation with Risk Summary Card]
2-Strike Spread (CALL_SPREAD):
bash
npx tsx scripts/build-rfq.ts \
--underlying ETH \
--type CALL \
--strikes 2000,2100 \
--expiry 1774684800 \
--contracts 1 \
--direction buy
Buy $2000 call, sell $2100 call. Max profit = $100 - premium. Max loss = premium.
3-Strike Butterfly (PUT_FLY):
bash
npx tsx scripts/build-rfq.ts \
--underlying ETH \
--type PUT \
--strikes 1900,1850,1800 \
--expiry 1774684800 \
--contracts 1 \
--direction buy
Profit maximized if ETH settles at $1850. Low cost, high reward if pinned.
4-Strike Condor (IRON_CONDOR):
bash
npx tsx scripts/build-rfq.ts \
--underlying ETH \
--type PUT \
--strikes 1800,1900,2100,2200 \
--expiry 1774684800 \
--contracts 1 \
--direction sell
Sell iron condor. Collect premium if ETH stays between $1900-$2100.
The SDK supports two trading systems. Choose based on your use case:
OptionBook****RFQ (Factory)****WhatFill existing market-maker ordersCreate custom options via sealed-bid auctionWhen to useQuick trades on listed optionsCustom strikes, expiries, multi-leg structuresStructuresVanilla onlyVanilla, spread, butterfly, condor, iron condorKey methodsfillOrder(), previewFillOrder()``buildRFQRequest(), requestForQuotation()PricingOrder prices from fetchOrders()MM pricing from getAllPricing()Data sourceBook indexer ( /api/v1/book/)Factory indexer ( /api/v1/factory/)User datagetUserPositionsFromIndexer()``getUserRfqs(), getUserOptionsFromRfq()StatsgetBookProtocolStats(), getBookDailyStats()``getFactoryProtocolStats(), getFactoryDailyStats()CollateralPaid upfront by takercollateralAmount = 0 (held by factory)SettlementCash onlyCash or physical
What it is: Fill existing orders posted by market makers - instant execution.
text
You → Fill existing order → Instant trade
Best for:
How it works:
npx tsx scripts/fetch-orders.ts --type PUTnpx tsx scripts/fill-order.ts --order-index 0 --collateral 10 --execute (reads WDK_SEED from .env)What it is: Request a custom quote from market makers - they respond within 45 seconds.
text
You → Submit RFQ → MM sees it → MM sends encrypted offer → You settle (or accept early)
Best for:
How it works:
npx tsx scripts/build-rfq.ts --underlying ETH --type PUT --strike 1900 --expiry <timestamp> --contracts 0.1 --direction buyto and dataScenarioUseWhy"I see a good price on orderbook"OrderbookInstant execution"Standard strike, liquidity exists"OrderbookFaster, simpler"Custom strike not on orderbook"RFQOnly way to get it"Large size trade"RFQMay get better pricing"Need it filled NOW"OrderbookRFQ takes up to 6 min"No orders at my strike"RFQRequest custom quote"Multi-leg structure"RFQOrderbook only supports vanilla"Physical settlement"RFQOrderbook is cash-only
Agent Decision Logic:
When a user wants to trade, the agent ALWAYS checks the orderbook first before recommending a trading method.
text
User wants to trade
│
▼
┌─────────────────────────┐
│ check-orderbook.ts │
│ Check for liquidity │
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ Exact strike match? │
└─────────────────────────┘
│ │
Yes No
│ │
▼ ▼
┌─────────┐ ┌─────────────────┐
│Orderbook│ │Nearby strikes? │
│ Fill │ │(within ±5%) │
└─────────┘ └─────────────────┘
│ │
Yes No
│ │
▼ ▼
┌─────────┐ ┌─────┐
│Show │ │ RFQ │
│options │ │ │
└─────────┘ └─────┘
Before recommending orderbook or RFQ, run:
bash
npx tsx scripts/check-orderbook.ts --underlying ETH --type PUT --strike 1900 --expiry 1774684800 --direction sell
The script returns:
recommendation: "orderbook" or "rfq"reason: Explanation of whyorderbookOrders: Matching orders with pricesnearbyStrikes: Alternative strikes if exact not foundnextStep: Command to run nextScenario 1: Liquidity Found
text
User: "Sell a PUT at $1900"
Agent: Runs check-orderbook.ts
Result: {
"recommendation": "orderbook",
"reason": "Found orderbook liquidity at strike $1900. Best bid: $8.08. Available: 75 contracts."
}
Agent: "Found a buyer at $8.08/contract. Use orderbook for instant execution."
Scenario 2: No Exact Match, Nearby Available
text
User: "Sell a PUT at $1850"
Agent: Runs check-orderbook.ts
Result: {
"recommendation": "rfq",
"reason": "No orderbook liquidity at $1850. Nearby strikes: $1900 (+2.7%), $1925 (+4.1%)"
}
Agent: "No orders at $1850, but $1900 is available (+2.7%). Would you like that, or submit RFQ for $1850?"
Scenario 3: Partial Fill
text
User: "Sell 100 contracts at $1900"
Agent: Runs check-orderbook.ts --size 100
Result: {
"recommendation": "orderbook",
"partialFillAvailable": true,
"partialSize": 75,
"reason": "Found 75 contracts (you requested 100). Partial fill available."
}
Agent: "Only 75 contracts available on orderbook. Fill partial now, or use RFQ for full 100?"
text
Step 1: Check wallet
└─> node scripts/wallet-discover.js
Step 2: Check balance
└─> node scripts/wallet-balance.js --chain base-mainnet --tokens 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Step 3: Fetch orderbook
└─> npx tsx scripts/fetch-orders.ts --type PUT
Step 4: Preview fill (see what you'll get)
└─> npx tsx scripts/fill-order.ts --order-index 0 --collateral 10
Step 5: Execute fill (reads WDK_SEED from .env — never pass seed on CLI)
└─> npx tsx scripts/fill-order.ts --order-index 0 --collateral 10 --execute --wait
text
Step 1: Check wallet & balance (same as above)
Step 2: Get MM pricing to see market levels
└─> npx tsx scripts/get-mm-pricing.ts ETH --type PUT
Step 3: Build RFQ with your terms
└─> npx tsx scripts/build-rfq.ts --underlying ETH --type PUT --strike 1900 --expiry 1774684800 --contracts 0.1 --direction buy
Step 4: Approve tokens (if selling/first time — prefer exact amount over --max)
└─> npx tsx scripts/approve-token.ts --token 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 --spender 0x8118daD971dEbffB49B9280047659174128A8B94 --amount <exact> --wait
(For unlimited approvals, add --max --confirm-max. Seed read from WDK_SEED env.)
Step 5: Send RFQ transaction (reads WDK_SEED from .env)
└─> npx tsx scripts/send-transaction.ts --to <from step 3> --data <from step 3> --wait
Step 6: Wait for MM offer (up to 45 seconds)
Step 7: Verify RFQ fill
└─> npx tsx scripts/check-rfq-fill.ts --address <wallet> --ticker <expected> --since <submission_timestamp>
└─> If filled: Shows position details
└─> If not filled: Shows suggestions for next steps
text
npx tsx scripts/get-positions.ts 0xYourWalletAddress
After trades expire, show the user their realized returns. This workflow uses existing scripts — no new tools needed.
text
Step 1: Fetch all positions
└─> npx tsx scripts/get-positions.ts <wallet_address>
Step 2: Categorize by status
- Settled/expired positions → REALIZED P&L
- Active/open positions → UNREALIZED (show separately)
Step 3: For each expired position
- Use the pnlUsd field from get-positions.ts (if available)
- If pnlUsd is missing, calculate manually:
└─> For PUT/CALL: npx tsx scripts/calculate-payout.ts --type <PUT|CALL> --strike <strike> --settlement <settlement_price> --contracts <amount> [--is-buyer]
└─> For spreads/butterflies/condors: use client.option.calculatePayout(optionAddress, settlementPrice) on-chain
(Note: calculate-payout.ts only supports vanilla PUT/CALL — multi-strike payouts need the SDK on-chain method)
Step 4: Aggregate and present using the template below
For BUYERS (paid premium):
For SELLERS (received premium):
Present results in this format:
text
Portfolio Performance Summary
══════════════════════════════════════════════════════════
Wallet: 0x...
Period: [earliest expired trade] to [latest expired trade]
REALIZED P&L (Expired Positions)
┌──────────────────────┬──────┬─────────┬─────────┐
│ Trade │ Side │ P&L │ ROI │
├──────────────────────┼──────┼─────────┼─────────┤
│ ETH-28MAR-2000-P │ SELL │ +$12.50 │ +6.25% │
│ ETH-28MAR-2100-C │ BUY │ -$8.00 │ -100% │
│ ETH-04APR-1900/1800 │ BUY │ +$45.00 │ +150% │
├──────────────────────┼──────┼─────────┼─────────┤
│ TOTAL │ │ +$49.50 │ │
└──────────────────────┴──────┴─────────┴─────────┘
Win rate: 2/3 (66.7%)
Average P&L per trade: +$16.50
Best trade: ETH PUT_SPREAD (+$45.00, +150%)
Worst trade: ETH CALL (-$8.00, expired worthless)
OPEN POSITIONS
┌──────────────────────┬──────┬───────────┬──────────┬────────┐
│ Trade │ Side │ Contracts │ Expiry │ Status │
├──────────────────────┼──────┼───────────┼──────────┼────────┤
│ ETH-18APR-2000-P │ BUY │ 0.5 │ Apr 18 │ Active │
└──────────────────────┴──────┴───────────┴──────────┴────────┘
After a market maker submits an encrypted offer to your RFQ, you can decrypt and accept it early — no need to wait for the full deadline.
text
Step 1: Submit RFQ (Workflow 2, Steps 1-5)
Step 2: MM sends encrypted offer (within 45 seconds)
Step 3: Decrypt the offer using your ECDH keypair
└─> Uses client.rfqKeys.decryptOffer()
Step 4: Accept the offer early
└─> Uses client.optionFactory.encodeSettleQuotationEarly(quotationId, offerAmount, nonce, offeror)
└─> Send transaction with returned {to, data}
Real examples:
0x105f75cdfb64a3796100f6d667bc4f7fec3836d2b5aa5c43b66073a1b40964ee0xa89fb6dbad43b430399bbdec878927185e602b7df9b5390f71d2d11c33e4d850If a user wants to cancel an active RFQ before it settles, or if no MM responded:
text
┌─────────────────────────────────────────────────────┐
│ RFQ submitted │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ MM responded? │ │
│ └─────────────────────┘ │
│ │ │ │
│ Yes No │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────┐ ┌──────────────────────────────┐ │
│ │ Accept │ │ RFQ expires with no offers │ │
│ │ or │ │ → No collateral was locked │ │
│ │ Cancel? │ │ → Nothing to refund │ │
│ └─────────┘ └──────────────────────────────┘ │
│ │ │ │
│ Accept Cancel │
│ │ │ │
│ ▼ ▼ │
│ Settle Cancel RFQ │
│ └─> cancelQuotation(quotationId) │
│ └─> Collateral returned to wallet │
└─────────────────────────────────────────────────────┘
typescript
// Cancel before settlement — requester only
const { to, data } = client.optionFactory.encodeCancelQuotation(quotationId);
// Send transaction with {to, data} via send-transaction.ts
typescript
// MM cancels their offer before settlement
const { to, data } = client.optionFactory.encodeCancelOfferForQuotation(quotationId);
typescript
// Query current state of an RFQ
const quotation = await client.optionFactory.getQuotation(quotationId);
// quotation status: 'active' | 'settled' | 'cancelled'
// quotation.isActive — whether RFQ is still open
// quotation.currentWinner — winning offeror's address
ScenarioCollateralWhat HappensBuy RFQ, no MM respondsNo collateral locked by buyerNothing to refund — buyer only pays premium if settledBuy RFQ, user cancels before settlementNo collateral lockedRFQ cancelled, no action neededSell RFQ, no MM respondsCollateral held by factoryRFQ expires, collateral returned automaticallySell RFQ, user cancels before settlementCollateral held by factorycancelQuotation() releases collateral back to walletSell RFQ, settledCollateral locked in optionCollateral backs the option until expiry/settlement**Orderbook fill (buy or sell)**Collateral paid/locked at fillIrreversible — no cancellation for orderbook fills
IMPORTANT: Orderbook fills are INSTANT and IRREVERSIBLE. Only RFQs can be cancelled (before settlement).
No ETH is sent as transaction value for option trades. All collateral flows happen via ERC-20 token transfers (USDC, WETH). ETH is only used for gas fees.
text
Buyer Flow:
1. Premium is paid in collateral tokens (USDC for PUTs, WETH for CALLs)
2. Token approval needed: approve-token.ts (one-time per token/spender)
3. Collateral is transferred to the contract when the trade executes
4. If RFQ: collateralAmount = 0 in the request (factory holds it)
5. If Orderbook: collateral deducted at fill time
What you PAY: Premium (in USDC or WETH) + gas (in ETH)
What you GET: Option position (right to profit if price moves your way)
At expiry: Payout if in-the-money, or nothing if out-of-the-money
text
Seller Flow:
1. Collateral is LOCKED to back the option (USDC for PUTs, WETH for CALLs)
2. Token approval needed: approve-token.ts (one-time per token/spender)
3. Collateral transferred to contract and held until expiry
4. Premium is received from buyer
What you PAY: Collateral lock (USDC or WETH) + gas (in ETH)
What you GET: Premium received upfront
At expiry: Keep premium if OTM, or payout difference if ITM
Remaining collateral returned after settlement
ActionCollateral TokenGas TokenApproval NeededBuy PUTUSDC (premium)ETHUSDC → OptionFactory/OptionBookBuy CALLWETH (premium)ETHWETH → OptionFactory/OptionBookSell PUTUSDC (collateral lock)ETHUSDC → OptionFactory/OptionBookSell CALLWETH (collateral lock)ETHWETH → OptionFactory/OptionBook
Always check balance BEFORE trading:
bash
# Check USDC balance (for PUTs)
node scripts/wallet-balance.js --chain base-mainnet --tokens 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
# Check WETH balance (for CALLs)
node scripts/wallet-balance.js --chain base-mainnet --tokens 0x4200000000000000000000000000000000000006
# Check ETH balance (for gas)
node scripts/wallet-balance.js --chain base-mainnet
The winner ( winningOfferor) in an RFQ is always the market maker with the best offer price. Winner selection works the same way for ALL option types — PUT, CALL, spreads, butterflies, condors, iron condors, and physical options.
The winner address appears in three places depending on context:
ContextFieldWhen to UseOn-chain stateQuotationState.currentWinnerDuring active RFQ — the current best offerorState APIStateRfq.winnerAfter settlement — the final winning MMSettlement eventQuotationSettledEvent.winningOfferorEmitted when RFQ settles
TypeBuyer Payout FormulaMax PayoutSettlementPUTmax(strike - price, 0) × contractsstrike × contractsCash (USDC)INVERSE_CALLmax(price - strike, 0) × contractsUnlimitedCash (WETH)CALL_SPREADmin(max(price - K1, 0), K2 - K1) × contracts(K2 - K1) × contractsCash (USDC)PUT_SPREADmin(max(K1 - price, 0), K1 - K2) × contracts(K1 - K2) × contractsCash (USDC)CALL_FLYPeaks at middle strike, 0 at wingswing width × contractsCash (USDC)PUT_FLYPeaks at middle strike, 0 at wingswing width × contractsCash (USDC)CALL_CONDORFlat max between K2-K3, tapers at wingsinner width × contractsCash (USDC)PUT_CONDORFlat max between K2-K3, tapers at wingsinner width × contractsCash (USDC)IRON_CONDORProfit if price between K2-K3premium collectedCash (USDC)PHYSICAL_CALLBuyer receives underlying assetN/A (delivery)PhysicalPHYSICAL_PUTBuyer delivers underlying, receives USDCN/A (delivery)Physical
The SDK utility client.utils.calculatePayout() supports these types:
typescript
type PayoutType = 'call' | 'put' | 'call_spread' | 'put_spread';
For butterflies, condors, and iron condors: use client.option.calculatePayout(optionAddress, settlementPrice) which calculates on-chain for any option type, or use client.option.simulatePayout() for simulation.
typescript
// For vanilla and spreads — use utility function
const payout = client.utils.calculatePayout({
type: 'call_spread',
strikes: [2000n * 10n**8n, 2100n * 10n**8n],
settlementPrice: 2050n * 10n**8n,
numContracts: 1n * 10n**18n,
});
// For butterflies, condors, iron condors — use on-chain calculation
const payout = await client.option.calculatePayout(optionAddress, settlementPrice);
When an option settles at expiry, these events are emitted:
EventFieldsDescriptionOptionExpiredEventsettlementPriceOracle records the final priceOptionPayoutEventbuyer, amountPaidOutBuyer receives payout (if ITM)CollateralReturnedEventseller, amountReturnedSeller gets remaining collateral back
r12 settlement is automatic. On the Base_r12 deployment, options no longer expose a zero-arg
payout()write entrypoint — settlement fires from the factory'snotifyTradeSettledcallback once the oracle records the final price. SDK ≥ 0.2.4 throwsINVALID_PARAMSon any legacypayout()call; check status withclient.option.getOptionInfo(optionAddress).settledand read amounts with the view methods above. Reclaiming collateral after settlement usesclient.option.reclaimCollateral(optionAddress, ownedOption)— the SDK auto-forwardsgetReclaimFee()asmsg.value.
Physical options involve actual asset delivery instead of cash settlement:
TypeBuyer PaysBuyer ReceivesSeller ProvidesPHYSICAL_CALLstrike × contracts (USDC)contracts of underlying (WETH)WETH collateralPHYSICAL_PUTcontracts of underlying (WETH)strike × contracts (USDC)USDC collateral
The SDK has dedicated builders for physical options:
client.optionFactory.buildPhysicalRFQ()client.optionFactory.buildPhysicalSpreadRFQ()client.optionFactory.buildPhysicalButterflyRFQ()client.optionFactory.buildPhysicalCondorRFQ()When viewing orders, availableAmount represents the maker's collateral budget, not the number of contracts. The actual number of purchasable contracts depends on the option type and collateral requirements.
These are the on-chain formulas using raw decimal values. For practical use, prefer previewFillOrder() which handles all conversions automatically.
Option Type# StrikesCollateralOn-Chain FormulaExampleVanilla PUT1USDC(collateral × 1e8) / strike10,000 USDC at $95k = 0.105 contractsINVERSE_CALL1WETHcollateral / 1e121 WETH = 1 contractCALL_SPREAD2USDC(collateral × 1e8) / spreadWidth10,000 USDC / $10k = 1 contractPUT_SPREAD2USDC(collateral × 1e8) / spreadWidth10,000 USDC / $10k = 1 contractBUTTERFLY3USDC(collateral × 1e8) / maxSpreadBased on widest strike rangeCONDOR4USDC(collateral × 1e8) / maxSpreadBased on widest strike rangePHYSICAL_CALL1WETHSame as INVERSE_CALL1 WETH = 1 contractPHYSICAL_PUT1USDCSame as Vanilla PUTSame formula
IMPORTANT: Only single-strike CALL options (INVERSE_CALL, PHYSICAL_CALL) use WETH collateral. ALL multi-strike structures use USDC, even CALL_SPREAD, CALL_FLY, and CALL_CONDOR.
Always use previewFillOrder() to see the actual contract count before filling:
typescript
const order = orders[0];
// Preview shows calculated max contracts based on collateral requirements
const preview = client.optionBook.previewFillOrder(order);
console.log(`Max contracts: ${preview.maxContracts}`);
console.log(`Collateral token: ${preview.collateralToken}`);
console.log(`Price per contract: ${preview.pricePerContract}`);
// Preview with specific premium amount
const preview10 = client.optionBook.previewFillOrder(order, 10_000000n); // 10 USDC premium
console.log(`Contracts for 10 USDC: ${preview10.numContracts}`);
For a PUT option with a $95,000 strike:
The previewFillOrder() method handles these calculations automatically for all option types.
ModulePurposeRequires Signerclient.erc20Token approvals, balances, transfersWrite ops onlyclient.optionBookFill/cancel orders, get fees, preview fillsWrite ops onlyclient.apiFetch orders, positions, statsNoclient.optionFactoryRFQ lifecycle managementWrite ops onlyclient.optionPosition management, payoutsWrite ops onlyclient.eventsQuery blockchain events (OfferMade, QuotationRequested)Noclient.wsReal-time WebSocket subscriptionsNoclient.pricingOption pricing, GreeksNoclient.mmPricingMarket maker pricing with fee adjustmentsNoclient.rfqKeysECDH keypair management for sealed-bid RFQ encryptionNoclient.collarCollar loans (capped-upside, zero-interest). Pricing/estimation works today; write methods throw NETWORK_UNSUPPORTED until CollarLoanCoordinator is deployed on Base.Write ops onlyclient.utilsDecimal conversions, payoffsNo
The SDK supports a referrer address for fee sharing on order fills. When a referrer is set, a portion of the trading fees is allocated to the referrer.
typescript
// Option 1: Set referrer at client initialization (applies to all fills)
const client = new ThetanutsClient({
chainId: 8453,
provider,
signer,
referrer: '0x92b8ac05b63472d1D84b32bDFBBf3e1887331567',
});
// All fillOrder calls will use this referrer automatically
await client.optionBook.fillOrder(order);
// Option 2: Pass referrer per fill call (overrides client default)
await client.optionBook.fillOrder(order, undefined, '0xYourReferrerAddress');
// Option 3: Use encode methods (for viem/wagmi/AA wallets)
const { to, data } = client.optionBook.encodeFillOrder(
order,
collateralAmount,
'0x92b8ac05b63472d1D84b32bDFBBf3e1887331567'
);
const hash = await walletClient.sendTransaction({ to, data });
// Query referrer fee split
const feeBps = await client.optionBook.getReferrerFeeSplit('0x...');
console.log(`Referrer fee: ${feeBps} bps`);
// Query accumulated fees
const fees = await client.optionBook.getFees(usdcAddress, '0x...');
console.log(`Accumulated fees: ${fees}`);
If no referrer is provided, the zero address ( 0x000...) is used (no fee sharing).
The SDK uses ECDH (Elliptic Curve Diffie-Hellman) key pairs for encrypted offers in the RFQ system. Keys are automatically persisted based on your environment:
EnvironmentDefault StoragePersistenceNode.jsFileStorageProviderKeys saved to .thetanuts-keys/ directoryBrowserLocalStorageProviderKeys saved to localStorage
typescript
// Keys are automatically persisted - no configuration needed
const keyPair = await client.rfqKeys.getOrCreateKeyPair();
console.log('Public Key:', keyPair.compressedPublicKey);
// Keys are saved automatically and survive process restarts
IMPORTANT: Back up your RFQ private keys! Keys are stored in
.thetanuts-keys/with secure permissions. If lost, you cannot decrypt offers made to your public key. There is no recovery mechanism.
Subscribe to live updates for orders and prices:
typescript
const client = new ThetanutsClient({ chainId: 8453, provider });
// 1. Connect
await client.ws.connect();
// 2. Subscribe to order updates
const unsubOrders = client.ws.subscribeOrders((update) => {
console.log(`Order ${update.event}:`, update);
});
// 3. Subscribe to price updates for ETH
const unsubPrices = client.ws.subscribePrices((update) => {
console.log(`ETH price: $${update.price}`);
}, 'ETH');
// 4. Handle connection state changes
const unsubState = client.ws.onStateChange((state) => {
console.log(`WebSocket state: ${state}`);
});
// 5. Disconnect when done
// unsubOrders(); unsubPrices(); unsubState();
// client.ws.disconnect();
The WebSocket module auto-reconnects by default (up to 10 attempts).
All SDK methods throw ThetanutsError with typed error codes:
typescript
import { isThetanutsError, OrderExpiredError, InsufficientAllowanceError } from '@thetanuts-finance/thetanuts-client';
try {
await client.optionBook.fillOrder(order, 10_000000n);
} catch (error) {
if (error instanceof OrderExpiredError) {
console.log('Order expired, fetching fresh orders...');
const freshOrders = await client.api.fetchOrders();
} else if (error instanceof InsufficientAllowanceError) {
console.log('Approving tokens first...');
await client.erc20.ensureAllowance(usdcAddress, optionBookAddress, amount);
} else if (isThetanutsError(error)) {
switch (error.code) {
case 'SLIPPAGE_EXCEEDED': console.log('Price moved too much'); break;
case 'INSUFFICIENT_BALANCE': console.log('Not enough tokens'); break;
case 'SIGNER_REQUIRED': console.log('Signer required'); break;
case 'CONTRACT_REVERT': console.log('Contract call failed'); break;
}
}
}
CodeDescriptionORDER_EXPIREDOrder has expired or will expire soonSLIPPAGE_EXCEEDEDPrice moved beyond toleranceINSUFFICIENT_ALLOWANCEToken approval neededINSUFFICIENT_BALANCENot enough tokensNETWORK_UNSUPPORTEDNetwork not supportedHTTP_ERRORAPI request failedCONTRACT_REVERTSmart contract call failedINVALID_PARAMSInvalid parameters providedORDER_NOT_FOUNDOrder not foundSIZE_EXCEEDEDFill size exceeds availableSIGNER_REQUIREDSigner needed for transactionWEBSOCKET_ERRORWebSocket connection error
typescript
interface ThetanutsClientConfig {
chainId: 8453; // Required: Chain ID
provider: Provider; // Required: ethers.js provider
signer?: Signer; // Optional: For transactions
referrer?: string; // Optional: Referrer address for fees
apiBaseUrl?: string; // Optional: Override API URL
indexerApiUrl?: string; // Optional: Override indexer URL
pricingApiUrl?: string; // Optional: Override pricing URL
wsUrl?: string; // Optional: Override WebSocket URL
env?: 'dev' | 'prod'; // Optional: Environment (default: prod)
logger?: ThetanutsLogger; // Optional: Custom logger
keyStorageProvider?: StorageProvider; // Optional: Custom RFQ key storage
}
typescript
const client = new ThetanutsClient({
chainId: 8453,
provider,
logger: {
debug: (msg, meta) => myLogger.debug(msg, meta),
info: (msg, meta) => myLogger.info(msg, meta),
warn: (msg, meta) => myLogger.warn(msg, meta),
error: (msg, meta) => myLogger.error(msg, meta),
},
});
Before deploying to production, verify the following:
https://mainnet.base.org which has strict rate limitsreferrer address to earn fee-sharing revenue on fillslogger to capture errors in Sentry/Datadogclient.erc20.ensureAllowance() before fillOrder()order.expiry before filling to avoid wasted gas estimates.thetanuts-keys/ directory — lost keys cannot decrypt past offersThe SDK provides utilities for safe decimal conversions:
TypeDecimalsExampleUSDC61000000 = 1 USDCWETH181000000000000000000 = 1 WETHcbBTC8100000000 = 1 cbBTCStrike/Price8185000000000 = $1850typescript
// Convert to on-chain values
const usdc = client.utils.toBigInt('100.5', 6); // 100500000n
const strike = client.utils.strikeToChain(1850); // 185000000000n
// Convert from on-chain values
const display = client.utils.fromBigInt(100500000n, 6); // '100.5'
const price = client.utils.strikeFromChain(185000000000n); // 1850
Agent: [Triggers onboarding flow]
wallet-discover.js → no wallet foundtext
Agent: Welcome to Thetanuts Options Trading! Let's get you set up.
I see you don't have a wallet configured yet.
Would you like to create a new wallet or import an existing one?
[After wallet creation...]
Your wallet: 0x7a3F...9e2B
Chain: Base Mainnet
Balance: 0 ETH | 0 USDC | 0 WETH
To start trading, you'll need to fund your wallet:
• ETH on Base for gas fees (bridge via bridge.base.org)
• USDC for PUT options | WETH for CALL options
Once funded, tell me:
1. What's your risk preference? (Low / Medium / High)
2. Are you bullish, bearish, or neutral on ETH?
I'll check the latest market news and recommend a strategy!
Agent:
"crypto market news today""ethereum bitcoin price catalyst this week"text
Agent: Let me check the latest market news...
Market Context:
• Fed meeting tomorrow — markets expect rate hold, dovish tone → BULLISH
• Large BTC options expiry ($2.3B) on Friday → potential pin risk → NEUTRAL
• ETH Layer-2 TVL hitting ATH → positive sentiment → BULLISH
Overall bias: MODERATELY BULLISH with near-term volatility expected
What risk level are you comfortable with?
LOW: Iron Condor — collect premium, profit if ETH stays in range
Good for the expected range-bound action before Fed meeting
MEDIUM: Call Spread — capped upside play on the bullish bias
Benefit from dovish Fed without unlimited downside
HIGH: Vanilla Call — unlimited upside if Fed sparks a rally
Lose full premium if market sells the news
Agent:
text
Agent: Here are the LOW RISK strategies available right now:
┌────────────────────────────────────────────────────┐
│ Strategy: Iron Condor (ETH) │
│ Risk: LOW │
│ Direction: NEUTRAL │
├────────────────────────────────────────────────────┤
│ Strikes: $1800 / $1900 / $2100 / $2200 │
│ Max Loss: $85/contract │
│ Max Gain: $15/contract (premium collected) │
│ Win Zone: ETH stays between $1900-$2100 │
│ Current: ETH $2,050 (right in the sweet spot!) │
│ Collateral: ~$100 USDC per contract │
└────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ Strategy: Put Butterfly (ETH) │
│ Risk: LOW │
│ Direction: NEUTRAL (target $2,000) │
├────────────────────────────────────────────────────┤
│ Strikes: $1950 / $2000 / $2050 │
│ Max Loss: ~$3/contract (premium paid) │
│ Max Gain: ~$47/contract (if pinned at $2,000) │
│ Win Zone: ETH settles near $2,000 │
│ Collateral: ~$50 USDC per contract │
└────────────────────────────────────────────────────┘
Which strategy interests you? Or want to see MEDIUM or HIGH risk options?
Agent:
get-positions.ts with user's wallet addresstext
Agent: Let me check your trading performance...
Portfolio Performance Summary
══════════════════════════════════════════════════════════
Wallet: 0x7a3F...9e2B
Period: Mar 14, 2026 to Apr 11, 2026
REALIZED P&L (Expired Positions)
┌───────────────────────┬──────┬──────────┬─────────┐
│ Trade │ Side │ P&L │ ROI │
├───────────────────────┼──────┼──────────┼─────────┤
│ ETH-28MAR-2000-P │ SELL │ +$12.50 │ +6.25% │
│ ETH-28MAR-2100-C │ BUY │ -$8.00 │ -100% │
│ ETH-04APR-1900/1800-PS│ BUY │ +$45.00 │ +150% │
├───────────────────────┼──────┼──────────┼─────────┤
│ TOTAL │ │ +$49.50 │ │
└───────────────────────┴──────┴──────────┴─────────┘
Win rate: 2/3 (66.7%)
Average P&L per trade: +$16.50
Best trade: ETH PUT_SPREAD (+$45.00, +150% ROI)
Worst trade: ETH CALL (-$8.00, expired worthless)
Insight: Your MEDIUM risk spread trades outperformed the HIGH risk
vanilla call. Consider focusing on spreads for better risk-adjusted returns.
OPEN POSITIONS
┌───────────────────────┬──────┬───────────┬──────────┐
│ Trade │ Side │ Contracts │ Expiry │
├───────────────────────┼──────┼───────────┼──────────┤
│ ETH-18APR-2000-P │ BUY │ 0.5 │ Apr 18 │
└───────────────────────┴──────┴───────────┴──────────┘
Want me to analyze what your open position might settle at?
User: Show me ETH puts
Agent:
npx tsx scripts/get-mm-pricing.ts ETH --type PUTAgent's Decision Process:
If Orderbook:
text
Agent: There's a BID at $1900. Let me fill it for you.
1. Previews: "You'll sell ~0.005 contracts, receive ~$X premium"
2. Asks: "Proceed?"
3. Executes fill
If RFQ:
text
Agent: No orderbook liquidity at $1900. I'll submit an RFQ.
1. Builds RFQ for ETH-1900-PUT sell
2. Approves USDC if needed
3. Submits RFQ
4. "RFQ submitted. MMs have 45 seconds to respond."
Agent:
npx tsx scripts/get-positions.ts 0xYourAddressAgent's Decision Process:
Agent:
text
Market Context:
• ETH down 3% on regulatory concerns → BEARISH
• Overall market sentiment weak → confirms bearish view
With $50 and a bearish view at MEDIUM risk, I recommend a PUT SPREAD:
┌────────────────────────────────────────────────────┐
│ Strategy: PUT_SPREAD │
│ Risk: MEDIUM │
│ Direction: BEARISH │
├────────────────────────────────────────────────────┤
│ Max Loss: $50 (your full collateral) │
│ Max Gain: $50 (spread width - premium) │
│ Break-even: ~$1892 │
│ Win Zone: ETH below $1900 at expiry │
│ Collateral: $50 USDC -> 0.5 contracts │
│ (20x more than vanilla PUT at 0.026) │
└────────────────────────────────────────────────────┘
Available strikes for tomorrow's expiry:
• PUT_SPREAD $1950/$1850 - Bid: $12/contract
• PUT_SPREAD $1900/$1800 - Bid: $8/contract
Which spread would you like?
Want HIGH risk instead? Vanilla PUT gives unlimited profit but only 0.026 contracts.
Want LOW risk instead? Put Butterfly costs ~$3 with $47 max gain.
Agent's Decision Process:
Agent:
text
Market Context:
• No major catalysts this week → NEUTRAL
• Options expiry Friday with large OI at $2000 → pin risk → NEUTRAL
With $30 and a neutral view, here's my LOW RISK recommendation:
┌────────────────────────────────────────────────────┐
│ Strategy: CALL_FLY (Butterfly) │
│ Risk: LOW │
│ Direction: NEUTRAL (target $2,050) │
├────────────────────────────────────────────────────┤
│ Strikes: 2000 / 2050 / 2100 │
│ Max Loss: ~$5/contract (premium paid) │
│ Max Gain: ~$45/contract (at exactly $2050) │
│ Break-even: $2005 - $2095 │
│ Collateral: $30 -> ~6 contracts │
└────────────────────────────────────────────────────┘
Why butterfly (LOW risk)?
• Cheapest way to play range-bound (more contracts per dollar)
• Max loss is tiny (~$5/contract)
• If ETH pins near $2050, each contract pays ~$45
Want MEDIUM risk? A Condor ($100+) gives a wider profit zone.
Build this RFQ? (y/n)
Agent's Decision Process:
Agent:
text
Market Context:
• Market consolidating after rally → NEUTRAL
• No major events this week → supports range-bound play
For premium selling, I recommend an IRON CONDOR (LOW RISK):
┌────────────────────────────────────────────────────┐
│ Strategy: IRON_CONDOR (sell) │
│ Risk: LOW │
│ Direction: NEUTRAL (profit if range-bound) │
├────────────────────────────────────────────────────┤
│ Strikes: $1800 / $1900 / $2100 / $2200 │
│ Max Loss: $85/contract (if ETH exits range) │
│ Max Gain: $15/contract (premium collected) │
│ Win Zone: ETH stays between $1900-$2100 │
│ Current: ETH $2,000 (right in the middle!) │
│ Collateral: ~$100 USDC per contract │
└────────────────────────────────────────────────────┘
This strategy profits ~60-70% of the time in range-bound markets.
You collect $15 premium and risk $85 if wrong.
Is ETH likely to stay between $1900-$2100? Let me build this RFQ.
Tailor detail level to the user's experience:
User TypeHow to DetectWhat to ShowNew userFirst session, no wallet, asks basic questionsSimplified info, no SDK details, guide step-by-stepReturning userHas wallet, has traded beforeRelevant data for their question, skip basicsAdvanced userAsks about multi-strike, SDK, specific paramsFull technical detail, contract addresses, raw data
ASCII Table Alignment Rules (CRITICAL):
┌───┐ top border, ├───┤ divider, and └───┘ bottom border MUST all be the exact same width│ ... │ MUST have the right │ aligned exactly under the ┐ corner─ chars wide (56 total with ┌┐), every content line must be exactly 54 chars between the │ borders│ always aligns│ borders)┌ ┐ └ ┘ ─ │ ├ ┤ ┼ ┬ ┴ (never heavy/double: ═ ║ ╔)For strategy recommendations — Always include the Risk Summary Card (see "Risk-Categorized Strategy Recommendations" section for the full template with all fields: Strategy, Risk Level, Direction, Max Loss, Max Gain, Break-even, Win Zone, Collateral).
For price/market data — Use compact tables:
text
ETH Options (PUT) — Current Price: $2,050
┌────────────┬─────────┬────────┬────────┐
│ Strike │ Expiry │ Bid │ Ask │
├────────────┼─────────┼────────┼────────┤
│ $1,900 │ Apr 25 │ $8.50 │ $12.00 │
│ $2,000 │ Apr 25 │ $25.00 │ $30.00 │
└────────────┴─────────┴────────┴────────┘
For trade confirmations — Clear summary before execution:
text
Trade Summary
═════════════
Action: Buy PUT_SPREAD
Risk Level: MEDIUM
Strikes: $1900 / $1800
Expiry: Apr 25, 2026
Contracts: 1.0
Cost: ~$15 USDC
Max Loss: $15 | Max Gain: $85
Confirm? (yes/no)
For errors — Never show raw JSON to the user:
{"error": "INSUFFICIENT_ALLOWANCE"} → "You need to approve USDC spending first. Want me to do that?"ToolWhen to UseExampleWebSearchBefore every strategy recommendation (fetch market news)"crypto market news today"WebFetchDrill into a specific article or data source from WebSearch resultsFetch full article contentcontext7When user asks about SDK features or encounters an unfamiliar errorResolve thetanuts-finance/thetanuts-client, then query docs
When a user asks a technical question about the Thetanuts SDK:
resolve-library-id to find thetanuts-finance/thetanuts-clientquery-docs with the specific topic (e.g., "fillOrder", "WebSocket", "error handling")CommandDescriptionnode scripts/wallet-discover.jsCheck if wallet configured, show addressesnode scripts/wallet-create.jsGenerate new walletnode scripts/wallet-import.js --seed-file /pathImport existing seednode scripts/wallet-balance.js --chain base-mainnetCheck balancenode scripts/wallet-select.js --family evm --chain base-mainnetSet active chain
CommandDescriptionnpx tsx scripts/get-prices.tsGet BTC/ETH pricesnpx tsx scripts/get-mm-pricing.ts ETH --type PUTGet MM option quotesnpx tsx scripts/fetch-orders.ts --type PUTFetch orderbooknpx tsx scripts/check-orderbook.ts --underlying ETH --type PUT --strike 1900 --expiry <ts> --direction sellCheck orderbook liquidity (run FIRST before trading)
CommandDescriptionnpx tsx scripts/fill-order.ts --order-index 0 --collateral 10 --executeFill orderbook order (reads WDK_SEED env)npx tsx scripts/build-rfq.ts --underlying ETH --type PUT --strike 1900 --expiry <ts> --contracts 0.1 --direction buyBuild vanilla RFQ (45s deadline)npx tsx scripts/build-rfq.ts --underlying ETH --type CALL --strikes 2000,2100 --expiry <ts> --contracts 1 --direction buyBuild multi-strike RFQ (spread/fly/condor)npx tsx scripts/approve-token.ts --token <addr> --spender <addr> --amount <n>Approve tokens (exact amount; add --max --confirm-max for unlimited)npx tsx scripts/send-transaction.ts --to <addr> --data <hex>Send transaction (reads WDK_SEED env)npx tsx scripts/check-rfq-fill.ts --address <wallet> --ticker <expected> --since <ts>Verify RFQ fill status
CommandDescriptionnpx tsx scripts/get-positions.ts <address>Get open positionsnpx tsx scripts/calculate-payout.ts --type PUT --strike 1900 --settlement 1800 --contracts 1Calculate payoff
Source of truth: these mirror
@thetanuts-finance/thetanuts-client@^0.2.4CHAIN_CONFIGS_BY_ID[8453]. Do not hard-code addresses in scripts — read them fromclient.chainConfigso future SDK upgrades stay in sync. If you ever see a mismatch, the SDK wins.
ContractAddressOptionBook0x1bDff855d6811728acaDC00989e79143a2bdfDedOptionFactory0x8118daD971dEbffB49B9280047659174128A8B94TWAP Consumer0xE909fb38767e0ac5F7a347DF9Dd4222217E10816
TokenAddressDecimalsUSDC0x833589fCD6eDb6E08f4c7C32D4f71b54bdA029136WETH0x420000000000000000000000000000000000000618cbBTC0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf8cbDOGE0x73c7A9C372F31c1b1C7f8E5A7D12B8735c817C798cbXRP0x7B2Cd9EA5566c345C9cdbcF58f5E211a0dB474446aBasWETH0xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb718aBascbBTC0xBdb9300b7CDE636d9cD4AFF00f6F009fFBBc8EE68aBasUSDC0x4e65fE4DbA92790696d040ac24Aa414708F5c0AB6
TypeAddressPUT0x7355EB92dfb0503DB558a70c10843618932ab290INVERSE_CALL0xE6c5756b0289e3f0994CB12eb8aB71Cd903Ed0EaLINEAR_CALL0x051791df68223AE173Fade5217C48875e36eef61CALL_SPREAD0xfaeD63f7040E65b79cF0Ae29706fDc423eE249A9PUT_SPREAD0x02Fe0d9635e0139DBB3768a5d5Db404Fd84d9134INVERSE_CALL_SPREAD0x7Be48100b1B0349528A96D64953295Cd0Bbe4B70CALL_FLY0xa1d5f6b16A2e7f298F8d2cDF78F7779B4A20C4C2PUT_FLY0x4fd2C6D271cC6FF3EbD2027da9815a0608d03AA3CALL_CONDOR0x14476CF2ea9F7C448100F061670E390f17c78817PUT_CONDOR0xC742E422c7BB43A7FDe1CEF47997bC9D5b543cDDIRON_CONDOR0x9ebd7E23AfD52a48F557523019285EfEF2170D59RANGER0x9980ec85bc6fE07340adb36c76FA093bb6D4FcBcCALL_LOAN0x7c444A2375275DaB925b32493B64a407eE955DEdPHYSICAL_CALL0x8c56100caE246f7daa4BC1EC4d1477d71178c563PHYSICAL_PUT0x6aD53DD058bea004829cCf58a282C21a7Df02DcA
Physical multi-leg variants ( PHYSICAL_CALL_SPREAD, PHYSICAL_PUT_SPREAD, PHYSICAL_*_FLY, PHYSICAL_*_CONDOR, PHYSICAL_IRON_CONDOR) are placeholders — the SDK throws if a quote routes through them.
The SDK's optionImplementations map still resolves these for parsing pre-r12 positions/events. Do not route new RFQs through them — quotes will reach a deprecated contract.
Type8453_v6 (legacy)Base_r10 (legacy)PUT0xF480F636301d50Ed570D026254dC5728b746A90F``0x64b4b21bf0845c79661f60ed48aa24d54bf74bb5INVERSE_CALL0x3CeB524cBA83D2D4579F5a9F8C0D1f5701dd16FE``0x1fdec69e5ac4fa9cb7092f381c2dd5688759d43cCALL_SPREAD0x4D75654bC616F64F6010d512C3B277891FB52540``0x2db5afa04aee616157beb53b96612947b3d13ee3PUT_SPREAD0xC9767F9a2f1eADC7Fdcb7f0057E829D9d760E086``0x571471b2f823cc6b5683fc99ac6781209bc85f55CALL_FLY0xD8EA785ab2A63a8a94C38f42932a54A3E45501c3``0xb727690fdd4bb0ff74f2f0cc3e68297850a634c5PUT_FLY0x1fE24872Ab7c83BbA26Dc761ce2EA735c9b96175``0x78b02119007f9efc2297a9738b9a47a3bc3c2777CALL_CONDOR0xbb5d2EB2D354D930899DaBad01e032C76CC3c28f``0x7d3c622852d71b932d0903f973caff45bcdba4f1PUT_CONDOR0xbdAcC00Dc3F6e1928D9380c17684344e947aa3Ec``0x5cc960b56049b6f850730facb4f3eb45417c7679IRON_CONDOR0x494Cd61b866D076c45564e236D6Cb9e011a72978``0xb200253b68fbf18f31d813aecef97be3a6246b79PHYSICAL_CALL0x07032ffb1df85eC006Be7c76249B9e6f39b60F32``0x025a8ef95f8939ffdba6a45973a28695846e9e45PHYSICAL_PUT0xAC5eCA7129909dE8c12e1a41102414B5a5f340AA``0x2d283d7ade2896d98331496ee761f15ed1d6a699
AssetAddressETH0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70BTC0x64c911996D3c6aC71f9b455B1E8E7266BcbD848FSOL0x975043adBb80fc32276CbF9Bbcfd4A601a12462DDOGE0x8422f3d3CAFf15Ca682939310d6A5e619AE08e57XRP0x9f0C1dD78C4CBdF5b9cf923a549A201EdC676D34BNB0x4b7836916781CAAfbb7Bd1E5FDd20ED544B453b1PAXG0x5213eBB69743b85644dbB6E25cdF994aFBb8cF31AVAX0xE70f2D34Fd04046aaEC26a198A35dD8F2dF5cd92
Updates are user-initiated only — the agent must never run update.sh
automatically. Updates change code that signs transactions, so they require
explicit consent each time.
To check for or apply an update:
bash
bash scripts/update.sh
The agent should surface the script's full output, summarize any version change, and ask the user to confirm before any code-changing step runs.
Optional flags:
REFRESH_WDK_DEPS=1 - Refresh dependencies from lockfileUPGRADE_WDK_DEPS=1 - Upgrade dependency versionsRESTART_WDK_RUNTIME=1 - Best-effort restart of WDK runtimeUpdates NEVER modify wallet secrets ( .env, WDK_SEED).
Options use: {UNDERLYING}-{EXPIRY}-{STRIKE}-{TYPE}
Examples:
ETH-28MAR26-2500-P = ETH Put, $2500 strike, March 28 2026 expiryBTC-28MAR26-95000-C = BTC Call, $95000 strike, March 28 2026 expiry