API-only option quotes
You do not need the trading UI to open an options RFQ. A bot or script posts POST /api/quote with the pair contract, intents (one per leg), and polarity. The API derives per-leg directions from polarity + each intent’s structure, then hashes those directions together with the taker address and a salt. Makers never see the raw taker address — they copy taker_address_hash into the signed offer.
No login is required to create a quote. Login is required only if you later post a maker offer.
For payoff intuition and strike ordering, see Option spreads. For filling as a maker, see Fill options RFQ quotes. On-chain FillMatched / Dutch / Authority: On-chain fill modes. Resting book: CLOB.
Lifecycle (API only)
1. Resolve the pair (CLOB) address for token0 / token1 / premium on the chain
2. POST /api/quote { chain_id, taker_address, polarity, intents, expires_in }
3. Response: quote (id, directions, taker_address_hash, taker_salt, …)
stream_token, topic rfq_stream:<stream_token>
4. Join the Phoenix topic (or poll GET /api/quote/:id with the stream token)
5. Collect rfq_offer events; pick one
6. Wallet: mothership executeIntent(request, makerSignature, maker, directions, takerSalt, takerPermits, referrer)
POST /api/quote is public. GET /api/quote/:id requires stream_token as ?stream_token= or X-Quote-Stream-Token.
Request body
{
"chain_id": 31337,
"taker_address": "0xYourTaker",
"polarity": "b_is_buy",
"expires_in": 60,
"legByLegOnly": true,
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "1000000000000000000"
}
]
}
| Field | Meaning |
|---|---|
chain_id | Must match a registry network (GET /api/networks) |
taker_address | Wallet that will call executeIntent. Hashed into taker_address_hash; not broadcast on the maker feed |
polarity | "b_is_buy" = buy / long / bullish the named strategy. "a_is_buy" = sell / short / bearish it |
expires_in | Quote window in seconds (typically ≤ 300) |
legByLegOnly / packageOnly / offer_formats | Which maker offer shapes to accept |
intents | One shared intent per leg, all on the same opshuns6909_contract |
Intent fields
| Field | Notes |
|---|---|
opshuns6909_contract | Pair / CLOB address (factory-deployed Opshuns core for that token0–token1–premium triple) |
option_type | "call" / 0 or "put" / 1 |
structure | 0 / "a" follows polarity. 1 / "b" flips that leg |
start_exercise_timestamp | 0 = American (anytime until expiry). Equal to expiry_timestamp = European (expiry day only) |
expiry_timestamp | Unix seconds |
strike_price | Integer string in premium token decimals (USDC-6 → 2500 USDC = "2500000000") |
amount | Integer string in underlying decimals (WETH-18 → 1 WETH = "1000000000000000000"). Butterfly body uses 2× |
Polarity, structure, and the address hash
On-chain, a maker offer binds to:
takerAddressHash(address taker, uint256 salt, uint8[] directions) → bytes32
(OpshunsRFQ / mothership. Directions: Buy = 0, Sell = 1.)
The API computes directions[i] from quote polarity and intent structure, then hashes them with taker_address and taker_salt. The result is quote.taker_address_hash.
| Polarity | Structure | Direction | Taker on that leg |
|---|---|---|---|
b_is_buy | 0 | 1 (Sell) | Pays premium — long the option |
b_is_buy | 1 | 0 (Buy) | Posts underlying — short the option |
a_is_buy | 0 | 0 (Buy) | Posts underlying — short the option |
a_is_buy | 1 | 1 (Sell) | Pays premium — long the option |
Structure 0 follows polarity. Structure 1 flips the leg.
To sell a named strategy, keep the same structure values as the buy recipe below and set "polarity": "a_is_buy". Do not invert structure yourself — polarity already flips every leg’s direction.
Makers must copy taker_address_hash from the RFQ into the signed request exactly. They never receive taker_address.
Shared constants used in the examples
Replace addresses, timestamps, and decimals with live registry / token metadata.
chain_id 31337
opshuns6909_contract 0xPairClob
taker_address 0xYourTaker
expiry_timestamp 1785950335
start_exercise_timestamp 0 # American; use expiry for European
amount (1×) 1000000000000000000 # 1 underlying unit, 18 decimals
Strike strings below assume a 6-decimal premium token.
Single
One call or put. structure: 0 (the only leg follows polarity).
Buy 1 ETH 2500 call — polarity: "b_is_buy"
{
"chain_id": 31337,
"taker_address": "0xYourTaker",
"polarity": "b_is_buy",
"expires_in": 60,
"legByLegOnly": true,
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "1000000000000000000"
}
]
}
Use "option_type": "put" for a put. Sell the same option with "polarity": "a_is_buy" and the same intent.
Derived direction for this buy call: [1] (taker pays premium). taker_address_hash = takerAddressHash(taker, salt, [1]).
Straddle
Long call + long put at the same strike. Both legs structure: 0.
Buy 2500 straddle
"polarity": "b_is_buy",
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "1000000000000000000"
},
{
"opshuns6909_contract": "0xPairClob",
"option_type": "put",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "1000000000000000000"
}
]
Buy directions: [1, 1]. Sell (a_is_buy): same intents, directions [0, 0].
Strangle
OTM put + OTM call at different strikes. Put strike below call strike. Both long when buying → structure: 0.
Buy 2000 / 3000 strangle
"polarity": "b_is_buy",
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "put",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2000000000",
"amount": "1000000000000000000"
},
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "3000000000",
"amount": "1000000000000000000"
}
]
Butterfly
Three strikes of the same type, ascending: low < mid < high. Buying: long wings (structure: 0), short 2× body (structure: 1).
Buy 2000 / 2500 / 3000 call butterfly (put butterfly: "option_type": "put" on every leg)
"polarity": "b_is_buy",
"intents": [
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2000000000",
"amount": "1000000000000000000"
},
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 1,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "2500000000",
"amount": "2000000000000000000"
},
{
"opshuns6909_contract": "0xPairClob",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "3000000000",
"amount": "1000000000000000000"
}
]
Buy directions: [1, 0, 1] (long, short, long). The middle amount is twice the 1× size.
Condor
Four strikes of the same type: outer low < inner low < inner high < outer high. Buying: long outer wings (0), short inner strikes (1). All amounts 1×.
Buy call condor 1800 / 2200 / 2800 / 3200
"polarity": "b_is_buy",
"intents": [
{ "option_type": "call", "structure": 0, "strike_price": "1800000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "2200000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "2800000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 0, "strike_price": "3200000000", "amount": "1000000000000000000" }
]
(Each object also needs opshuns6909_contract, start_exercise_timestamp, and expiry_timestamp as in the full examples above.)
Buy directions: [1, 0, 0, 1].
Iron butterfly
Long OTM put wing, short ATM put, short ATM call, long OTM call wing. Body strike is shared. Order: put wing < body < call wing.
Buying the named iron butterfly is the debit (long wings, short body). structure: 1 on the two short body legs.
Buy iron butterfly (put wing 2000, body 2500, call wing 3000)
"polarity": "b_is_buy",
"intents": [
{ "option_type": "put", "structure": 0, "strike_price": "2000000000", "amount": "1000000000000000000" },
{ "option_type": "put", "structure": 1, "strike_price": "2500000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "2500000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 0, "strike_price": "3000000000", "amount": "1000000000000000000" }
]
Buy directions: [1, 0, 0, 1]. Sell (a_is_buy) is the credit iron butterfly (short wings, long body) with the same structures.
Iron condor
Long far put, short inner put, short inner call, long far call. Order: long put < short put < short call < long call.
Buy iron condor (1800 / 2200 / 2800 / 3200)
"polarity": "b_is_buy",
"intents": [
{ "option_type": "put", "structure": 0, "strike_price": "1800000000", "amount": "1000000000000000000" },
{ "option_type": "put", "structure": 1, "strike_price": "2200000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "2800000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 0, "strike_price": "3200000000", "amount": "1000000000000000000" }
]
Buy directions: [1, 0, 0, 1].
Risk reversal
Bullish: long call + short put. Bearish: long put + short call (same intents, polarity: "a_is_buy"). Put strike below call strike.
The short put is the flipped leg when buying/bullish → structure: 1. The long call follows polarity → structure: 0.
Bullish risk reversal (put 2000, call 3000)
"polarity": "b_is_buy",
"intents": [
{ "option_type": "put", "structure": 1, "strike_price": "2000000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 0, "strike_price": "3000000000", "amount": "1000000000000000000" }
]
Buy/bullish directions: [0, 1] (short put, long call).
Collar
Long collar: long put + short call (classic protective collar). Short collar: reverse via a_is_buy. Put strike below call strike.
Long collar (put 2000, call 3000)
"polarity": "b_is_buy",
"intents": [
{ "option_type": "put", "structure": 0, "strike_price": "2000000000", "amount": "1000000000000000000" },
{ "option_type": "call", "structure": 1, "strike_price": "3000000000", "amount": "1000000000000000000" }
]
Buy/long directions: [1, 0] (long put, short call).
Structure cheat sheet (buy / long / bullish)
polarity is always "b_is_buy" in this table. Selling the same strategy: same rows, "polarity": "a_is_buy".
| Strategy | Legs (option_type, structure, amount) |
|---|---|
| Single | (call or put, 0, 1×) |
| Straddle | (call, 0, 1×), (put, 0, 1×) same strike |
| Strangle | (put, 0, 1×), (call, 0, 1×) put < call |
| Butterfly | (type, 0, 1×) low, (type, 1, 2×) mid, (type, 0, 1×) high |
| Condor | (type, 0, 1×) outer low, (type, 1, 1×) inner low, (type, 1, 1×) inner high, (type, 0, 1×) outer high |
| Iron butterfly | (put, 0, 1×) wing, (put, 1, 1×) body, (call, 1, 1×) body, (call, 0, 1×) wing |
| Iron condor | (put, 0, 1×) long, (put, 1, 1×) short, (call, 1, 1×) short, (call, 0, 1×) long |
| Risk reversal | (put, 1, 1×), (call, 0, 1×) |
| Collar | (put, 0, 1×), (call, 1, 1×) |
After create
201 body includes:
quote.directions—uint8[]used on-chain and in the hashquote.taker_address_hash—takerAddressHash(taker_address, taker_salt, directions)quote.taker_saltstream_token/topic— private offer stream
curl -sS https://api.staging.opshuns.com/api/quote \
-H 'Content-Type: application/json' \
-d @quote.json
Then subscribe:
Phoenix /socket → join rfq_stream:<stream_token>
events: rfq_offer, rfq_result
Or poll:
GET /api/quote/<id>
X-Quote-Stream-Token: <stream_token>
Settlement still uses the quote’s directions and taker_salt in executeIntent. Approve the mothership (or factory pull spender) for premium and/or underlying according to those directions before sending the transaction.
The trading UI’s post to book path (single calls/puts after an empty RFQ window) does not use this endpoint — it posts an unfilled order on the pair CLOB. See Orders & exclusive fills and Trading app.
Dutch auction (dutch_only)
Create a taker-signed Dutch competition instead of a standard RFQ. Same POST /api/quote endpoint; detection is via offer_formats: "dutch_only" (or a present dutch_request).
{
"chain_id": 31337,
"taker_address": "0xYourTaker",
"offer_formats": "dutch_only",
"directions": [1],
"expires_in": 300,
"compete_ms": 30000,
"exclusive_seconds": 5,
"taker_signature": "0x…",
"dutch_request": {
"intents": [/* same intent shape as standard RFQ */],
"tokens": [{ "token0": "0x…", "token1": "0x…", "premium_token": "0x…" }],
"dutch_premium": {
"token": "0x…",
"maker_pays_taker": false,
"start_premium": "1000000",
"end_premium": "500000",
"decay_rate": "1667",
"auction_start": 1785950000
},
"auction_end": 1785950300,
"maker_address_hash": "0x…",
"taker_nonce": "1785950000123456",
"required_authority": "0x0000000000000000000000000000000000000000",
"required_dutch_authority": "0xMothership",
"referrer": "0x0000000000000000000000000000000000000000"
}
}
| Field | Meaning |
|---|---|
directions | Explicit Buy/Sell per leg (revealed to the API; not broadcast to makers while dutch_phase is competing) |
taker_signature | EIP-712 signature over mothership digestDutchFillRequest (domain OpshunsDutchFill / 1) recovering to taker_address |
dutch_request | Normalized on-chain DutchFillRequest (snake_case). Intents are taken from here |
compete_ms | Optional competition window (clamped to auction TTL) |
exclusive_ms / exclusive_seconds | Optional exclusive award window (admin max applies) |
Competition binding (auctioneer path)
For API-brokered competition (what the trading UI uses):
maker_address_hash = directionsHash(directions)(polarity-only; not a bound maker)required_dutch_authority = mothership(any registered dutch auctioneer)required_authority = 0unless you also require KYC fill-authority
Signing reveals trade side because directions are committed into the hash and posted with the quote.
Phases
dutch_phase | Behavior |
|---|---|
competing | Public maker feed hides directions / executable payload; makers POST both-side bids on POST /api/quote/:id/offer (buy_override_bps, sell_override_bps) |
exclusive | Auctioneer awards a winner; fillable payload published; preferred maker receives private exclusive_salt |
open | Same public fillable; any eligible maker may fill (soft-override / sniping rules apply) |
Taker stream (rfq_stream:<stream_token>) receives rfq_offer for dutch bids and rfq_request / poll updates when the phase advances. On-chain settlement: On-chain fill modes — Dutch. Trading UI: Dutch auction (UI).
Resolve the pair contract
opshuns6909_contract is the deployed pair for (token0, token1, premiumToken) on that chain — not the underlying ERC-20. Discover factory / token addresses from GET /api/networks, then read the pair from the factory (or create it first if it is not deployed). All legs of a spread must use the same pair address and the same expiry.