跳转至

即时兑换交易

网络与兼容性

资源
API 基础 URL https://api.sera.cx/api/v1
以太坊主网(chainId 1
Sera 合约 0xB5C50C5D5f038404F85970b7f5B7259C4AC0E198
Vault 合约 0xC7d4Fd2638e6630C8C61329878676b88A8A24D43
SOR 合约 0xa7A0cf7cd6f043fCA23f29d8ae5aae6b46e11c18

签名原语。 所有交易型变更都是针对 Sera 域的 EIP-712 类型化数据签名。走 permit 路径的充值使用 ERC-2612 Permit 扩展 — USDC、EURC 以及许多现代稳定币支持,但并非所有 ERC-20 都支持;签名前请调用 GET /permit/metadata 检查。API key 管理使用 EIP-712 ManageApiKey 载荷。

已验证客户端。 Python eth_account >= 0.10 + requests;TypeScript ethers v6(signer.signTypedData)。已验证支持 EIP-712 类型化数据的浏览器钱包:MetaMaskRabbyFrameCoinbase WalletTrustRainbow。Safe 多签通过 EIP-1271 支持(消息在链上验证,而非通过 ecrecover)。

地址大小写。 读取类端点(/balances/orders/fills)将 owner_address 视为大小写敏感 — 请传入小写形式。EIP-712 签名载荷接受 EIP-55 校验和地址。

即时兑换为希望立即以最佳可用价格交换代币的交易者提供即时执行。与限价单不同,即时兑换是 Fill-or-Kill 的 — 要么完全执行,要么被拒绝。

兑换流程

sequenceDiagram
    participant User
    participant API as Sera API
    participant Chain as Ethereum

    User->>API: POST /swap/quote
    API-->>User: Quote (uuid, route_params)
    User->>User: Sign route_params (EIP-712)
    User->>API: POST /swap (uuid + signature)
    API->>Chain: Settlement
    Chain-->>API: Confirmation
    API-->>User: Success (trade_id)

第 1 步:请求报价

import time, requests

quote = requests.post(
    "https://api.sera.cx/api/v1/swap/quote",
    json={
        "from_token":    "0x1920...F88",    # USDC address
        "to_token":      "0xd3Bd...779",    # EURC address
        "from_amount":   "1000000000",      # 1000 USDC(6 位小数)
        "owner_address": "0xYOUR_WALLET",
        "recipient":     "0xYOUR_WALLET",   # 接收输出代币的地址(可以是另一个钱包)
        "expiration":    int(time.time()) + 3600,
        "gas_mode":      "receive_less",
    },
    timeout=10,
).json()
# quote["uuid"]           — 唯一报价标识
# quote["route_params"]   — 需要签名的 EIP-712 参数
# quote["fee_breakdown"]  — Gas 费用详情(gas_cost_usd、gas_cost_from_token)
const response = await fetch("https://api.sera.cx/api/v1/swap/quote", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    from_token:    "0x1920...F88",    // USDC address
    to_token:      "0xd3Bd...779",    // EURC address
    from_amount:   "1000000000",      // 1000 USDC(6 位小数)
    owner_address: "0xYOUR_WALLET",
    recipient:     "0xYOUR_WALLET",   // 接收输出代币的地址(可以是另一个钱包)
    expiration:    Math.floor(Date.now() / 1000) + 3600,
    gas_mode:      "receive_less",
  }),
});

const quote = await response.json();
// quote.uuid            — 唯一报价标识
// quote.route_params    — 需要签名的 EIP-712 参数
// quote.fee_breakdown   — Gas 费用详情(gas_cost_usd、gas_cost_from_token)

报价参数

参数 类型 说明
from_token address 输入代币的 ERC-20 地址
to_token address 输出代币的 ERC-20 地址
from_amount string 原始代币单位的金额(例如 "1000000000" 表示 1000 USDC)
owner_address address 您的钱包地址
recipient address 输出代币的接收地址;可以是任意地址——设置为其他钱包即可将兑换输出发送给第三方。
expiration integer Unix 时间戳截止时间
gas_mode string "receive_less""pay_more"

报价响应

响应包含:

  • uuid — 此报价的唯一标识符(提交时使用)
  • route_params — 需要签名的 EIP-712 Intent 结构体字段
  • fee_breakdown — Gas 费用详情:gas_cost_usdgas_cost_from_token
  • expires_at — 报价过期时间(报价为一次性使用)

第 2 步:签署报价

使用您的钱包通过 EIP-712 类型化数据签名对 route_params 进行签名:

from eth_account import Account
from eth_account.messages import encode_typed_data

DOMAIN = {
    "name": "Sera",
    "version": "1",
    "chainId": 1,                                              # Mainnet
    "verifyingContract": "0xB5C50C5D5f038404F85970b7f5B7259C4AC0E198",
}

INTENT_TYPES = {
    "Intent": [
        {"name": "taker",                "type": "address"},
        {"name": "inputToken",           "type": "address"},
        {"name": "outputToken",          "type": "address"},
        {"name": "maxInputAmount",       "type": "uint256"},
        {"name": "minOutputAmount",      "type": "uint256"},
        {"name": "recipient",            "type": "address"},
        {"name": "initialDepositAmount", "type": "uint256"},
        {"name": "uuid",                 "type": "uint256"},
        {"name": "deadline",             "type": "uint48"},
    ]
}

signable  = encode_typed_data(DOMAIN, INTENT_TYPES, quote["route_params"])
signature = Account.from_key(PRIVATE_KEY).sign_message(signable).signature.hex()
import { Wallet } from "ethers";

const DOMAIN = {
  name: "Sera",
  version: "1",
  chainId: 1,                                                  // Mainnet
  verifyingContract: "0xB5C50C5D5f038404F85970b7f5B7259C4AC0E198",
};

const INTENT_TYPES = {
  Intent: [
    { name: "taker",                type: "address" },
    { name: "inputToken",           type: "address" },
    { name: "outputToken",          type: "address" },
    { name: "maxInputAmount",       type: "uint256" },
    { name: "minOutputAmount",      type: "uint256" },
    { name: "recipient",            type: "address" },
    { name: "initialDepositAmount", type: "uint256" },
    { name: "uuid",                 type: "uint256" },
    { name: "deadline",             type: "uint48"  },
  ],
};

const signature = await signer.signTypedData(DOMAIN, INTENT_TYPES, quote.route_params);

第 3 步:执行兑换

提交已签名的报价:

swap = requests.post(
    "https://api.sera.cx/api/v1/swap",
    json={"uuid": quote["uuid"], "signature": "0x" + signature.lstrip("0x")},
    timeout=10,
).json()
# swap["success"]  — 兑换是否被接受
# swap["trade_id"] — 用于追踪的唯一交易标识
const result = await fetch("https://api.sera.cx/api/v1/swap", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ uuid: quote.uuid, signature }),
});

const swap = await result.json();
// swap.success  — 兑换是否被接受
// swap.trade_id — 用于追踪的唯一交易标识

Gas 模式

与限价单(需要以真实 ETH 支付 Gas)不同,兑换的 Gas 费用由服务器自动计入报价中。您无需持有 ETH 即可执行兑换 — Gas 被吸收到代币金额中。

请求报价时,您可以选择 Gas 费用的应用方式:

模式 行为
receive_less Gas 费用从输出中扣除。您花费的恰好是 from_amount,但收到的略少。
pay_more Gas 费用添加到输入中。您收到完整的报价金额,但花费的略多。

报价响应中的 fee_breakdown 显示确切的 Gas 费用,不会有意外。服务器会计算调整后的金额 — 您的前端只需按原样签署 route_params 即可。

多段路由

Sera 自动为您的兑换找到最优路由。如果直接交易对不存在或多跳路径能提供更好的定价,兑换会透明地通过中间货币路由。

例如,GBP → SGD 的兑换可能执行为:

  1. GBP → USD
  2. USD → SGD

这是原子性的 — 要么所有段都成功,要么全部不执行。

降低 MEV 暴露

Sera 的即时兑换并不是像公共 AMM 兑换那样执行的。报价生成、路由和撮合都在 Sera 的 Web2 引擎中链下完成,而 Ethereum 只作为最终结算层使用。

这意味着用户不会把一笔开放式市价兑换订单暴露到公共内存池中进行价格发现。相反,您会先请求报价,再签署精确的 route_params,然后结算要么在已签名的边界内执行,要么直接失败。

已签名的 Intent 包含 maxInputAmountminOutputAmount、一次性 uuiddeadline。由于交易并不是在公共内存池中被发现和重新定价,兑换对开放式 AMM 市价单常见的夹击攻击路径暴露更低。

错误处理

HTTP 状态码 含义
200 兑换已接受并正在处理
400 请求参数无效
410 报价已过期(请重新请求报价)
429 超出频率限制(请等待后重试)
503 服务暂时不可用