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:

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 exerciseOption to swap settlement for collateral (any amount, any time before endTimestamp).
  • 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: 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 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 ID, 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, txHash }.
  4. Terminal events. rfqCancelled and rfqExpired broadcasts close the loop for everyone else.

State machines, as stored by the backend:

EntityStatuses
RFQopenquotedaccepted | cancelled | expired
Quoteopenaccepted | rejected | expired

Wire format notes

Two representations of the same data cross the network:

  • Wire detailsOptionDetails 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 and uncurateOptionDetails convert curated shapes back into signable OptionDetails.

All payloads are validated with zod schemas exported from @overcast/core (createRfqPayloadWireSchema, quoteNewPayloadSchema, …), so you can reuse the exact contract in your own services.