Skip to Content
硬件接入
本页目录

evmSignTypedData

以太坊:签名类型化数据

使用指定 BIP32 路径派生的私钥对 EIP-712  类型化数据消息进行签名。

设备请求用户确认签名;展示的详情取决于使用解析签名还是哈希签名。

  • 支持 EIP-712(v3 和 v4)
  • 使用 evmSignTypedData 来签名类型化结构数据
  • 不适用于 EIP-191 类型化数据签名
  • 使用 evmSignMessage 来签名 EIP-191(personal_sign)
const result = await HardwareSDK.evmSignTypedData(connectId, deviceId, params);

参数

可选通用参数

  • path - 必需 string | Array<number> SDK 校验的最小长度为 3。 路径格式
  • data - 必需 Object 类型为 EthereumSignTypedDataMessage<EthereumSignTypedDataTypes>。JSON Schema 定义可在 EIP-712 规范中找到。
  • metamaskV4Compat - 必需 boolean 设置为 true 以兼容 MetaMask 的 signTypedData_v4 。
  • chainId - 可选 number ETH 中的 ChainId 是特定以太坊网络的唯一标识符,用于区分不同版本的区块链。参考 

解析签名

传入完整的 EIP-712 对象(包括 types.EIP712Domain),并让 metamaskV4Compat 与原始请求一致。在解析流程中,设备向 SDK 请求类型定义和字段值。可展示的详情取决于机型与固件,参见 Clear Signing 指南。

退回哈希签名

SDK 选择哈希签名时,设备收到的是预先计算的哈希,而不是结构化消息。这属于盲签,不能提供结构化 Clear Signing。哈希必须使用相同编码模式从完全相同的数据计算;SDK 不会代为计算,也不会交叉校验传入的哈希与 data 是否一致。

  • domainHash:解析签名时可选,退回哈希签名时必需。十六进制编码的 32 字节 EIP-712 域哈希。
  • messageHash:公开类型中可选,但当前自动退回分支要求提供。它是 32 字节 EIP-712 消息结构哈希,不是最终带 0x1901 前缀计算的签名摘要。仅域请求(primaryType: 'EIP712Domain')在解析签名时可以省略;当前退回分支的校验仍要求两个哈希。

当前 SDK 在以下情况下选择哈希签名:

条件适用范围
设备未报告 EIP-712 解析能力,或数据超过格式限制Classic、Mini、Classic1s、Classic Pure;不能把 Classic1s/Pure 一概视为只能盲签
传入数据包含嵌套数组所有设备系列
message.data 的十六进制字节数超过对应上限Classic1s/Pure、Pro2/Neo 为 1,536 字节;Touch/Pro 固件 4.4.0 及以上为 1,536 字节;其余为 1,024 字节

上述大小检查针对 message.data,不是整个 JSON 文档。Classic/Mini 系列另有格式限制:每个结构最多 16 个字段、类型名/字段名最多 63 字符、数组类型字段最多 24 个、访问路径深度最多 6 层、自定义依赖结构最多 8 个(不含主类型和域)、每个 string/bytes 值最多 1,536 字节。基础类型数组最多 24 个元素;v4 模式下结构数组也有此限制。类型循环依赖同样会触发退回。

Touch/Pro 的哈希签名要求固件 4.2.0 或更高版本。低于该版本时,进入此分支会返回 CallMethodNeedUpgradeFirmware。

提供哈希不会在解析签名可用时强制切换到哈希路径。需要退回但缺少哈希时,调用会失败。应用应明确处理此情况,不应自动替换原始请求,也不能用签名成功证明所有字段都已展示。

示例

以下片段使用 ethers v6 准备 v4 编码的备用哈希,复用已初始化的 SDK 和选定的钱包。提供哈希不会跳过设备确认。

import { TypedDataEncoder } from 'ethers'; const eip712Data = { types: { EIP712Domain: [ { name: 'name', type: 'string', }, { name: 'chainId', type: 'uint256' }, ], Message: [ { name: "wallet", type: "string" }, { name: "number", type: "uint64" } ] }, primaryType: 'Message', domain: { name: 'example.onekey.io', chainId: 1, }, message: { "wallet": "OneKey Wallet", // be careful with JavaScript numbers: MAX_SAFE_INTEGER is quite low "number": `${2n ** 55n}`, }, }; const { EIP712Domain, ...messageTypes } = eip712Data.types; const domainHash = TypedDataEncoder.hashDomain(eip712Data.domain); const messageHash = TypedDataEncoder.from(messageTypes).hash(eip712Data.message); const result = await HardwareSDK.evmSignTypedData(connectId, deviceId, { path: "m/44'/60'/0'/0/0", data: eip712Data, metamaskV4Compat: true, domainHash, messageHash, chainId: eip712Data.domain.chainId, }); if (result.success === false) throw new Error(result.payload.error);

Safe Tx

在支持的固件上使用下方标准 Safe v1.3.0 SafeTx schema。domain.verifyingContract 为 Safe 地址,网络必须真实,并提供全部十个消息字段。operation 为 0 表示 CALL,1 表示 DELEGATECALL。示例地址和 Safe Nonce 为占位值,接入时应替换为应用准备提交的完整交易。示例未提供备用哈希,因此需要哈希签名的请求会失败。

const safeTypedData = { types: { EIP712Domain: [ { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, ], SafeTx: [ { name: 'to', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'data', type: 'bytes' }, { name: 'operation', type: 'uint8' }, { name: 'safeTxGas', type: 'uint256' }, { name: 'baseGas', type: 'uint256' }, { name: 'gasPrice', type: 'uint256' }, { name: 'gasToken', type: 'address' }, { name: 'refundReceiver', type: 'address' }, { name: 'nonce', type: 'uint256' }, ], }, primaryType: 'SafeTx', domain: { chainId: 1, verifyingContract: '0x1111111111111111111111111111111111111111', }, message: { to: '0x2222222222222222222222222222222222222222', value: '0', data: '0x', operation: '0', safeTxGas: '0', baseGas: '0', gasPrice: '0', gasToken: '0x0000000000000000000000000000000000000000', refundReceiver: '0x0000000000000000000000000000000000000000', nonce: '0', }, }; const result = await HardwareSDK.evmSignTypedData(connectId, deviceId, { path: "m/44'/60'/0'/0/0", data: safeTypedData, metamaskV4Compat: true, chainId: safeTypedData.domain.chainId, }); if (result.success === false) throw new Error(result.payload.error);

这里返回 owner 签名,不会广播交易。验签和收集签名时必须保留相同 Typed Data。与链上 Safe 调用的区别见 Safe Tx 接入。

返回结果

{ success: true, payload: { address: string, signature: string, // hexadecimal string without the "0x" prefix } }

错误

{ success: false, payload: { error: string, // error message code?: string | number // error code } }
最后更新于