Skip to Content

Solana Signer

Solana 硬件签名全链路指南,覆盖地址确认与交易/消息签名,强调受支持的硬化路径、序列化消息签名与设备可验证。

目录

  1. 工作原理
  2. 安装
  3. 初始化
  4. 适用场景
  5. 交互与状态
  6. 示例
  7. 验签与排障

工作原理

通过 HardwareSDK 将序列化消息发送给设备。设备解析支持的内容并请求用户确认,具体可展示字段取决于请求、设备与固件。应用将返回签名附加到交易(@solana/web3.js),并验证签名及账户公钥。

关键点:

  • 使用固件支持的全硬化路径,例如:m/44'/501'/account'/0'(如 m/44'/501'/0'/0')。
  • rawTx 只接受十六进制字符串,若使用 Transaction,编码 serializeMessage() 的结果;若使用 VersionedTransaction,编码 message.serialize() 的结果。不要传包含签名区的整笔交易序列化结果。
  • 应用需自行构造并序列化消息(含 recent blockhash);Versioned Transaction 的支持情况应按目标机型、固件与对应链方法的要求验证。

安装

npm i @onekeyfe/hd-core @onekeyfe/hd-common-connect-sdk

初始化

示例统一使用标准钱包。隐藏钱包应在每次地址和签名调用中,以经过确认的 passphraseState 替代 useEmptyPassphrase: true。受保护调用前先注册 UI 处理器,使用返回数据前检查 success。含待提供交易参数的代码是接入片段,应用仍需实现 UI、设备选择与网络数据获取。

import HardwareSDK from '@onekeyfe/hd-common-connect-sdk'; const initialized = await HardwareSDK.init({ env: 'webusb', fetchConfig: true, debug: false }); if (!initialized) throw new Error('SDK initialization failed'); const devices = await HardwareSDK.searchDevices(); if (!devices.success) throw new Error(devices.payload.error); if (!devices.payload.length) throw new Error("No device"); // Select explicitly in the application when more than one device is available. if (devices.payload.length !== 1) throw new Error('Select exactly one device before continuing'); const { connectId } = devices.payload[0]; if (!connectId) throw new Error("Missing connectId"); const state = await HardwareSDK.getDeviceState(connectId); if (!state.success) throw new Error(state.payload.error); const deviceId = state.payload.identity.deviceId; if (!deviceId) throw new Error("Device has no wallet identity");

适用场景

场景 1:获取地址

const res = await HardwareSDK.solGetAddress(connectId, deviceId, { useEmptyPassphrase: true, path: "m/44'/501'/0'/0'", showOnOneKey: true, }); if (!res.success) throw new Error(res.payload.error); // res.payload.address and res.payload.path are strings.

参数

  • path: 必填,推荐使用 44'/501'/account'/0' 的全硬化结构。
  • showOnOneKey?: 可选,是否在设备上展示并确认。

返回

type AddressResult = | { success: true; payload: { address: string; path: string } } | { success: false; payload: { error: string; code?: string | number } };

场景 2:签名交易

const res = await HardwareSDK.solSignTransaction(connectId, deviceId, { path: "m/44'/501'/0'/0'", rawTx, useEmptyPassphrase: true, }); if (!res.success) throw new Error(res.payload.error); // res.payload.signature is hex; res.payload.path is the serialized path.

参数

  • path: 必填,同上。
  • rawTx: 必填,仅包含签名消息的序列化字节(hex,0x 可选),不包含交易的签名区。运行时要求提供该字段。
  • useEmptyPassphrase?: 可选,强制标准钱包。隐藏钱包请带 passphraseState。

返回

type SignResult = | { success: true; payload: { signature: string; path: string } } | { success: false; payload: { error: string; code?: string | number } };

可附加到 Transaction 后广播。

交互与状态

  • 接口以 Promise 结束;用户提示通过 UI_REQUEST 事件触发(解锁设备、确认交易)。
  • 同一设备串行调用。复用 passphraseState 或 useEmptyPassphrase: true。keepSession 仍是传输持有,不是钱包绑定。

示例

示例:交易签名

import HardwareSDK from '@onekeyfe/hd-common-connect-sdk'; const initialized = await HardwareSDK.init({ env: 'webusb', debug: false }); if (!initialized) throw new Error('SDK initialization failed'); const devices = await HardwareSDK.searchDevices(); if (!devices.success) throw new Error(devices.payload.error); if (!devices.payload.length) throw new Error("No device"); // Select explicitly in the application when more than one device is available. if (devices.payload.length !== 1) throw new Error('Select exactly one device before continuing'); const { connectId } = devices.payload[0]; if (!connectId) throw new Error("Missing connectId"); const state = await HardwareSDK.getDeviceState(connectId); if (!state.success) throw new Error(state.payload.error); const deviceId = state.payload.identity.deviceId; if (!deviceId) throw new Error("Device has no wallet identity"); const path = "m/44'/501'/0'/0'"; const confirmedAddress = await HardwareSDK.solGetAddress(connectId, deviceId, { path, showOnOneKey: true, useEmptyPassphrase: true, }); if (!confirmedAddress.success) throw new Error(confirmedAddress.payload.error); // Obtain these bytes from Transaction.serializeMessage() or // VersionedTransaction.message.serialize(), with a fresh recent blockhash. declare const messageBytes: Uint8Array; const rawTx = Array.from(messageBytes, byte => byte.toString(16).padStart(2, '0')).join(''); const { success, payload } = await HardwareSDK.solSignTransaction(connectId, deviceId, { path, rawTx, useEmptyPassphrase: true, }); if (!success) throw new Error(payload.error); // Attach the signature and verify it against the confirmed address before broadcasting.

验签与排障

  • 路径:SDK 校验至少 3 级,固件进一步限制受支持的派生路径。示例使用 m/44'/501'/account'/0'。
  • 消息/交易:确保包含最新 recentBlockhash、正确的账户列表和指令顺序。
  • 设备核对:路径/账户、公链币金额、目标账户、费用摘要需与前端一致。
  • 验签:用 @solana/web3.js 将签名附加到 Transaction,验证签名公钥对应 solGetAddress 返回的 base58 address。
  • 常见问题:
    • 超大消息或不支持的指令:报告限制并终止当前请求;如需精简或拆分交易,重新构造请求并取得用户确认,不自动改写后重签。
    • 会话交互:同一设备串行调用;openWalletSession 一次后带上 passphraseState。keepSession 仍是传输持有,不是钱包绑定。
    • 拒签/超时:提供重试/取消,不要自动重播。

参见方法文档:solSignTransaction。

最后更新于