兌換交易¶
網路與相容性
| 資源 | 值 |
|---|---|
| API 基礎 URL | https://api.sera.cx/api/v1 |
| 鏈 | Ethereum 主網(chainId 1) |
| Sera 合約 | 0xB5C50C5D5f038404F85970b7f5B7259C4AC0E198 |
| Vault 合約 | 0xC7d4Fd2638e6630C8C61329878676b88A8A24D43 |
| SOR 合約 | 0xa7A0cf7cd6f043fCA23f29d8ae5aae6b46e11c18 |
簽名原語。 所有交易型變更都是針對 Sera domain 的 EIP-712 類型化資料簽名。走 permit 路徑的充值使用 ERC-2612 Permit 擴充功能 — USDC、EURC 以及許多現代穩定幣支援,但並非所有 ERC-20 都支援;簽名前請呼叫 GET /permit/metadata 檢查。API key 管理使用 EIP-712 ManageApiKey payload。
已驗證客戶端。 Python eth_account >= 0.10 + requests;TypeScript ethers v6(signer.signTypedData)。已驗證支援 EIP-712 類型化資料的瀏覽器錢包:MetaMask、Rabby、Frame、Coinbase Wallet、Trust、Rainbow。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)
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.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_usd和gas_cost_from_tokenexpires_at— 報價到期時間(報價為一次性使用)
步驟二:簽署報價¶
使用錢包透過 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);
步驟三:執行兌換¶
提交已簽署的報價:
Gas 模式¶
與限價單(需要以真實 ETH 支付 Gas)不同,兌換的 Gas 費用由伺服器自動計入報價中。您無需持有 ETH 即可執行兌換 — Gas 被吸收到代幣金額中。
請求報價時,您可以選擇 Gas 費用的應用方式:
| 模式 | 行為 |
|---|---|
receive_less | Gas 費用從輸出中扣除。您花費的金額恰好是 from_amount,但收到的金額略少。 |
pay_more | Gas 費用加到輸入中。您收到完整的報價金額,但花費略多。 |
報價回應中的 fee_breakdown 顯示確切的 Gas 費用,不會有意外。伺服器會計算調整後的金額 — 您的前端只需按原樣簽署 route_params 即可。
多段路由¶
Sera 會自動為您的兌換找到最佳路徑。如果直接交易對不存在,或多跳路徑能提供更好的定價,兌換會透過中間貨幣透明地進行路由。
例如,GBP → SGD 的兌換可能執行為:
- GBP → USD
- USD → SGD
這是原子性的 — 要麼所有段都成功,要麼全部不執行。
降低 MEV 暴露¶
Sera 的兌換並不是像公共 AMM 兌換那樣執行的。報價生成、路由和撮合都在 Sera 的 Web2 引擎中鏈下完成,而 Ethereum 只作為最終結算層使用。
這意味著使用者不會把一筆開放式市價兌換訂單暴露到公共 mempool 中進行價格發現。相反,您會先請求報價,再簽署精確的 route_params,然後結算要麼在已簽署的邊界內執行,要麼直接失敗。
已簽署的 Intent 包含 maxInputAmount、minOutputAmount、一次性 uuid 和 deadline。由於交易並不是在公共 mempool 中被發現和重新定價,兌換對開放式 AMM 市價單常見的夾擊攻擊路徑暴露更低。
錯誤處理¶
| HTTP 狀態碼 | 含義 |
|---|---|
| 200 | 兌換已接受並正在處理 |
| 400 | 請求參數無效 |
| 410 | 報價已過期(請重新請求報價) |
| 429 | 超過速率限制(等待後重試) |
| 503 | 服務暫時不可用 |