账户端点¶
获取余额¶
获取账户的钱包余额和 Vault 余额汇总。
身份验证: 需要 API Key
查询参数¶
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
owner_address | address | 是 | 必须与已认证的 API Key 所有者匹配 |
include_zero | boolean | 否 | 默认 false:过滤普通零余额记录,但保留有恢复、同步或限制状态的零余额记录。true 包含全部白名单代币。 |
响应¶
{
"owner_address": "0x...",
"balances": [
{
"token": "0x965d4b4546716e416e950bc30467d128455d2d0e",
"symbol": "USDC",
"decimals": 6,
"wallet_balance": "1250000000",
"vault_available": "400000000",
"vault_frozen": "100000000",
"vault_total": "500000000",
"total": "1750000000",
"chain_verified": null,
"spendability_status": null,
"restriction_reason": null,
"recovery_status": null,
"projection_present": null,
"recovery_version": null,
"balance_sync_hint": null,
"coverage_block_number": null,
"coverage_block_hash": null,
"coverage_checked_at": null,
"verification_status": null,
"generation": null,
"snapshot_block_number": null,
"snapshot_block_hash": null,
"snapshot_policy_digest": null,
"projection_updated_at": null,
"vault_spendable": null
}
],
"updated_at": "2026-04-15T09:12:34.567890+00:00",
"wallet_balance_available": true,
"vault_balance_available": true
}
| 字段 | 类型 | 说明 |
|---|---|---|
token | address | ERC-20 合约地址 |
symbol | string | 代币符号 |
decimals | integer | 该代币的精度,用于把原始字符串转换成人类可读值 |
wallet_balance | string | 用户钱包余额(原始 uint256 十进制字符串) |
vault_available | string | 已提交账务的可用余额,不一定可花(原始 uint256 字符串)。 |
vault_frozen | string | Vault 中冻结在未结订单中的余额(原始 uint256 十进制字符串) |
vault_total | string | vault_available + vault_frozen |
total | string | wallet_balance + vault_total |
wallet_balance_available | boolean | 如果钱包余额 RPC 调用失败则为 false |
金额字段为原始 uint256 十进制字符串,使用 decimals 换算;可花资格与链上观察可用性需要分别判断。
可交易金额与恢复状态¶
vault_spendable 非 null 时表示本次返回的可花金额。vault_available 是已提交账务中的可用金额;恢复或限制期间它可以大于零,而 vault_spendable 为 "0"。余额读取不授权下单,最终准入仍会重新检查资格。旧版余额模式下新增的逐资产状态字段可以为 null;不能把 null 当成零或资格证明。
| 字段 | 类型 | 含义 |
|---|---|---|
vault_spendable | string / null | 资格检查后的原始可花金额;账务余额为正时仍可能为零。 |
spendability_status | string / null | 可花资格状态,包括 ready、local_covered、restricted、sync_required、current_coverage_required。 |
restriction_reason | string / null | 限制原因;未报告原因时为 null。 |
recovery_status | string / null | 恢复状态,包括 ready、pending、draining、recovering、proven、blocked。 |
projection_present | boolean / null | 是否存在该资产的已提交账务投影。 |
recovery_version | string / null | 不透明恢复版本;同步时传入当前值。 |
balance_sync_hint | string / null | none:未观察到差异;check_required:链上与账务有差异;unknown:缺少新鲜链上观察。它不是花费授权。 |
coverage_block_number / coverage_block_hash | integer / string / null | 当前覆盖证据的区块标识。 |
coverage_checked_at | string / null | 覆盖核验的 ISO 时间;读取缓存不会延长此证明的有效期。 |
verification_status | string / null | 账务快照验证状态,例如 verified 或 unverified;不能单独决定可否花费。 |
generation | integer / null | 账务快照代次。 |
snapshot_block_number / snapshot_block_hash | integer / string / null | 账务快照对应的区块标识。 |
snapshot_policy_digest | string / null | 快照关联的策略摘要。 |
projection_updated_at | string / null | 账务投影更新的 ISO 时间;不是链顶或本次核验时间。 |
外层 vault_balance_available 表示 Vault 链上观察是否可用且新鲜,即使 balances=[] 也有意义;它不同于快照 verification_status。local_covered 行可能已有可花金额,但历史 verification_status="unverified"。projection_updated_at 不代表证明的新鲜度。遇到未知状态应刷新,不能假定资金可花。
例如 vault_available="400000000"、vault_spendable="0"、spendability_status="restricted"、recovery_status="recovering" 可以同时成立。恢复状态为 pending/draining/recovering/proven/blocked、资格为 restricted/sync_required/current_coverage_required、balance_sync_hint="check_required",或已有投影且同步提示为 unknown 时,零余额行仍会保留。恢复流程见余额同步。
示例¶
import requests
balances = requests.get(
"https://api.testnet.sera.cx/api/v1/balances",
params={"owner_address": "0x..."},
headers={"Authorization": "Bearer sera_key:secret"},
timeout=10,
).json()["balances"]
for bal in balances:
print(f"{bal['symbol']}: available={bal['vault_available']}, frozen={bal['vault_frozen']}")
const response = await fetch(
"https://api.testnet.sera.cx/api/v1/balances?owner_address=0x...",
{ headers: { "Authorization": "Bearer sera_key:secret" } },
);
const { balances } = await response.json();
for (const bal of balances) {
console.log(`${bal.symbol}: available=${bal.vault_available}, frozen=${bal.vault_frozen}`);
}
请求余额同步¶
身份验证: 需要 API Key,owner_address 必须属于该 Key。先读取 GET /api/v1/config → capabilities.balances.sync_enabled。这是账务同步请求,不是充值或付款。
请求¶
{
"owner_address": "0x1111111111111111111111111111111111111111",
"token_address": "0x965d4b4546716e416e950bc30467d128455d2d0e",
"expected_recovery_version": null
}
owner_address 与 token_address 为 0x 前缀的 20 字节十六进制地址字符串,会规范化为小写。三个字段均必填,拒绝额外字段。先查询余额,将该资产的 recovery_version 作为 expected_recovery_version:没有版本时显式传 null,否则传 64 位小写十六进制字符串。不能省略或自行生成版本。
响应¶
HTTP 202:
{
"status": "queued",
"recovery_status": "pending",
"recovery_version": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
status 为 queued 或 already_pending;recovery_status 为 pending、draining、recovering 或 proven。轮询 GET balances 查看进度和可花资格;202 与 proven 本身不授权交易。不要为解决同步延迟而重复充值。
| HTTP | detail.code / 处理 |
|---|---|
| 400 | unknown_token:使用注册表中的代币 |
| 409 | balance_sync_unavailable:当前部署不接受同步 |
| 409 | balance_recovery_blocked:查看返回原因及余额状态,不要盲目重试 |
| 409 | balance_sync_version_changed:重新查询余额,再决定是否用当前版本重试 |
错误可能带可为空的 detail.recovery_version 与 detail.reason:vault_below_frozen、frozen_reservation_differs、chain_event_conflict、chain_snapshot_integrity_error 或 account_recovery_blocked。还需处理 401/403、422、429 和通用 503。属于 transfer 限流组(当前 2 req/s)。响应未知时先查询余额再重试;完成对账前保留已提交版本。
充值辅助端点¶
请先通过 GET /config 获取当前的 vault_address 和 sor_address。现在充值流程使用三个辅助端点:
- 如不使用 permit,先调用
POST /approve构建 ERC-20 授权交易。 - 调用
POST /deposit构建depositFund(...)或depositFundWithPermit(...)。 - 本地签名后,通过
POST /tx/send广播授权或充值交易。
如果代币支持 EIP-2612,您可以在 POST /deposit 中直接传入 permit_signature 与 permit_deadline,从而跳过单独的授权交易。若需要在客户端探测 permit 能力,请使用 GET /permit/metadata。
充值与提款可用性¶
展示操作前读取 /config:capabilities.deposits.enabled、.deposits.restriction_reason、.withdrawals.instant_enabled 与 .balances.sync_enabled。充值记账暂停时,/deposit 及 /tx/send 的充值分支返回 409、detail.code="deposit_accounting_paused",包括已签名但尚未广播的充值。不要创建另一笔充值,应等待能力恢复。该守卫不表示 /tx/send 的 approve 分支也被同样拒绝。
构建充值交易¶
身份验证: 需要 API Key
请求体¶
{
"token": "0x965d4b4546716e416e950bc30467d128455d2d0e",
"owner": "0x...",
"amount": "1000000000",
"permit_signature": "0x...",
"permit_deadline": 1713254400,
"permit_amount": "1000000000"
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
token | address | 是 | 要充值的 ERC-20 代币地址 |
owner | address | 是 | 充值人地址,必须与已认证 API Key 的所有者一致 |
amount | string | 是 | 原始 uint256 代币数量 |
permit_signature | hex string | 否 | 用于内联授权的 EIP-2612 permit 签名;支持标准 65 字节与紧凑 64 字节两种十六进制编码 |
permit_deadline | integer | 否 | permit 中签名使用的截止时间 |
permit_amount | string | 否 | permit 的原始授权额度;省略时默认等于 amount |
未提供 permit 字段时,构建器会生成 depositFund(token, owner, amount);提供 permit 字段时,会改为生成 depositFundWithPermit(...)。
响应¶
{
"tx": {
"to": "0x83475A1bD98a8DC2DCd507A747e4DC85da241D6e",
"data": "0x...",
"value": "0x0",
"chainId": "0xaa36a7",
"nonce": "0x2b",
"gas": "0x13880",
"type": "0x2",
"maxFeePerGas": "0x...",
"maxPriorityFeePerGas": "0x..."
}
}
构建授权交易¶
身份验证: 需要 API Key
请求体¶
{
"token": "0x965d4b4546716e416e950bc30467d128455d2d0e",
"owner": "0x...",
"spender": "0x3c7945840bAE0d7e7f3824Ebccef1962629250F0",
"amount": "1000000000"
}
spender 只能是 GET /config 返回的实时 Vault 或 SOR 地址;其他目标都会被拒绝。
响应¶
{
"tx": {
"to": "0x965d4b4546716e416e950bc30467d128455d2d0e",
"data": "0x...",
"value": "0x0",
"chainId": "0xaa36a7",
"nonce": "0x2a",
"gas": "0xc350",
"type": "0x2",
"maxFeePerGas": "0x...",
"maxPriorityFeePerGas": "0x..."
}
}
广播已签名的授权或充值交易¶
身份验证: 需要 API Key
请求体¶
响应¶
POST /tx/send 只接受选择器与目标地址组合在允许列表内的已签名授权/充值交易。
对于 approve,编码后的 spender 还必须是 GET /config 返回的实时 Vault 或 SOR 地址。如果提交的是 EIP-7702 的 type-4 交易,则每个授权 delegate 都必须在当前部署的允许列表内,否则请求会被拒绝。
提款(联合签名)¶
请求服务器联合签名以进行即时提款。Sera 的智能合约要求即时提款需要两个签名 — 您的签名和服务器的签名 — 以防止未经授权的提款。这是一个三步流程:获取联合签名、构建交易、然后广播。
第一步:获取服务器联合签名¶
身份验证: EIP-712 WithdrawIntent 签名
请求体¶
{
"intent": {
"user": "0x...",
"tokens": ["0x965d4b4546716e416e950bc30467d128455d2d0e"],
"amounts": ["1000000000"],
"recipient": "0x...",
"deadline": "1704153600",
"uuid": "123456789"
},
"user_signature": "0x..."
}
| 字段 | 类型 | 说明 |
|---|---|---|
intent.user | address | 提款用户的钱包 |
intent.tokens | address[] | 要提取的代币地址(1-20 个) |
intent.amounts | string[] | 每种代币的金额(原始单位) |
intent.recipient | address | 代币发送目标地址 |
intent.deadline | string | Unix 秒数的 uint256 字符串。POST /withdraw 要求在未来且最多领先 600 秒;请读取 GET /config 的 limits.withdraw.cosignature_max_ttl_seconds。 |
intent.uuid | string | 用于防重放的唯一标识符(uint256 字符串) |
user_signature | string | EIP-712 WithdrawIntent 签名 |
tokens 和 amounts 长度必须相等,且包含 1 至 20 项;每个 amount 必须为正 uint256 字符串。
响应¶
第二步:构建提款交易¶
身份验证: 可选
请求体¶
{
"intent": {
"user": "0x...",
"tokens": ["0x965d4b4546716e416e950bc30467d128455d2d0e"],
"amounts": ["1000000000"],
"recipient": "0x...",
"deadline": "1704153600",
"uuid": "123456789"
},
"user_signature": "0x...",
"executor": "0x...",
"executor_signature": "0x..."
}
| 字段 | 类型 | 说明 |
|---|---|---|
intent | object | 与第一步相同的 WithdrawIntent |
user_signature | string | 您的 EIP-712 签名 |
executor | address | 第一步响应中的联合签名地址 executor_address |
executor_signature | string | 第一步获得的服务器联合签名 |
响应¶
{
"tx": {
"to": "0x...",
"data": "0x...",
"value": "0x0",
"chainId": "0xaa36a7",
"nonce": "0x2c",
"gas": "0x249f0",
"type": "0x2",
"maxFeePerGas": "0x...",
"maxPriorityFeePerGas": "0x..."
}
}
第三步:广播已签名交易¶
身份验证: 可选
请求体¶
响应¶
POST /withdraw/send 只接受调用 executeInstantWithdrawDualSig(...) 且目标合约为来自 GET /config 的当前 Sera 合约地址的已签名交易。如果提交的是 EIP-7702 的 type-4 交易,则每个授权 delegate 都必须在当前部署的允许列表内,否则请求会被拒绝。
用钱包签署第二步的交易,然后将已签名的十六进制提交到此端点。
紧急提款¶
API 不可用时,可使用 Sera 的链上两步流程:
- 调用
emergencyWithdraw(token, amount)发起请求,金额须为正且不超过当前 Vault 余额。 - 至少等待 7,200 个区块(按每块 12 秒估算约 24 小时)。
- 在请求区块 + 7,200 至 + 14,400(含边界)执行,使用相同代币及
0 < amount <= 请求金额,当前余额仍须足够。 - 成功后会删除整个请求,即使只提取部分金额。超过 14,400 区块窗口后,再次调用会创建新请求并重新等待。
完整规则见 Sera.sol 合约参考。
ERC-20 转账¶
无需自己的 RPC 连接即可构建和广播 ERC-20 代币转账。仅接受 /tokens 注册表中列出的代币。
构建转账¶
身份验证: 需要 API Key
请求体¶
{
"token": "0x965d4b4546716e416e950bc30467d128455d2d0e",
"to": "0x...",
"amount": "1000000000",
"from_address": "0x..."
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
token | address | 是 | ERC-20 代币合约地址(必须在代币注册表中) |
to | address | 是 | 接收者地址 |
amount | string | 是 | 原始代币单位的金额 |
from_address | address | 是 | 发送者钱包地址 |
响应¶
{
"tx": {
"to": "0x...",
"data": "0x...",
"value": "0x0",
"chainId": "0xaa36a7",
"nonce": "0x2d",
"gas": "0xea60",
"type": "0x2",
"maxFeePerGas": "0x...",
"maxPriorityFeePerGas": "0x..."
}
}
广播转账¶
身份验证: 需要 API Key
请求体¶
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
raw_tx | string | 是 | 已签名的交易十六进制(0x 前缀) |
响应¶
余额核验与提款错误¶
chain_verified=true 描述旧版模式对缓存 Vault 金额的链上纠正,不能概括已提交账务的验证状态;请结合上方状态字段。null 不代表刚完成核验。分别检查 wallet_balance_available、vault_balance_available 与 updated_at。
GET /api/v2/balances 是相同查询参数与响应的别名。所有余额金额均为 raw 整数字符串。
POST /withdraw 同时返回 executor_address 和 executor_signature,构建 /withdraw/build 时必须把该地址作为 executor 传入。联合签名截止时间最多领先 600 秒,即使通用签名意图模型允许更长时间。409 且 detail.error_code="WITHDRAW_UNAVAILABLE" 表示即时提款准入拒绝,请刷新余额/订单后再决定是否重试。受保护账户操作还可能返回 COMPLIANCE_BLOCKED。
交易签名与广播规则¶
POST /transfer要求from_address与 API Key 所有者一致。/transfer/send只接受向已登记代币合约调用transfer(...),原始交易签名者也必须是该所有者。/tx/send同样要求外层交易签名者与 API Key 所有者一致;充值 calldata 中的 owner 也必须一致。充值 permit 在广播前会重新检查代币、Vault spender、金额、当前 nonce 与 deadline;过时的 permit 需要重新获取元数据并签名。/withdraw/send要求原始交易签名者等于intent.user,不能替换为第三方付费者。若携带 API Key,所有者也必须一致。广播前会再次筛查用户与接收方。- 构建接口返回未签名交易。返回
tx_hash表示提交给 RPC,不表示已成功打包或余额已最终变化;请检查交易收据并刷新余额。