Skip to main content

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:

import type { Bytes32 } from "@overcast-xyz/core";

type OptionDetails = {
startTimestamp: number; // option becomes exercisable (unix seconds)
endTimestamp: number; // option expires (unix seconds)
premiumAsset: Bytes32; // what the taker pays the maker up front
premiumAmount: bigint;
collateralAsset: Bytes32; // what the maker locks up
collateralAmount: bigint;
settlementAsset: Bytes32; // what the taker pays on exercise
settlementAmount: bigint;
settlementLayer: Bytes32; // on-chain settlement-layer config committed to
domain: number;
borrowAllowed: boolean; // signable term: feeds getId + operation bytes
};

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. There is a single Offer type — { creator, expiry, details, salt, side } — with side: "MAKER" | "TAKER" selecting the collateral vs settlement side:

  • side: "MAKER" — the maker's side (posts collateral).
  • side: "TAKER" — the taker's side (pays the premium).

Matching a collateral offer with a settlement offer whose details agree mints a MarketOption on-chain. An option carries an optionId derived from both offer IDs (plus parties + terms) — there is no independent id apart from that derivation.

Offer (MAKER) ┐
├──► matchOffer ──► MarketOption
Offer (TAKER) ┘

Once minted, the option is managed through claims:

  • The taker holds exercise claims and calls exerciseOption to swap settlement for collateral (any amount up to the exercise-claim balance, while startTimestamp <= now <= endTimestamp and with borrows repaid).
  • The maker holds redeem claims and calls redeemOption to pull their collateral (plus any settlement paid in) back out.

Canonical IDs and signatures​

Offers and options are content-addressed: async getId serializes the struct into a fixed byte layout (byte-for-byte identical to the on-chain implementations), sha256-hashes it, and returns a 0x-hex Bytes32 (const id = await getId(offer)). Every field — including the random salt, the expiry, and borrowAllowed — is part of the hash, so an ID commits to the complete terms.

Authorization in the RFQ flow is a signature over the CreateOfferOperation digest (which embeds the offer bytes: domain-separated "overcast_intent" + version + domain + curve + action + expiry + nonce + offer bytes), produced by the OvercastProtocolSigner (ed25519 or secp256k1 depending on the chain). There are no session tokens: your public key identifies you, your signature over the operation proves you authored the offer.

Offer expiry​

Offers carry a unix-seconds expiry. If you don't set one, the SDK defaults to 60 seconds from now (DEFAULT_EXPIRY_DURATION). The minted option itself has no offer expiry — its own endTimestamp governs its life.

Settlement Methods​

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 (Gated or Intrinsic mode).

Pick the config before signing — the writer commits whatever the offer carries, verbatim:

details.settlementLayer = await app.settlement.defaultConfig(details);

How these layers compose into the five product methods (Physical · Raw and Physical · Gated share capital; Intrinsic; Access × both) is covered in Settlement Methods.

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 as TAKER: they buy the option.

The flow, end to end:

  1. Create. The creator sends an RFQ with partial details — all the terms except the premium — via createRfq. The server assigns an rfqId and broadcasts newRfq to the opposite side.
  2. Quote. Responders price the request and call submitQuote with the complete OptionDetails (their premium filled in). Under the hood the SDK builds the responder's offer, content-addresses it with getId, signs the CreateOfferOperation digest, and sends it. The server broadcasts newQuote back to the creator.
  3. 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, txHashes }.
  4. Terminal events. rfqCancelled and rfqExpired broadcasts close the loop for everyone else.

State machines, as stored by the backend:

EntityStatuses
RFQopen → quoted → accepted | cancelled | expired
Quoteopen → accepted | rejected | expired

Wire format notes​

Two representations of the same data cross the network:

  • Wire details — OptionDetails with the bigint amounts as decimal strings (JSON can't carry bigints). The SDK converts with amountToWire / amountFromWire; you always work with bigint.
  • Curated details — broadcast payloads (newRfq, newQuote) resolve raw asset addresses into full Asset metadata (symbol, decimals, price hints). completeOptionDetails(details, premium, utils) (or (await app.curator()).completeOptionDetails(details, premium)) and uncurateOptionDetails(details, utils) convert curated shapes back into signable OptionDetails.

All payloads are validated with zod schemas exported from @overcast-xyz/core (createRfqPayloadWireSchema, submitQuotePayloadWireSchema, …; note quoteNewPayloadSchema is the curated newQuote broadcast shape, not the client→server wire shape), so you can reuse the exact contract in your own services.