跳轉至

兌換交易

網路與相容性

資源
API 基礎 URL https://api.testnet.sera.cx/api/v1
Sepolia(chainId 11155111
Sera 合約 0x83475A1bD98a8DC2DCd507A747e4DC85da241D6e
Vault 合約 0x3c7945840bAE0d7e7f3824Ebccef1962629250F0
SOR 合約 0x83c1368110B640A729f3810De5FBe94b99aa5668

簽名原語。 所有交易型變更都是針對 Sera domain 的 EIP-712 類型化資料簽名。走 permit 路徑的充值使用 ERC-2612 Permit 擴充功能 — USDC、EURC、EURT 以及多數現代穩定幣支援,但並非所有 ERC-20 都支援;簽名前請呼叫 GET /permit/metadata 檢查。API key 管理使用 EIP-712 ManageApiKey payload。

已驗證客戶端。 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 簽名 payload 接受 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, quote_breakdown)
    User->>User: Sign route_params (EIP-712)
    User->>API: POST /swap (uuid + signature)
    API->>Chain: Settlement
    Chain-->>API: Confirmation
    API-->>User: Success (trade_id)

步驟一:請求報價

import time, requests

quote = requests.post(
    "https://api.testnet.sera.cx/api/v1/swap/quote",
    json={
        "from_token":    "0x965d4b...d0e",    # USDC address
        "to_token":      "0xef64d1...40f",    # 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["quote_breakdown"] — 用於顯示的扣 Gas 前後金額
# quote["fee_breakdown"]   — 舊版 Gas 摘要(gas_cost_usd、gas_cost_from_token)
const response = await fetch("https://api.testnet.sera.cx/api/v1/swap/quote", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    from_token:    "0x965d4b...d0e",    // USDC address
    to_token:      "0xef64d1...40f",    // 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.quote_breakdown — 用於顯示的扣 Gas 前後金額
// 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 結構欄位
  • quote_breakdown — 僅用於顯示的扣 Gas 前後金額,使用原始代幣單位
  • fee_breakdown — 舊版 Gas 摘要:gas_cost_usdgas_cost_from_token
  • expires_at — 報價到期時間(報價為一次性使用)

步驟二:簽署報價

使用錢包透過 EIP-712 類型資料簽名對 route_params 進行簽署:

from eth_account import Account
from eth_account.messages import encode_typed_data

DOMAIN = {
    "name": "Sera",
    "version": "1",
    "chainId": 11155111,                                              # Sepolia
    "verifyingContract": "0x83475A1bD98a8DC2DCd507A747e4DC85da241D6e",
}

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: 11155111,                                                  // Sepolia
  verifyingContract: "0x83475A1bD98a8DC2DCd507A747e4DC85da241D6e",
};

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);

步驟三:執行兌換

提交已簽署的報價:

swap = requests.post(
    "https://api.testnet.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.testnet.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 費用加到輸入中。您收到完整的報價金額,但花費略多。

報價回應中的 quote_breakdown 會以原始代幣單位顯示扣 Gas 前金額、Gas 金額和扣 Gas 後金額。前端可用它顯示金額。fee_breakdown 仍作為舊版 Gas 摘要,顯示 USD 和輸入代幣顯示單位。

前端應嚴格依照回傳值簽署 route_params,不要用顯示欄位重新計算簽署金額。

多段路由

Sera 會自動為您的兌換找到最佳路徑。如果直接交易對不存在,或多跳路徑能提供更好的定價,兌換會透過中間貨幣透明地進行路由。

例如,GBP → SGD 的兌換可能執行為:

  1. GBP → USD
  2. USD → SGD

這是原子性的 — 要麼所有段都成功,要麼全部不執行。

為什麼 MEV 不是問題

Sera 兌換是先報價再簽名結算的流程。使用者不會把開放式市價單廣播到公共市場中進行價格發現。

這意味著使用者不會把一筆開放式市價兌換訂單暴露到公共 mempool 中進行價格發現。相反,您會先請求報價,再簽署精確的 route_params,然後結算要麼在已簽署的邊界內執行,要麼直接失敗。

已簽署的 Intent 包含 maxInputAmountminOutputAmount、一次性 uuiddeadline。由於交易並不是在公共 mempool 中被發現和重新定價,常見的夾擊攻擊路徑並不存在。

錯誤處理

HTTP 狀態碼 含義
200 兌換已接受並正在處理
400 請求參數無效
410 報價已過期(請重新請求報價)
429 超過速率限制(等待後重試)
503 服務暫時不可用