跳转至

虚拟流动性

虚拟流动性(Virtual Liquidity, VL)允许您在多个不同交易对上同时挂出限价单,并让这些订单共享同一笔预算。系统冻结的是单个订单中的最大成本,而不是所有订单成本之和,因此更适合做市和多市场报价。

为什么选择虚拟流动性?

设想一位做市商希望同时在 EURC/USDCGBPC/USDCXSGD/USDC 上挂买单。如果没有 VL,每个订单都需要独立锁定抵押品,3 个订单就意味着 3 倍资金占用。使用 VL 时,一笔 USDC 预算即可为这三个订单提供支持。当其中一个订单成交后,其余订单会自动调整,以确保总花费始终在预算内。

方式 所需资金 风险
3 个独立订单 所有订单成本之和 每个订单独立锁定
1 个 VL 批次 单个订单中的最大成本 共享预算,自动调整

它如何运作

一个 VL 批次包含:

  • 一个 VL 批次包含 2 到 50 个兄弟订单(运行时通过 GET /configlimits.vl_batch 获取当前上限)
  • 所有兄弟订单必须属于不同交易对
  • 整个批次必须共享同一个 spent token(实际被花掉的代币)
  • 当其中一个订单成交后,其余订单会自动缩量或取消,确保总花费不超过共享预算
flowchart TD
  A[提交 VL 批次] --> B[冻结共享预算]
  B --> C1[兄弟订单 1: EURC/USDC 买单]
  B --> C2[兄弟订单 2: GBPC/USDC 买单]
  B --> C3[兄弟订单 3: XSGD/USDC 买单]
  C1 -->|成交| D[扣减预算]
  D --> E{还有剩余预算吗?}
  E -->|有| F[调整兄弟订单 2 和 3]
  E -->|没有| G[取消兄弟订单 2 和 3]

共享预算与抵押品

VL 的关键在于兄弟订单位于不同的交易对上,因此任一时刻最多只有一个订单可以被撮合。这意味着 Vault 只需要冻结单个订单中的最大成本,而不是所有订单成本之和。

花费代币(fromToken)规则

VL 批次中的所有兄弟订单必须解析为相同的 fromToken——即实际被花掉的 ERC-20 代币。fromTokenside 与交易对定义共同决定,其中 from_address 是基础代币、to_address 是报价代币:

Side fromToken(花费) toToken(收到) 每次成交的成本
Bid (买入) to_address(报价代币) from_address(基础代币) quantity x fill_price
Ask (卖出) from_address(基础代币) to_address(报价代币) quantity

不会自动纠正

系统不会自动翻转 side 或交换交易对方向来让各兄弟订单的 fromToken 一致。fromToken 会根据提交的 sidefrom_addressto_address 推导;如果推导出的支出代币不一致,整个批次会直接返回 422

示例 1:同方向批次(全部 bid,花费 USDC)

  • Bid from_address: EURC, to_address: USDC @ 1.08 — fromToken = USDC
  • Bid from_address: GBPC, to_address: USDC @ 1.27 — fromToken = USDC
  • Bid from_address: XSGD, to_address: USDC @ 0.75 — fromToken = USDC

冻结金额:1,500 USDC(最大单个订单成本),而非 3,215 USDC。

示例 2:混合方向批次(均花费 USDC)

当 USDC 在一个交易对中是报价币、在另一个交易对中是基础币时,只要推导出的 fromToken 都是 USDC,仍可组成批次:

  • Bid from_address: MYRC, to_address: USDC @ 4.50 — fromToken = USDC ✅
  • Ask from_address: USDC, to_address: GBPC @ 0.79 — fromToken = USDC ✅

此批次有效,因为两个订单都花费 USDC。

示例 3:无效的混合方向批次(fromToken 不匹配)

  • Ask from_address: MYRC, to_address: USDC @ 4.50 — fromToken = MYRC ❌
  • Ask from_address: GBPC, to_address: USDC @ 1.27 — fromToken = GBPC ❌

尽管这两个市场都以 USDC 计价,但一个兄弟订单实际支出 MYRC,另一个实际支出 GBPC。该批次将被拒绝并返回 422

下单

通过 POST /orders/vl/batch 提交所有兄弟订单:

const response = await fetch('https://api.sera.cx/api/v1/orders/vl/batch', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    orders: [
      {
        owner_address: walletAddress,
        side: 'bid',
        amount: '1000.0',
        price: '1.08',
        order_type: 'limit',
        from_address: EURC_ADDRESS,
        to_address: USDC_ADDRESS,
        order_id: eurOrderId,         // UUID,必须与签名值一致
        uuid_int: eurUuidInt,         // 由 executor_id + UUID bits 生成的组合 uint256
        signature: eurSig,            // EIP-712 Order 签名
        expiration: Math.floor(Date.now() / 1000) + 86400
      },
      {
        owner_address: walletAddress,
        side: 'bid',
        amount: '500.0',
        price: '1.27',
        order_type: 'limit',
        from_address: GBPC_ADDRESS,
        to_address: USDC_ADDRESS,
        order_id: gbpOrderId,
        uuid_int: gbpUuidInt,
        signature: gbpSig,
        expiration: Math.floor(Date.now() / 1000) + 86400
      },
      {
        owner_address: walletAddress,
        side: 'bid',
        amount: '2000.0',
        price: '0.75',
        order_type: 'limit',
        from_address: XSGD_ADDRESS,
        to_address: USDC_ADDRESS,
        order_id: sgdOrderId,
        uuid_int: sgdUuidInt,
        signature: sgdSig,
        expiration: Math.floor(Date.now() / 1000) + 86400
      }
    ]
  })
});

const batch = await response.json();
// batch.order_ids — 系统接受的兄弟订单 ID 列表

每个兄弟订单都必须带上自己的有效 expiration,并遵守与普通限价单相同的有界未来时间规则。

成交与调整流程

当某个兄弟订单成交时,系统自动调整批次中的其余订单:

sequenceDiagram
  participant Sera
  participant Vault

  Sera->>Sera: 兄弟订单 1 成交(消耗 500 USD)
  Sera->>Sera: 预算:1500 -> 1000 USD
  alt 预算充足
    Sera->>Sera: 调整剩余兄弟订单数量以适配 1000 USD
  else 预算耗尽
    Sera->>Sera: 取消剩余兄弟订单
  end
  Sera->>Vault: 更新冻结余额

调整示例

初始预算:1,500 USD

  1. 兄弟订单 1(EURC/USDC Bid 1000 @ 1.08)部分成交 500 单位 — 消耗 540 USDC
  2. 剩余预算:960 USDC
  3. 兄弟订单 2(GBPC/USDC Bid 500 @ 1.27)被调整:新数量 = floor(960 / 1.27) = 755
  4. 兄弟订单 3(XSGD/USDC Bid 2000 @ 0.75)被调整:新数量 = floor(960 / 0.75) = 1280

每个兄弟订单独立受限于剩余预算。

取消 VL 批次

通过 POST /orders/vl/cancel 取消批次中的所有兄弟订单:

const cancelRes = await fetch('https://api.sera.cx/api/v1/orders/vl/cancel', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    owner_address: walletAddress,
    vl_batch_id: batch.order_ids[0],   // 批次的主订单 ID
    signature: cancelVlSig             // EIP-712 CancelVLBatch 签名
  })
});

取消行为:

  • 取消单个兄弟订单(通过 POST /orders/cancel)— 预算不会解冻(其余兄弟订单仍在挂单中)
  • 取消整个批次(通过 POST /orders/vl/cancel)— 所有兄弟订单被取消,剩余预算解冻
  • 所有兄弟订单成交或取消 — 一旦没有活跃的兄弟订单,剩余预算自动解冻

验证规则

VL 批次在下单时进行验证,必须满足以下约束:

规则 原因
所有兄弟订单属于不同交易对 防止同一订单簿上的竞态条件
所有兄弟订单共享相同的花费代币 确保单一预算可以支撑所有订单
每个批次 2 到 50 个兄弟订单 通过 GET /configlimits.vl_batch 读取当前值
每个兄弟订单是有效的限价单 适用标准订单验证

有效:

  • Bid EURC/USDC + Bid GBPC/USDC + Bid XSGD/USDC(全部 fromToken = USDC)
  • Ask USDC/GBPC + Bid MYRC/USDC(方向混合,但 fromToken 均为 USDC)

无效:

  • Bid EURC/USDC + Bid EURC/USDC(重复交易对)
  • Bid XSGD/USDC + Ask USDC/XSGD(正反向互逆市场)
  • Ask MYRC/USDC + Ask GBPC/USDC(fromToken = MYRC vs GBPC —— 不匹配,返回 422)

后续步骤