Fill options RFQ quotes
Options RFQs are short quote auctions. A taker opens a window with shared intents + polarity. Makers compete with signed leg_by_leg (or package) offers. The taker picks an offer and settles on-chain.
The trading app today settles the common path with mothership executeIntent (maker-signed leg-by-leg FillMatched, optional CLOB book takes). On-chain there are also FixedPackage, Dutch auction, and Authority-gated fills — see On-chain fill modes. How the resting book relates: CLOB.
Lifecycle
Taker: POST /api/quote
→ API broadcasts rfq_request on quotes:<chain_id>:requests
→ Taker joins rfq_stream:<stream_token>
Maker: receives rfq_request (no taker address)
→ builds request + EIP-712 maker_signature
→ POST /api/quote/:id
Taker: receives rfq_offer on private stream
→ selects offer in UI / bot
→ wallet: executeIntent(...) // leg-by-leg FillMatched
For Dutch (taker-signed) or FixedPackage / Authority settlement, use the mothership entrypoints in On-chain fill modes instead of (or in addition to) this API auction shape. API-brokered Dutch competition (dutch_only create + both-side bids): API quotes — Dutch. Trading UI premium-range fallback: Trading app — Dutch.
Create quote (taker) — reference
POST /api/quote (no login)
{
"chain_id": 31337,
"taker_address": "0x...",
"intents": [
{
"opshuns6909_contract": "0x...",
"option_type": "call",
"structure": 0,
"start_exercise_timestamp": 0,
"expiry_timestamp": 1785950335,
"strike_price": "1000000000000000000",
"amount": "1000000000000000000"
}
],
"polarity": "a_is_buy",
"expires_in": 60,
"legByLegOnly": true
}
Response 201 includes:
quote— id, directions,taker_address_hash, salts, state…stream_token— secret for the taker stream / GETtopic—rfq_stream:<stream_token>events—{ "offer": "rfq_offer", "result": "rfq_result" }
Polarity + structure determine per-leg Directions (Buy=0, Sell=1). Those directions are hashed into taker_address_hash with the taker address and salt — makers must copy taker_address_hash into the signed request exactly.
Named multi-leg strategies on the trading UI (straddle, butterfly, iron condor, …) expand into multiple intents with mixed structure values. The quote box currently sets legByLegOnly: true. For the exact per-strategy payloads and how polarity + structure are hashed with the taker address, see API-only option quotes. Payoff notes: Option spreads. App walkthrough: Trading app.
Maker: listen for requests
POST /api/pubsub/premiumwith{ "chain_id": 31337 }- Join Phoenix topic
quotes:31337:requests - Handle event
rfq_request
Filler payload includes intents, taker_address_hash, offer_formats, expiry — not the raw taker address.
Also join rfq_maker:<your_address> (with api_key for bots) for private outcomes on quotes you bid.
Maker: post an offer
POST /api/quote/:id
Headers: Authorization: Bearer <siwe-or-api-key>
{
"offer_type": "leg_by_leg",
"request": {
"intents": [ /* same intents as the RFQ */ ],
"premiums": [
{
"buy": { "premium_rate_bps": 150, "clob_offers": [] },
"sell": { "premium_rate_bps": 200, "clob_offers": [] }
}
],
"maker_expiry": 4294967295,
"taker_address_hash": "0x…",
"maker_nonce": "12345"
},
"maker_signature": "0x…"
}
For package offers, replace premiums with package_premium (token + A/B amounts and pay flags).
Requirements
- Maker rights (org admin or member key)
request.intentsmust match the open RFQtaker_address_hashmust match the RFQmaker_signaturemust be a valid EIP-712 signature over the mothership digest for that request- When
verify_offer_signaturesis enabled (default outside tests), bad signatures are rejected at the API
See EIP-712 signing.
Poll / stream as taker
- WebSocket:
rfq_stream:<stream_token>→rfq_offer,rfq_result - HTTP:
GET /api/quote/:id?stream_token=…(orX-Quote-Stream-Token)
On-chain settlement
Leg-by-leg FillMatched (standard)
Taker (or their relayer) calls mothership RfqFacet:
executeIntent(
request, // OpshunIntentRequestLegByLeg
makerSignature,
maker,
directions, // from the quote
takerSalt,
takerPermits, // empty ⇒ ERC-20 allowance pulls
referrer // address(0) ⇒ no referral fee
)
If the maker offer included clob_offers, the mothership takes that book size first, then fillMatched any remainder at the quoted bps. Details: CLOB, On-chain fill modes.
Framed embeds with ?referralAddress=0x… pass that wallet as referrer; otherwise use address(0).
The taker must approve the mothership for premium (and/or underlying, depending on directions) via ERC-20 transferFrom and/or Permit2. A non-zero referrer may pull a little extra on maker-paid legs on top of protocol fees.
Other fill modes
| Mode | When to use |
|---|---|
| FixedPackage | Maker-signed multi-leg with one absolute package premium → executeFixedPackageIntent |
| Dutch | Taker-signed decaying/ramping package premium → maker calls executeDutchIntent |
| Authority | Dutch / FixedPackage with requiredAuthority != 0 → *WithAuthority + EIP-712 AuthorityApproval |
Full tables, digests, and exclusive-fill rules: On-chain fill modes.
Tips
- Keep
maker_expiryas a futureuint32unix time. - Use a unique
maker_nonceper offer; reused nonces revert on-chain. - Prefer joining the filler feed before high-traffic hours so you do not race the quote TTL.
- Local fake MM signs with the Anvil maker key against the live mothership digest — mirror that in production bots.
- Optional
clob_offersmust fill exactly on-chain or the whole intent reverts.