Skip to main content

RFQ Swap with Liquidity Providers

Status: Specification (LP Protocol v1, draft 2026-09-23). This section is the contract Rocky is building against. Endpoint paths, field names and status values are frozen for v1 unless a change is announced in the Changelog; anything marked v2 is not part of the first release.

Rocky supports two ways for an external firm to provide liquidity:

Order-book market makingRFQ Swap (this section)
How you provide liquidityRest limit orders on the Spot order book from your own partyAnswer quote requests; each trade is a private, quote-driven swap
Who initiates a tradeAny taker hitting your orderA trader using the Swap page or a wallet's swap feature
Your integration surfaceSpot REST/WS as a client (Market Maker Guide)An HTTPS service you host that Rocky calls (LP Service API), plus Rocky endpoints you call (Rocky endpoints for LPs)
Last lookNone: a resting order is a commitmentYes: you accept or reject each trade at accept-and-allocate
SettlementTwo-leg DvP under your MemberAuth, netted per counterparty pairToken Standard allocations locked by each side, executed by Rocky in one transaction under both sides' MemberAuth
Exchange feeMaker/taker fee on the wallet-funded segment (see Fees)Taker fee paid by the trader; maker fee 0 in v1
What you sign on-chainMemberAuth onceMemberAuth once at onboarding, then one AllocationFactory_Allocate per trade you accept

The two models coexist. The same firm may run both from the same party.

Actors​

ActorCanton partyRole in a swap
Trader (wallet provider)The user's own party, keys in the walletRequests a quote, locks the input amount in an allocation, unlocks it if the swap does not settle. Never talks to the LP.
Rocky (venue and executor)Operator party Rocky::… (executor); fee collector party for fee legsFans the RFQ out to LPs, picks the best quote, verifies both allocations against the frozen quote, executes the transfer legs atomically, records the trade.
Liquidity provider (LP)Your party, hosted on a validator or wallet you operatePrices requests, locks the output amount (plus maker fee, when non-zero) in an allocation when you accept a trade.

Rocky never holds LP inventory or LP keys. The LP never holds trader funds before settlement: until Allocation_ExecuteTransfer runs, each side's allocation is still owned by that side and can be withdrawn after the settlement deadline.

Sequence​

Trader wallet Rocky LP service Canton
│ GET quote │ │ │
│─────────────────────►│ POST /rfq/quotes (all LPs) │ │
│ │───────────────────────────────────►│ │
│ best quote, rfqId │◄───────────────────────────────────│ │
│◄─────────────────────│ │ │
│ POST /v1/swaps/rfq │ │ │
│─────────────────────►│ swapId, allocation to sign │ │
│ prepare → sign → execute (AllocationFactory_Allocate: input + taker fee) │
│──────────────────────────────────────────────────────────────────────────────────►│
│ │ POST /rfq/trades/{swapId}/accept-and-allocate │
│ │───────────────────────────────────►│ Allocate (output) │
│ │ │──────────────────────►│
│ │ ALLOCATED + allocation cids │ │
│ │◄───────────────────────────────────│ │
│ │ Allocation_ExecuteTransfer × all legs, one transaction │
│ │───────────────────────────────────────────────────────────►│
│ SUCCEEDED, updateId │ POST /rfq/trades/{swapId}/result │ │
│◄─────────────────────│───────────────────────────────────►│ │
  1. Quote. The trader asks Rocky for a quote (symbol, side, baseQty). Rocky sends one POST /rfq/quotes to every LP active on that symbol, waits at most the quote timeout, and returns the best executable price as an rfqId valid for a few seconds. The trader sees an all-in amount.
  2. Create. The trader accepts the quote: POST /v1/swaps/rfq freezes the price, amounts, fees and settlement window on a swapId and returns exactly what the wallet must allocate.
  3. Trader allocates. The wallet exercises AllocationFactory_Allocate for the input amount (receiver: LP party) and the taker fee (receiver: Rocky fee collector), with Rocky's operator party as executor and settlementRef.id = swapId. Rocky can prepare this transaction for external signing or the wallet can build it itself.
  4. LP accepts and allocates. Rocky verifies the trader's allocation on ledger against the frozen row, then calls your accept-and-allocate. You have last look: you either allocate the output amount to the trader (same executor, same settlementRef) and answer ALLOCATED, or answer REJECTED.
  5. Settle. Rocky exercises Auth_OpenAllocations on your MemberAuth (peer: the trader's), which runs Allocation_ExecuteTransfer on every leg inside one Canton transaction. Either all legs move or none.
  6. Report. The trader's poll returns SUCCEEDED with the cantonUpdateId; Rocky posts the result to your service and it is queryable at GET /v1/lp/trades/{swapId}.

What moves​

Trader ──[baseQty or quoteQty, the input]──► LP
Trader ──[taker fee]────────────────────────► Rocky fee collector
LP ──[the output amount]────────────────► Trader
LP ──[maker fee, 0 in v1]───────────────► Rocky fee collector

Amounts are fixed when the swap is created and are never recomputed: the trader's execute and your accept-and-allocate are verified against the same frozen row, and Allocation_ExecuteTransfer moves exactly what each allocation holds. If either side allocates a different amount, instrument or receiver, Rocky does not settle.

Fees​

LegPayerv1 valueNotes
Taker feeTraderSpot taker rate (5 bps) on the input, plus the fixed network execution cost of the Swap page (converted into the input asset at the quoted price; 0 on markets whose quote asset is not a dollar token)Both are charged in the input asset and allocated together as one fee leg to the fee collector, so the LP always sends its full output to the trader
Maker feeLP0 bpsThe field exists in every message so a non-zero or negative (rebate) rate can be introduced without a protocol change
Canton trafficEach participant for what it submits and confirms—You pay for your Allocate and for confirming the settlement; Rocky pays for the execution transaction. See Canton traffic.

Timing​

WindowDefaultMeaning
Quote timeout800 msRocky waits this long for POST /rfq/quotes; late or non-200 answers are treated as no quote
Quote validity (validUntil)you choose, ≤ 5 sRocky will not create a swap on an expired quote
Accept timeout5 sRocky waits this long for accept-and-allocate
allocateBeforecreate + 60 sBoth allocations must exist on ledger by then; otherwise the swap is EXPIRED
settleBeforecreate + 120 sAfter this an allocation can be withdrawn by its sender; Rocky's executor cancels what it can before that

Status machine​

QUOTED ──► TRADER_ALLOCATING ──► TRADER_ALLOCATED ──► LP_ACCEPTING ──► LP_ALLOCATED ──► SETTLING ──► SUCCEEDED
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
EXPIRED EXPIRED EXPIRED LP_ACCEPT_FAILED CANCELLED SETTLEMENT_FAILED
StatusTerminalTrader inputLP outputWhat happens next
QUOTEDnofree—Wallet has not allocated yet
TRADER_ALLOCATINGnoin flight—Rocky is waiting for the allocation to be visible on ledger
TRADER_ALLOCATEDnolocked—Rocky is verifying the allocation
LP_ACCEPTINGnolocked—Your service is being called
LP_ALLOCATEDnolockedlockedRocky is submitting the settlement
SETTLINGnolockedlockedSettlement submitted, awaiting confirmation; retried on transient errors
SUCCEEDEDyestransferredtransferredcantonUpdateId set
EXPIREDyesfree, or withdrawable—No allocation by allocateBefore
LP_ACCEPT_FAILEDyeswithdrawable—You rejected, timed out, or your allocation did not match; Rocky cancels the trader allocation, the wallet can also Allocation_Withdraw
SETTLEMENT_FAILEDyeswithdrawablewithdrawableExecution failed terminally; Rocky cancels both allocations; either sender can withdraw after settleBefore
CANCELLEDyeswithdrawablewithdrawableRocky abandoned before settling (operator action or verification mismatch)

Retry policy on SETTLING follows the Spot settler: transient transport and contention errors are retried with backoff inside the settleBefore window; a Daml authorization failure or a missing allocation is terminal.

What is recorded​

A SUCCEEDED swap is a venue trade: it is written to the trade journal in the same transaction as the settlement row, committed on-chain in the next epoch commitment, reported to the trader in GET /api/v3/myTrades with source: "RFQ" and its cantonUpdateId, and reported to you at GET /v1/lp/trades. It is not an order-book fill: it does not print on @trade streams, klines or the 24 h ticker.

Pages in this section​