签名与鉴权
本文档说明 链写操作 的签名与鉴权(亦可使用 官方 SDK 封装)。做市接入流程见 做市商对接指南;Action 字段 schema 见 write-actions。
1. 双通道概览
| 通道 | 适用 Action | 签名方式 | 典型私钥 |
|---|---|---|---|
| L1 | 41 个交易/管理操作(PlaceOrder、CancelOrder、SetMarginMode…) | msgpack → connectionId → EIP-712 phantom L1Action | Agent 或 Master secp256k1 |
| User-Signed | 仅 RegisterAgent · RevokeAgent · WithdrawRequest | 标准 EIP-712 typed data(钱包 eth_signTypedData_v4) | Master 钱包 |
做市推荐路径:Master 注册 Agent → 日常用 Agent 私钥 走 L1 通道;envelope.signer 始终为 master 地址。
禁止:对 PlaceOrder / SetMarginMode / SetLeverage 等 L1 Action 构造
primaryType等于 Action 名的 typed data(钱包eth_signTypedData_v4)。链上只认 phantomL1Action(string source,bytes32 connectionId)。签错通道时 ecrecover 出野地址,常表现为AgentNotRegistered(即使 agent 已注册)。
2. 环境参数
以下值参与签名,须与目标环境 逐字一致。
2.1 Auroran 生产(单节点)
| 参数 | 值 | 用途 |
|---|---|---|
chain_id | 42 | envelope.chain_id(auth 校验等于节点配置) |
network_tag | "zepto-dev" | L1 phantom L1Action.source(UTF-8,区分大小写) |
zeptoChain | "zepto-dev" | User-Signed 消息体(与 network_tag 同值) |
domain_chain_id(L1) | 42 | 必须与 chain_id 相同。验签 digest 与 txid 都读此字段;L1 EIP-712 domain 的 chainId = envelope.domain_chain_id |
action_version | 2 | 可用 getExchangeConfig 校验 |
| EIP-712 domain | ZeptoSignTransaction / 1 / verifyingContract=0x0…0 | 两通道共用 domain 名与版本 |
domain_chain_id 按通道
| 通道 | envelope.domain_chain_id | EIP-712 domain chainId |
|---|---|---|
| L1 | 42(= chain_id) | = domain_chain_id(故亦为 42) |
| User-Signed | master 钱包当前 EVM 链 ID(如 BSC 56) | 同上(写入 typed data) |
- User-Signed 时
envelope.chain_id仍为42,可与钱包 EVM 链 ID 不同。 - 勿把 User-Signed 的
56套到 L1 的domain_chain_id——否则 digest domain 与按chain_id=42签出的摘要不一致。
2.2 本地开发
| 项 | 典型值 |
|---|---|
| Chain | https://rpc.auroran.io |
chain_id / network_tag / zeptoChain | 与本地 genesis.json 一致(常为 42 / "zepto-dev") |
链上 无 只读 RPC 返回
chain_id/network_tag;私有节点以genesis.json为准。
3. Envelope 与 Credential
所有写请求 params 格式:
{
"envelope": {
"chain_id": 42,
"domain_chain_id": 42,
"action_version": 2,
"nonce": 0,
"signer": "0x1111222233334444555566667777888899990000",
"credential": {
"Secp256k1": {
"signature": [95, 83, 94, "... 64 bytes r||s ..., 27"]
}
},
"action": { "PlaceOrder": { "...": "..." } }
}
}
| 字段 | 说明 |
|---|---|
signer | Master 地址;Agent 代签时仍为 master,不是 agent 地址 |
credential.Secp256k1.signature | 65 字节 JSON 数组 r||s||v,v ∈ {27,28}(与 eth_signTypedData_v4 / ethers 输出一致) |
method(JSON-RPC 外层) | 必须等于 action 变体名,否则 -32602 |
鉴权逻辑:recover 地址 == signer → master 直签;否则在 signer 的 agents 列表中查找 → agent 代签(须未过期且角色满足)。详见 交易与账户 §5。
4. User-Signed:注册 Agent
4.1 流程
getAccount→ 当前nonce。- 构造 EIP-712
RegisterAgent(zeptoChain="zepto-dev",roleMask=1= Trader)。 - Master 钱包签名;
domain.chainId= 钱包 EVM 链 ID。 POST /api/v1/action,method=RegisterAgent,envelope.chain_id=42,domain_chain_id=钱包链 ID。- 成功后,用 agent 私钥 对后续 Trader Action 做 L1 签名。
Typed data 与字段表见 write-actions §3.3。
4.2 撤销 Agent
RevokeAgent 同为 User-Signed;见 write-actions §3.4。
5. L1 通道:msgpack 与 digest
适用于除 User-Signed 三例外外的全部 Action(含 Agent 代签的 PlaceOrder 等)。
5.1 算法(5 步)
msgpack_bytes = rmp_serde::to_vec_named(action) // 禁止 JSON
connectionId = keccak256(msgpack_bytes || nonce.to_be_bytes(8))
domain = EIP712Domain(ZeptoSignTransaction, 1, domain_chain_id, 0x0)
// L1 须 domain_chain_id == chain_id(生产均为 42)
struct_hash = keccak256( typeHash(L1Action) || enc_string(network_tag) || connectionId )
digest = keccak256(0x19 || 0x01 || domain || struct_hash)
signature = secp256k1_sign(digest) → credential.Secp256k1.signature // r||s||v
逐步公式与 enc_string / enc_uint 定义见 write-actions §2.1 与 交易与账户 §4.6。
5.2 msgpack 编码规则(自研必守)
| 类型 | 编码 |
|---|---|
| Action enum | externally-tagged map:{"PlaceOrder": {…}} |
Address20 | 20 字节 bin(非 hex 字符串) |
Option::None | msgpack nil(JSON 省略字段 ≡ null) |
i128 / 整数 | msgpack 定长整数 |
| String | msgpack str |
nonce 拼接 | 8 字节 大端 附在 msgpack 后参与 keccak256 |
使用 JSON 代替 msgpack 会导致
BadMasterSignature。
5.3 PlaceOrder 请求骨架
{
"jsonrpc": "2.0",
"id": 1,
"method": "PlaceOrder",
"params": {
"envelope": {
"chain_id": 42,
"domain_chain_id": 42,
"action_version": 2,
"nonce": 0,
"signer": "0x1111222233334444555566667777888899990000",
"credential": { "Secp256k1": { "signature": ["...r||s...", 27] } },
"action": {
"PlaceOrder": {
"owner": "0x1111222233334444555566667777888899990000",
"symbol": "BTC-USDT",
"side": "Bid",
"limit_price": "97100.00",
"qty": "1.00000",
"tif": "Gtc",
"client_order_id": "mm-btc-001",
"reduce_only": false,
"expires_at_ms": null
}
}
}
}
}
Action 内数值均为 canonical decimal string,见 precision。
6. txid 与 signing digest(勿混淆)
| signing digest | txid(result.tx_hash) | |
|---|---|---|
| 哈希 | keccak256(EIP-712) | SHA-256 |
| 输入 | msgpack(action)+nonce, network_tag | chain_id, domain_chain_id, action_version, nonce, signer, SHA256(msgpack(action)) |
| 用途 | 验签 | 公共交易标识 |
7. 常见错误
| 现象 | 可能原因 |
|---|---|
ChainIdMismatch | envelope.chain_id ≠ 节点配置 |
BadMasterSignature | msgpack 非 canonical、错 network_tag、错 nonce 拼接、错私钥、signature 非 r||s||v、ecrecover 失败 |
AgentNotRegistered | ① 未 RegisterAgent;② 更常见:签错通道 / digest 不一致(如对 SetMarginMode 做 typed data),recover 地址既不是 master 也不是已注册 agent——先查签名算法,勿仅当「未注册」 |
AgentExpired | agent 委托已过期 |
AgentRoleMissing | agent 未授予 Trader(roleMask) |
MasterOnlyAction | 管理类 Action 不允许 agent 代签 |
8. 延伸阅读
| 主题 | 文档 |
|---|---|
| 做市接入流程 | 做市商对接指南 |
| Action schema / 批量上限 | write-actions |
| Nonce / kept-reject | 做市商对接指南 §4 · 交易与账户 §4.3 |
| raw i128 reason | precision §5 |