Settlement and Onboarding
Status: Specification (LP Protocol v1, draft 2026-09-23).
On-ledger model
RFQ swaps settle with the Canton Token Standard allocation workflow (CIP-0056), the same primitives Rocky's spot settler uses for its allocate-and-execute path. Each party locks its own leg in an Allocation naming Rocky's operator party as executor. Executing a registry allocation needs the authority of executor, sender and receiver, so the single settlement transaction is wrapped in the two parties' MemberAuth contracts (rocky-clob-v8 ≥ 0.7.0, Auth_OpenAllocations → Auth_RunAllocationsWith): both the trader's wallet and the LP grant MemberAuth once, at enrollment / onboarding, exactly as for order-book settlement. There is no Rocky-specific escrow contract.
| Choice | Signer | When | Effect |
|---|---|---|---|
AllocationFactory_Allocate | Trader (wallet) | After POST /v1/swaps/rfq | Locks the input amount (receiver: LP party) and the taker fee (receiver: Rocky fee collector) |
AllocationFactory_Allocate | LP | In accept-and-allocate | Locks the output amount (receiver: trader party) and the maker fee when non-zero |
MemberAuth Auth_OpenAllocations → Allocation_ExecuteTransfer × every leg | Rocky operator, with both MemberAuths | Once both sides are verified | Atomic transfer of all legs in one Canton transaction |
MemberAuth Auth_CancelAllocations → Allocation_Cancel | Rocky operator, with the sender's MemberAuth | On any terminal failure | Releases that member's locked allocations before settleBefore |
Allocation_Withdraw | Sender (trader or LP) | After settleBefore, if still locked | Sender releases its own allocation without Rocky |
Every allocation of a swap shares one SettlementInfo:
executor = Rocky operator party
settlementRef = { id: <swapId>, cid: None }
requestedAt = swap creation time
allocateBefore = requestedAt + 60 s
settleBefore = requestedAt + 120 s
meta.values = { "rocky.swapId": <swapId>, "rocky.symbol": <symbol> }
settlementRef.id = swapId is what binds the on-chain transaction to the trade you were shown, exactly as settlementRef.id = tradeId does for order-book fills. Rocky verifies each allocation on ledger before proceeding: instrument, amount, sender, receiver, executor and the full SettlementInfo must equal the frozen swap. Rocky never edits amounts to make a swap fit.
Instruments must support Token Standard allocations through their registry (Canton Coin and the CC-wrapped assets Rocky lists do). Receiver preapprovals are not required on this path.
Failure handling
| Failure | Detected by | Trader input | LP output |
|---|---|---|---|
| Trader never allocates | allocateBefore passes | free | — |
| Trader allocation mismatch | Rocky verification | executor Allocation_Cancel; wallet may Allocation_Withdraw after settleBefore | — |
| LP rejects / times out | accept-and-allocate | executor cancels | — |
| LP allocation mismatch or late | Rocky verification / allocateBefore | executor cancels | executor cancels; LP may withdraw after settleBefore |
| Settlement transient error | executor | stays locked while retrying inside settleBefore | same |
| Settlement terminal error | executor | executor cancels; withdrawable after settleBefore | same |
| Rocky unavailable mid-swap | — | withdrawable after settleBefore | withdrawable after settleBefore |
The windows are deliberately short so that a stuck swap never locks funds for more than two minutes. Because execution is one transaction, there is no state in which one side has paid and the other has not.
Verifying a swap on Canton
- Take
swapIdandcantonUpdateIdfromGET /v1/lp/trades/{swapId}(LP) orGET /v1/swaps/rfq/{swapId}(trader). - Open the update on a participant that hosts your party or in Canton Scan.
- The transaction's root is an
Auth_OpenAllocationsexercise on aMemberAuth(with a nestedAuth_RunAllocationsWith), containing oneAllocation_ExecuteTransferper leg (two legs plus one or two fee legs) and nothing else; every exercise carriessettlementRef.idequal toswapIdandexecutorequal to Rocky's operator party. - Amounts equal the
legsblock of the trade object.
A SUCCEEDED swap is also included in the next epoch commitment; GET /v1/chain/proof?ref_id=<swapId> returns its inclusion proof.
Onboarding
Commercial terms, symbols, sizes and support channels are captured in an Integration Pack. The technical steps:
| Step | You | Rocky |
|---|---|---|
| 1. Party | Provide the Canton party id that will hold inventory and its validator/wallet type | Binds an account to the party (Institutional Onboarding §2) |
| 2. API key | Register an Ed25519 public key (fingerprint read back over a second channel) | Registers the key on the ext-mm rate tier |
| 2b. MemberAuth | Complete MemberAuth for your party once (Institutional Onboarding §4); it authorises Rocky to execute allocations whose executor is Rocky and whose legs are between you and the trader, nothing else | Verifies the contract exists before the first RFQ is routed to you |
| 3. Service | Provide serviceUrl, egress IPs, TLS details (and mTLS client cert requirements, if any) | Registers the LP, publishes Rocky's signing key fingerprint to you |
| 4. Symbols | List symbols with minBaseQty / maxBaseQty | Configures fan-out; maxBaseQty caps a single swap |
| 5. Testing | Keep your service answering while Rocky drives test swaps | Registers you in TESTING: only Rocky's test traders reach you, with small sizes, on mainnet. Runs the go-live checklist below |
| 6. Go-live | Confirm the manual checklist items | Switches you to ACTIVE; public RFQs start reaching you |
There is no separate test network: the assets Rocky lists exist on Canton MainNet, so go-live testing runs on mainnet in TESTING status with small sizes (typically 0.00001 CBTC per swap). Nothing reaches you from the public until Rocky switches you to ACTIVE.
A reference LP service (signature verification, quote shaping, allocation through your JSON Ledger API) is in the Rocky API Maker Bot repository: python/rocky_spot/rfq_lp.py and python/examples/rfq_lp.py.
Service levels
Rocky measures per LP: quote answer rate and p95 latency, accept rate, mismatch rate, and settlement success. DEGRADED health (three consecutive failed probes) removes you from fan-out automatically. The other measures feed fanOutShare: an LP whose accept rate over the trailing hour falls below 80 % or whose mismatches exceed 1 % receives a smaller share of RFQs until the numbers recover. In v1 fanOutShare is adjusted by Rocky operations after review, not automatically. Thresholds are published in the Integration Pack and can only be tightened with notice.
Go-live checklist
GET /healthanswers in under 500 ms with your production party and symbols.- Quotes arrive within the quote timeout at p95 across a 30-minute sandbox run.
- Ten consecutive test swaps settle; each
cantonUpdateIdverified with the shape above. - One forced rejection returns
409 REJECTEDand the trader's allocation is released. - One forced timeout shows
LP_ACCEPT_FAILEDatGET /v1/lp/trades/{swapId}and no dangling allocation on your side. POST /v1/lp/pauseremoves you from fan-out within one second;resumerestores it.- Your validator's traffic balance monitoring is in place (Canton traffic).
What RFQ swaps are not
- They are not order-book trades: no
@tradeprint, no kline or ticker impact, no effect on the Spot leaderboard of order-book volume. - They are not custodial: Rocky holds neither side's assets at any point.
- They are not netted: every swap is its own transaction, so per-swap Canton traffic applies to both sides.