跳转至

账户端点

获取余额

获取账户的钱包余额和 Vault 余额汇总。

GET /balances

身份验证: 需要 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}`);
}

请求余额同步

POST /api/v1/balances/sync
POST /api/v2/balances/sync

身份验证: 需要 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。现在充值流程使用三个辅助端点:

  1. 如不使用 permit,先调用 POST /approve 构建 ERC-20 授权交易。
  2. 调用 POST /deposit 构建 depositFund(...) 或 depositFundWithPermit(...)。
  3. 本地签名后,通过 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 分支也被同样拒绝。

构建充值交易

POST /deposit

身份验证: 需要 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..."
  }
}

构建授权交易

POST /approve

身份验证: 需要 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..."
  }
}

广播已签名的授权或充值交易

POST /tx/send

身份验证: 需要 API Key

请求体

{
  "raw_tx": "0x..."
}

响应

{
  "tx_hash": "0x..."
}

POST /tx/send 只接受选择器与目标地址组合在允许列表内的已签名授权/充值交易。

对于 approve,编码后的 spender 还必须是 GET /config 返回的实时 Vault 或 SOR 地址。如果提交的是 EIP-7702 的 type-4 交易,则每个授权 delegate 都必须在当前部署的允许列表内,否则请求会被拒绝。


提款(联合签名)

请求服务器联合签名以进行即时提款。Sera 的智能合约要求即时提款需要两个签名 — 您的签名和服务器的签名 — 以防止未经授权的提款。这是一个三步流程:获取联合签名、构建交易、然后广播。

第一步:获取服务器联合签名

POST /withdraw

身份验证: 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 字符串。

响应

{
  "success": true,
  "executor_address": "0x...",
  "executor_signature": "0x...",
  "error": null
}

第二步:构建提款交易

POST /withdraw/build

身份验证: 可选

请求体

{
  "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

身份验证: 可选

请求体

{
  "raw_tx": "0x..."
}

响应

{
  "tx_hash": "0x..."
}

POST /withdraw/send 只接受调用 executeInstantWithdrawDualSig(...) 且目标合约为来自 GET /config 的当前 Sera 合约地址的已签名交易。如果提交的是 EIP-7702 的 type-4 交易,则每个授权 delegate 都必须在当前部署的允许列表内,否则请求会被拒绝。

用钱包签署第二步的交易,然后将已签名的十六进制提交到此端点。


紧急提款

API 不可用时,可使用 Sera 的链上两步流程:

  1. 调用 emergencyWithdraw(token, amount) 发起请求,金额须为正且不超过当前 Vault 余额。
  2. 至少等待 7,200 个区块(按每块 12 秒估算约 24 小时)。
  3. 在请求区块 + 7,200 至 + 14,400(含边界)执行,使用相同代币及 0 < amount <= 请求金额,当前余额仍须足够。
  4. 成功后会删除整个请求,即使只提取部分金额。超过 14,400 区块窗口后,再次调用会创建新请求并重新等待。

完整规则见 Sera.sol 合约参考。

ERC-20 转账

无需自己的 RPC 连接即可构建和广播 ERC-20 代币转账。仅接受 /tokens 注册表中列出的代币。

构建转账

POST /transfer

身份验证: 需要 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..."
  }
}

广播转账

POST /transfer/send

身份验证: 需要 API Key

请求体

{
  "raw_tx": "0x..."
}
字段 类型 必填 说明
raw_tx string 是 已签名的交易十六进制(0x 前缀)

响应

{
  "tx_hash": "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,不表示已成功打包或余额已最终变化;请检查交易收据并刷新余额。