跳到主要内容

交易与账户

交易接入的主路径:写操作(45 Action)、账户 / 订单 / 持仓读方法交易 WebSocket 主题事件投影。行情与历史数据见 行情数据 · 历史与审计

用途入口
下单 / 撤单 / 改单 / 保证金 / 清算 / 市场管理写操作
账户 / 订单 / 持仓 / 触发单 / Bootstrap 读方法账户与订单
强平价 / 订单价值 / 预估占用(全仓·逐仓·多空)下单预估与强平价
账户 / 成交 / 订单 / 触发单实时推送WebSocket 交易主题
事件结构(交易 / 桥 / 返佣)事件投影
签名(Agent / L1 / User-Signed)与鉴权签名与鉴权

以下是交易协议的基础约定:签名信封与鉴权体系。

4. 签名信封(SignedActionEnvelope)

所有写操作 body 的 params 格式为:

{
"envelope": {
"chain_id": 1,
"domain_chain_id": 1,
"action_version": 2,
"nonce": 42,
"signer": "0x1111222233334444555566667777888899990000",
"credential": {
"Secp256k1": {
"signature": [95, 83, 94, "...r||s...", 27]
}
},
"action": {
"PlaceOrder": {
"owner": "0x1111222233334444555566667777888899990000",
"symbol": "BTC-USDT",
"side": "Bid",
"limit_price": "97100.00",
"qty": "1.00000",
"tif": "Gtc",
"reduce_only": false
}
}
}
}

4.1 Envelope 字段

字段类型说明
chain_idu64Zepto 链标识(来自 ChainConfig.chain_id)。auth 层校验 envelope.chain_id == 节点 chain_id,不匹配 → ChainIdMismatch。防跨 Zepto 网络重放(如 Mainnet envelope 不可在 Testnet 重放)
domain_chain_idu64验签 EIP-712 domain 的 chainId 与 txid 输入。auth 用 envelope.domain_chain_id 重建 digest。L1:须等于 chain_id(生产 42)。User-Signed:= 钱包当前 EVM 链 ID(如 BSC 56
action_versionu32Wire 协议版本,当前固定 2
nonceu64账户 nonce;须落在 [account.nonce, account.nonce + window) 且未重复(见 §4.3)
signerAddress20签名者的 master 账户地址(20 字节 hex,0x 前缀)。不参与签名 digest
credentialSigCredential签名凭证 {"Secp256k1": {"signature": [byte, ...]}}。65 字节 secp256k1(r || s || v
actionAction具体操作(externally-tagged,变体名 = 写 method 名)

4.2 chain_iddomain_chain_id

两个字段语义不同,各自服务于不同的安全边界:

chain_id — Zepto 链网络标识。由链部署方在 ChainConfig.chain_id 中设定。用途:

  • auth 层校验 envelope.chain_id == 节点 chain_id,防跨 Zepto 网络重放(Mainnet ↔ Testnet)
  • 公共交易标识 txid 的输入

domain_chain_id — 写入 envelope 后,同时用于:

  1. 签名 digest:auth 以 domain_chain_id 作为 EIP-712 domain 的 chainId(L1 与 User-Signed 皆然)
  2. txid 计算输入

按通道取值

场景chain_iddomain_chain_id
L1 通道(41 个 Action)Zepto 链 ID(生产 42必须 = chain_id(生产 42
User-Signed(3 个 Action)Zepto 链 ID(生产 42用户钱包的 EVM 链 ID(如 1、56、42161)

L1 若误把 User-Signed 习惯的 56 写入 domain_chain_id,而按 chainId=42 签名,验签必失败。

network_tag(又称 source)来自 ChainConfig.signing_network_tagAuroran 生产为 "zepto-dev" — 见 签名 §2

链上无只读 RPC 返回 chain_id / network_tag;固定值见 签名 §2。私有节点以 genesis.json 为准。

4.3 Nonce 管理

链上采用 nonce 窗口:允许 [account.nonce, account.nonce + window) 内乱序到达(Auroran 默认 window = 64;若配置为 window = 1 则退化为「必须恰好等于下界」的旧行为)。

规则说明
有效区间account.nonce ≤ envelope.nonce < account.nonce + window
去重窗口内同一 nonce 不可重复提交(nonce_used_mask 位图)
下界推进消费后 sweep 连续低位,account.nonce 前移(中间「洞」可后补)
kept-reject同样消耗该 nonce
拒绝低于下界、超出窗口、或窗口内重放 → NONCE_REPLAY-32001),error.data{"account_nonce": X, "got": Y, "window": W}

getAccount.nonce 含义:已 sweep 的 下界,不是「下一笔必须恰好等于该值」。窗口 > 1 时,仍可使用 [nonce, nonce + window) 内任意 未消费 值(例如先落 N+1 再补 N)。

对接建议(客户端侧,非链上硬约束):

  • 本地为每笔 Action 分配唯一、单调递增 nonce(不跳号、不复用)
  • 可在窗口内 并发在途(多笔 HTTP 同时飞行),不必等前一笔 200 再发下一笔
  • NONCE_REPLAY 时用 error.datagetAccount 重新同步

完整状态机见 做市商对接指南 §4

4.4 Credential 格式

当前仅支持 secp256k1 单通道签名:

"credential": {
"Secp256k1": {
"signature": [95, 83, 94, "...r||s...", 27]
}
}

signature65 字节 JSON 数组(非 hex 字符串),格式为 r || s || v,其中 v ∈ {27, 28}(recovery id)。r 和 s 各 32 字节大端。与 eth_signTypedData_v4 / ethers 等以太坊工具的字节输出一致(不是 v || r || s)。

4.5 鉴权通道

recover 出的地址 == envelope.signermaster 直签。 recover 出的地址 ≠ envelope.signer → 在 signer 账户的 agents 列表中按地址查找 → agent 委托签名

通道适用场景签名 key
Master 直签Admin / 敏感操作 / 低频用户master 的 secp256k1 私钥
Agent 委托程序化做市 / 高频交易已注册的 API-wallet secp256k1 私钥

Agent 必须由 master 经 EIP-712 通道(RegisterAgent)预先授权,指定角色子集和过期时间。

4.6 签名算法

所有 45 个 Action 均走 EIP-712 签名,但分两个 算法通道(与下方 §5.1「谁可以签」正交):

算法通道Action签什么
L1除下列三外的 41 个(含 PlaceOrder / SetMarginMode / SetLeverage…)phantom L1Action + msgpack connectionId禁止以 Action 名为 primaryType
User-Signed WithdrawRequest / RegisterAgent / RevokeAgent钱包 typed data(primaryType 为 Withdraw / RegisterAgent / RevokeAgent)

完整接入说明见 签名与鉴权

L1 通道(41 个交易/管理操作)

适用于除 WithdrawRequest、RegisterAgent、RevokeAgent 外的全部 Action。

L1 通道使用 phantom EIP-712 类型 L1Action(string source,bytes32 connectionId),其构造分 3 步:

Step 1 — 计算 connectionId

msgpack_bytes = rmp_serde::to_vec_named(action)
connectionId = keccak256(msgpack_bytes || nonce.to_be_bytes(8))
  • action 对象做 msgpack 序列化(rmp_serde named map 模式),得到确定性字节串
  • 拼接 nonce 的 8 字节大端表示
  • 对整体求 keccak256,得到 32 字节 connectionId

msgpack 编码细节(确定性保证,跨语言可复现):

  • i128 → msgpack 定长整数
  • Option::None → msgpack nil
  • Enum → msgpack map(外部 tag,key 为变体名字符串)
  • Address20 → msgpack 20 字节 bin
  • String → msgpack str
  • bool/u32/u64 → 对应 msgpack 类型

Step 2 — EIP-712 domain + struct hash

domain = keccak256(
typeHash("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
|| enc_string("ZeptoSignTransaction")
|| enc_string("1")
|| enc_uint(domain_chain_id) // L1 须 domain_chain_id == chain_id(生产 42)
|| enc_address(0x0000...0000)
)

struct_hash = keccak256(
typeHash("L1Action(string source,bytes32 connectionId)")
|| enc_string(network_tag)
|| connectionId
)
  • typeHash(s) = keccak256(utf8_bytes(s))
  • enc_string(s) = keccak256(utf8_bytes(s))
  • enc_uint(v) = 32 字节大端,高位补零
  • enc_address(a) = 左侧补 12 个零字节,右对齐 20 字节地址

Step 3 — EIP-712 最终摘要

digest = keccak256(0x19 || 0x01 || domain || struct_hash)

Step 4 — 签名

对上述 32 字节 digest 做 secp256k1 recoverable 签名,得到 65 字节 [r_0..r_31, s_0..s_31, v]r || s || v,v ∈ {27, 28}),填入 credential.Secp256k1.signature

EIP-712 通道(WithdrawRequest / RegisterAgent / RevokeAgent)

走标准 eth_signTypedData_v4,由钱包直接构造 typed data 并签名。domain 名/版本与 L1 相同(ZeptoSignTransaction),但 domain.chainId = domain_chain_id(钱包 EVM 链),primary type 按 Action 不同。typed data 结构和 domain JSON 见 写操作 - EIP-712 签名

自研签名:见专章 签名与鉴权(Agent、L1 msgpack、User-Signed)。Action schema 见 write-actions §2。Nonce 见 做市商对接指南 §4

公共交易标识 txid

响应中的 tx_hash公共交易标识txid),与签名彻底解耦。同一笔交易无论由 master 还是 agent 签名,txid 相同。其计算独立于签名 digest

txid = SHA-256(
b"zepto-txid-v1" // domain-separation 标签
|| chain_id // u64 BE (8 bytes)
|| domain_chain_id // u64 BE (8 bytes)
|| action_version // u32 BE (4 bytes)
|| nonce // u64 BE (8 bytes)
|| signer // 20 bytes raw address
|| SHA-256(msgpack(action))
)

注意:txid 使用 SHA-256domain_chain_id,而签名 digest 使用 keccak256 和 EIP-712 结构。两者算法不同,不可混淆。


5. 鉴权体系

5.1 Action 鉴权一览

下表「鉴权通道」表示 谁可以签(master / agent / master-only),不是签名算法通道。算法上:除末三行 User-Signed 外,其余均为 L1(见 签名与鉴权 / §4.6)。

Action鉴权(谁可签)所需角色算法通道
PlaceOrder / CancelOrdermaster_or_agent_with_roleTraderL1
BatchPlaceOrdermaster_or_agent_with_roleTraderL1
AmendOrder / BatchModify / MassCancel / ClosePosition / ScheduleCancelmaster_or_agent_with_roleTraderL1
SetLeverage / SetMarginMode / SetIsolatedMarginmaster_or_agent_with_roleTraderL1
PlaceTriggerOrder / CancelTriggerOrder / AmendTriggerOrder / BatchAmendTriggermaster_or_agent_with_roleTraderL1
PlaceOco / CancelOcomaster_or_agent_with_roleTraderL1
SetReferrer / RegisterReferrer / SetInviterKeepRatiomaster_or_agent_with_roleTraderL1
SetGlobalRebateRatio / SetAccountRebateRatiomaster_or_agent_with_roleAdminL1
SubmitOracleQuote / BatchSubmitOracleQuotemaster_or_agent_with_roleQuoterL1
Liquidatemaster_or_agent_with_roleLiquidatorL1
RecordDeposit / CreditDepositmaster_or_agent_with_roleSettlementOperatorL1
WithdrawSettle / WithdrawRefundmaster_or_agent_with_roleSettlementOperatorL1
SetSettlementPausedmaster_or_agent_with_roleAdminL1
SetUserFeeRatemaster_or_agent_with_roleAdminL1
CreateMarket / ActivateMarket / HaltMarket / ResumeMarketmasterL1
RequestDelist / CompleteDelistmasterL1
SetFeeRecipient / AmendMarketConfig / SetEmergencyHaltmasterL1
SetAccountRolemasterL1
WithdrawRequestuser_signed_masterUser-Signed
RegisterAgent / RevokeAgentuser_signed_masterUser-Signed

5.2 角色位掩码

角色位值说明
Trader1下单、撤单、改单、保证金管理
OracleOperator2保留
SettlementOperator4Bridge 充提操作
Admin8闸门、费率管理
Liquidator16清算执行
Quoter32预言机报价提交