与流动性提供方的 RFQ Swap
状态:规范(LP 协议 v1,2026-09-23 草案)。 本节是 Rocky 正在按其实现的接口契约。除非在 变更日志 中公告,v1 的路径、字段名与状态值不再变动;标注 v2 的内容不属于首个版本。
外部机构在 Rocky 提供流动性有两种方式:
| 订单簿做市 | RFQ Swap(本节) | |
|---|---|---|
| 提供流动性的方式 | 用自己的 party 在现货订单簿挂限价单 | 应答询价;每笔交易是一次私下的、按报价成交的 swap |
| 谁发起交易 | 任何吃掉你挂单的 taker | 使用 Swap 页或钱包内 swap 功能的交易者 |
| 你的接入面 | 作为客户端使用现货 REST/WS(做市商指南) | 一个由你托管、由 Rocky 调用的 HTTPS 服务(LP 服务接口),以及你调用的 Rocky 接口(面向 LP 的接口) |
| Last look | 无:挂单即承诺 | 有:在 accept-and-allocate 逐笔接受或拒绝 |
| 结算 | 在你的 MemberAuth 下两腿 DvP,按对手方对净额 | 双方各自以 Token Standard allocation 锁定,Rocky 在双方 MemberAuth 下一笔交易执行 |
| 交易所手续费 | 钱包托管段为零 | 交易者付 taker 费;v1 中 maker 费为 0 |
| 你在链上签什么 | MemberAuth 一次 | 接入时签一次 MemberAuth,之后每接受一笔交易签一次 AllocationFactory_Allocate |
两种模式并存,同一家机构可以用同一个 party 同时运行两者。
参与方
| 参与方 | Canton party | 在一笔 swap 中的角色 |
|---|---|---|
| 交易者(钱包提供方) | 用户自己的 party,私钥在钱包 | 请求报价,把输入金额锁进 allocation,swap 未结算时解锁。从不与 LP 通信。 |
| Rocky(场所与执行方) | 操作员 party Rocky::…(executor);手续费腿收款用手续费 party | 向 LP 广播询价、选最优报价、对照冻结报价核验双方 allocation、一笔原子执行所有转账腿、记录成交。 |
| 流动性提供方(LP) | 你的 party,托管在你运营的 validator 或钱包 | 对询价定价;接受交易时把输出金额(以及非零时的 maker 费)锁进 allocation。 |
Rocky 从不持有 LP 的库存或密钥;结算前 LP 也从不持有交易者资金:在 Allocation_ExecuteTransfer 执行之前,双方的 allocation 仍归各自所有,过 了结算期限即可撤回。
时序
交易者钱包 Rocky LP 服务 Canton
│ GET 报价 │ │ │
│─────────────────────►│ POST /rfq/quotes(所有 LP) │ │
│ │───────────────────────────────────►│ │
│ 最优报价, rfqId │◄───────────────────────────────────│ │
│◄─────────────────────│ │ │
│ POST /v1/swaps/rfq │ │ │
│─────────────────────►│ swapId, 需签名的 allocation │ │
│ prepare → 签名 → execute(AllocationFactory_Allocate:输入 + taker 费) │
│──────────────────────────────────────────────────────────────────────────────────►│
│ │ POST /rfq/trades/{swapId}/accept-and-allocate │
│ │───────────────────────────────────►│ Allocate(输出) │
│ │ │──────────────────────►│
│ │ ALLOCATED + allocation cid │ │
│ │◄───────────────────────────────────│ │
│ │ 对所有腿 Allocation_ExecuteTransfer,一笔交易 │
│ │───────────────────────────────────────────────────────────►│
│ SUCCEEDED, updateId │ POST /rfq/trades/{swapId}/result │ │
│◄─────────────────────│───────────────────────────────────►│ │
- 报价。 交易者向 Rocky 询价(
symbol、side、baseQty)。Rocky 向该交易对上所有活跃 LP 各发一次POST /rfq/quotes,最多等待报价超时时间,然后以rfqId返回最优可成交价,有效期数秒。交易者看到的是含费用的到手金额。 - 创建。 交易者接受报价:
POST /v1/swaps/rfq把价格、金额、费用与结算窗口冻结到一个swapId上,并精确返回钱包需要 allocate 的内容。 - 交易者 allocate。 钱包执行
AllocationFactory_Allocate:输入金额(receiver 为 LP party)与 taker 费(receiver 为 Rocky 手续费 party),executor 为 Rocky 操作员 party,settlementRef.id = swapId。Rocky 可以为外部签名准备好这笔交易,钱包也可以自己构造。 - LP 接受并 allocate。 Rocky 在账本上核验交易者的 allocation 与冻结行一致,然后调用你的
accept-and-allocate。你有 last look:要么把输出金额 allocate 给交易者(同一 executor、同一settlementRef)并回复ALLOCATED,要么回复REJECTED。 - 结算。 Rocky 在你的
MemberAuth上执行Auth_OpenAllocations(peer 为交易者的MemberAuth),在一笔 Canton 交易里对每一腿执行Allocation_ExecuteTransfer。要么全部转移,要么全不转移。 - 回报。 交易者轮询得到
SUCCEEDED与cantonUpdateId;Rocky 把结果推送到你的服务,并可在GET /v1/lp/trades/{swapId}查询。
资金流向
交易者 ──[baseQty 或 quoteQty,即输入]──► LP
交易者 ──[taker 费]────────────────────► Rocky 手续费 party
LP ──[输出金额]─────────────────────► 交易者
LP ──[maker 费,v1 为 0]── ──────────► Rocky 手续费 party
金额在创建 swap 时固定,之后不再重算:交易者的 execute 与你的 accept-and-allocate 对照同一冻结行核验,Allocation_ExecuteTransfer 精确转移每个 allocation 中的数量。任一方 allocate 的金额、资产或收款方不同,Rocky 就不结算。
费用
| 腿 | 付费方 | v1 取值 | 说明 |
|---|---|---|---|
| taker 费 | 交易者 | 现货 taker 费率(5 bps)按输入计,加上 Swap 页固定的网络执行成本(按报价折算成输入资产;计价资产不是美元代币的市场为 0) | 两者都以输入资产计,合并为一条手续费腿 allocate 给手续费 party,LP 始终把全部输出付给交易者 |
| maker 费 | LP | 0 bps | 每条消息中都保留该字段,未来引入非零或负值(返佣)不需要改协议 |
| Canton 流量 | 各参与方为自己提交与确认的部分付费 | — | 你为自己的 Allocate 与结算确认付费;Rocky 为执行交易付费。见 Canton 流量。 |
时间窗口
| 窗口 | 默认 | 含义 |
|---|---|---|
| 报价超时 | 800 ms | Rocky 等待 POST /rfq/quotes 的时长;迟到或非 200 视为无报价 |
报价有效期(validUntil) | 由你决定,≤ 5 s | 过期报价不会被用来创建 swap |
| 接受超时 | 5 s | Rocky 等待 accept-and-allocate 的时长 |
allocateBefore | 创建 + 60 s | 双方 allocation 必须在此之前上链,否则 swap 为 EXPIRED |
settleBefore | 创建 + 120 s | 此后 allocation 可由发送方撤回;Rocky 的 executor 会在此之前尽量取消 |
状态机
QUOTED ──► TRADER_ALLOCATING ──► TRADER_ALLOCATED ──► LP_ACCEPTING ──► LP_ALLOCATED ──► SETTLING ──► SUCCEEDED
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
EXPIRED EXPIRED EXPIRED LP_ACCEPT_FAILED CANCELLED SETTLEMENT_FAILED
| 状态 | 终态 | 交易者输入 | LP 输出 | 接下来 |
|---|---|---|---|---|
QUOTED | 否 | 未锁 | — | 钱包尚未 allocate |
TRADER_ALLOCATING | 否 | 提交中 | — | Rocky 等待 allocation 在账本可见 |
TRADER_ALLOCATED | 否 | 已锁 | — | Rocky 正在核验 |
LP_ACCEPTING | 否 | 已锁 | — | 正在调用你的服务 |
LP_ALLOCATED | 否 | 已锁 | 已锁 | Rocky 正在提交结算 |
SETTLING | 否 | 已锁 | 已锁 | 结算已提交,等待确认;瞬时错误会重试 |
SUCCEEDED | 是 | 已转移 | 已转移 | cantonUpdateId 已写入 |
EXPIRED | 是 | 未锁或可撤回 | — | allocateBefore 前没有 allocation |
LP_ACCEPT_FAILED | 是 | 可撤回 | — | 你拒绝、超时或 allocation 不匹配;Rocky 取消交易者 allocation,钱包也可 Allocation_Withdraw |
SETTLEMENT_FAILED | 是 | 可撤回 | 可撤回 | 执行终态失败;Rocky 取消双方 allocation;settleBefore 后任一发送方可撤回 |
CANCELLED | 是 | 可撤回 | 可撤回 | Rocky 在结算前放弃(运营操作或核验不匹配) |
SETTLING 的重试策略与现货结算器一致:传输与竞争类瞬时错误在 settleBefore 窗口内按退避重试;Daml 授权失败或 allocation 缺失为终态。
记录方式
SUCCEEDED 的 swap 是一笔场所成交:与结算行同一事务写入成交日志,纳入下一次 epoch 承诺,在交易者的 GET /api/v3/myTrades 中以 source: "RFQ" 及其 cantonUpdateId 呈现,并在你的 GET /v1/lp/trades 中可查。它不是订单簿成交:不会出现在 @trade 流、K 线或 24 小时行情中。
本节页面
- LP 服务接口:由你实现、Rocky 调用的 HTTPS 服务。
- RFQ Swap 接口:Rocky 面向 LP 与钱包提供方的接口。
- 结算与接入:链上 choice 与签名方、失败处理、核验、沙盒与上线。