The RFQ Lifecycle
Everything in Overcast revolves around one data structure and one flow. This page explains both; the RFQ quoter guide turns them into code.
The data model
OptionDetails
OptionDetails is the canonical, signable description of an option's terms:
type OptionDetails = {
startTimestamp: number; // option becomes exercisable
endTimestamp: number; // option expires
premiumAsset: Id; // what the taker pays the maker up front
premiumAmount: bigint;
collateralAsset: Id; // what the maker locks up
collateralAmount: bigint;
settlementAsset: Id; // what the taker pays on exercise
settlementAmount: bigint;
settlementLayer: Id; // on-chain settlement-layer config committed to
domain: number;
};
Read it as: the maker locks collateralAmount of collateralAsset; the
taker pays premiumAmount of premiumAsset for the right to swap
settlementAmount of settlementAsset for that collateral until
endTimestamp. A covered call and a cash-secured put are the same structure
with the cash leg on different sides.
Offers and options
Each side of a trade is expressed as an offer over the same details:
CollateralOffer— the maker's side:{ maker, expiry, details, salt }SettlementOffer— the taker's side:{ taker, expiry, details, salt }
Matching a collateral offer with a settlement offer whose details agree mints
a MarketOption on-chain. An option is always identified by its pair of
offer IDs — there is no standalone option ID on-chain.
CollateralOffer (maker) ┐
├──► acceptOffer ──► MarketOption
SettlementOffer (taker) ┘
Once minted, the option is managed through claims:
- The taker holds exercise claims and calls
exerciseOptionto swap settlement for collateral (any amount, any time beforeendTimestamp). - The maker holds redeem claims and calls
redeemOptionto pull their collateral (plus any settlement paid in) back out.
Canonical IDs and signatures
Offers and options are content-addressed: getId serializes the struct
into a fixed byte layout (byte-for-byte identical to the on-chain Rust
implementation), sha256-hashes it, and base58-encodes the result. Every field
— including the random salt and the expiry — is part of the hash, so an
ID commits to the complete terms.
Authorization in the RFQ flow is a plain ed25519 signature over the offer's
canonical ID, produced by the OvercastProtocolSigner. There are no session
tokens: your public key identifies you, your signature over the ID proves you
authored the offer.
Offer expiry
Offers carry a unix-seconds expiry. If you don't set one, the SDK defaults
to one hour from now (DEFAULT_EXPIRY_DURATION). The minted option itself
has no offer expiry — its own endTimestamp governs its life.
Settlement layers
The settlementLayer field commits the option to an on-chain settlement
configuration. The SDK ships a registry (app.settlement) with two known
kinds:
"physical"— direct asset-for-asset settlement (the default)."oracle"— oracle-gated settlement derived from the assets' price feeds.
Pick the config before signing — the writer commits whatever the offer carries, verbatim:
details.settlementLayer = app.settlement.defaultConfig(details);
The RFQ flow
RFQ is the off-chain price-discovery channel: a socket.io namespace on the Overcast backend that brokers quote requests between the two sides.
Participants register a role:
MAKER— will end up on the collateral side (e.g. writes the covered call).TAKER— will end up on the settlement side, pays the premium. Market makers register asTAKER: they buy the option.
The flow, end to end:
- Create. The creator sends an RFQ with partial details — all the
terms except the premium — via
createRfq. The server assigns anrfqIdand broadcastsnewRfqto the opposite side. - Quote. Responders price the request and call
submitQuotewith the completeOptionDetails(their premium filled in). Under the hood the SDK builds the responder's offer, content-addresses it withgetId, signs the ID, and sends it. The server broadcastsnewQuoteback to the creator. - Accept. The creator picks a quote and calls
acceptQuote. The SDK builds and signs the creator's opposite offer, and the backend settles the pair on-chain inline — the acknowledgment carries{ optionId, txHash }. - Terminal events.
rfqCancelledandrfqExpiredbroadcasts close the loop for everyone else.
State machines, as stored by the backend:
| Entity | Statuses |
|---|---|
| RFQ | open → quoted → accepted | cancelled | expired |
| Quote | open → accepted | rejected | expired |
Wire format notes
Two representations of the same data cross the network:
- Wire details —
OptionDetailswith thebigintamounts as decimal strings (JSON can't carry bigints). The SDK converts withamountToWire/amountFromWire; you always work withbigint. - Curated details — broadcast payloads (
newRfq,newQuote) resolve raw asset addresses into fullAssetmetadata (symbol, decimals, price hints).completeOptionDetailsanduncurateOptionDetailsconvert curated shapes back into signableOptionDetails.
All payloads are validated with zod schemas exported from @overcast/core
(createRfqPayloadWireSchema, quoteNewPayloadSchema, …), so you can reuse
the exact contract in your own services.