Skip to main content
A negotiated quote commits live amounts, keys, timeouts, and route-specific construction data before settlement begins. This page defines the wire family that carries those quotes — one family for every route. Served today: the four BTC corridor pairs. The transport for routes whose endpoints both use arkade (arkadeSwapRequest) is specified and joins when served — Asset Swaps covers how that route sources its quote today.

Quote Lifecycle

Addressed and Published Requests

Addressed is the shipped path — build against it. Published mode is still in development: no client publisher exists, the reference deployment bids only on Lightning sends, and its payloads are not specified below, deliberately. Pinning rfq_open and rfq_bid field names before an implementation exists would publish a schema that the first working publisher then contradicts. The client work is tracked in ts-sdk#725; once it lands, dropping the transport’s solverPubkey is the entire client change. Neither a request nor a bid locks funds. A bid is signed and attributable but not bonded. The signed quote returned by the selected solver is the binding RFQ message.

Relay

RFQ messages may travel through Nostr relays. Client and solver each dial out; neither needs to accept inbound connections. A relay transports messages but does not select a solver, aggregate quotes, or touch funds. The negotiation kinds sit in NIP-01’s ephemeral range: relays deliver them to live subscribers and store nothing, so an expired negotiation leaves no public record. The cost is no store-and-forward — a request sent while the solver is offline is dropped, which the client timeout and retry cover. The advertisement is replaceable: standing state a relay keeps the current version of. Kind numbers remain provisional.

Message Family

Every payload carries the envelope {"v": 1, "type": …}. Addressed payloads are transport-independent. The reference service accepts HTTP and Nostr traffic; clients can implement another transport while preserving the same payloads.

Payload Schemas

Field names below are exact. The reference solver validates a request strictly: an unknown field, a misspelled one, or a missing required one refuses the whole request rather than being ignored — a wrong name and an absent field fail identically. These are the addressed-mode payloads. rfq_open and rfq_bid are work in progress and are not specified here — see Addressed and Published Requests.

Encodings

Both scripts commit to ripemd160(sha256(P)), derived locally from payment_hash on each side; the hex on the wire is the sha256 digest alone. Client-supplied keys are x-only. Solver-supplied keys — solver_pubkey, htlc_pubkey, claim_pubkey — may also arrive as 33-byte compressed keys, and the reference client drops the prefix before using them.

Request Envelope

rfq_id is the client’s, not the solver’s — generate it before sending and correlate every reply against it. A reply whose rfq_id differs is a reply to a different negotiation and must not be read as this one’s answer. The BOLT11 send profile is the one route that omits amount: the invoice fixes it, so the request pins amount_side to to and restates nothing.

arkade:BTC → lightning:BTC

arkade:BTC → onchain:BTC

lightning:BTC → arkade:BTC

P never travels in the clear. The solver receives the hash and the sealed packet; it learns the preimage only when a claim publishes it.

onchain:BTC → arkade:BTC

Routes whose endpoints both use arkade

Specified, not served — see Asset Swaps for how this route sources its quote today. The pair string stays coarse and the asset ids ride the profile, mirroring the offer TLV.
Exactly one of want_asset and offer_asset is set per request — the BTC side names no asset. want_asset describes an arkade:BTC->arkade:ASSET request, offer_asset the reverse; each is the hex-serialized asset id.

Quote

rfq_id and pair must equal the request’s. refund_locktime is present on every HTLC-class quote and absent where both endpoints use arkade. The spread between from_amount and to_amount is the complete cost — there is no fee field. The quote profile carries the route’s construction data: Which of these bind and which are compared is set out in Quote Boundary and, per direction, in Derive, Never Accept.

Refusal

reason comes from a closed set: unsupported_pair, unsupported_payload, amount_out_of_range, exposure_cap, invoice_expired, quote_conflict, pricing_unavailable. Treat an unrecognized reason as a generic decline — the set may grow, and a refusal is never an error to retry blindly.

Status

state is one of the wire states below. Status is best effort: a timeout, a missing response, or an unknown state means unknown, never failed.

Transport Framing

The payloads above are what a transport carries; neither transport changes a field. A 4xx carrying an rfq_refusal body is the refusal path, so the status code alone cannot decide whether to read the body — parse it, and fall back to the status code only when the body is not a payload of this family. On Nostr the client signs with a transport key it may generate fresh per negotiation; nothing in the protocol needs a stable client identity, since the quote binds to the contract rather than to who asked for it. Replies arrive on a subscription filtered to that key and are correlated by rfq_id.

RFQ Wire States

The client-side RfqSwapManager monitors funded swaps from chain evidence with its own states rather than consuming these wire states; its failed outcome is client-side reporting.

Quote Boundary

Economic commitments stay at the top level. Settlement construction data stays inside profile. Profile fields are also checked against client-owned inputs such as an invoice, payment hash, destination, and network. Recheck time-sensitive conditions immediately before value moves.

Transport Requirements

Regardless of transport implementation:
  • reconnect after both connection errors and clean closes;
  • restore live subscriptions on a new connection;
  • bound and persist or reject outbound events queued while disconnected;
  • correlate every response with the expected RFQ identifier and route;
  • treat a status timeout as unknown, not failed.
Contract and destination-network evidence remain authoritative.

Persistence Requirements

Persist recovery data before the route’s first enforceable commitment. At a minimum, retain:
  • route;
  • swap identifier or RFQ identifier;
  • encoded terms and verified solver identity;
  • derived script or contract data;
  • funding transaction identifier;
  • expiry and refund timing;
  • wallet-owned key or preimage derivation data;
  • destination-network identifiers required for reconciliation.
Some generated secrets do not exist anywhere else. Losing them can remove an interactive refund or claim path.

Current Boundary

The reference RFQ service implements the four BTC corridor profiles but is closed source. See Implementation Status before building against the wire protocol.