Solana 签名接入
本文说明如何在 UKey Wallet 硬件设备上完成 Solana 地址确认和交易签名。重点放在三件事:使用设备支持的派生路径、把交易序列化成设备可解析的 rawTx,以及在应用端完成签名回填和校验。
目录
原理
应用通过 SDK 入口把待签名交易发送给设备端 Solana App。设备会根据序列化后的 rawTx 展示账户、转账目标、金额、费用等摘要,用户确认后才会返回签名。应用随后需要把签名写回 Transaction 或 VersionedTransaction,并用 @solana/web3.js 校验签名者公钥。
接入时请先确认以下边界:
- 派生路径必须是 4 段全硬化结构:
m/44'/501'/account'/0',例如m/44'/501'/3'/0'。 rawTx传入十六进制字符串即可,前缀0x可带可不带。- 如果手里是
Transaction或VersionedTransaction对象,先执行Buffer.from(tx.serialize()).toString('hex')再传给 SDK。 - 应用负责构造交易、设置
recentBlockhash、序列化交易;签名只在设备确认后产生。 - Versioned Transaction 需要固件版本满足 UKey Lite 24 / UKey Lite 25 / UKey Core 26 ≥
1.1.0。
部署
npm i @ukeyfe/hardware-core @ukeyfe/hardware-common-connect-sdk
启动
import ukeySdk from "@ukeyfe/hardware-common-connect-sdk";
await ukeySdk.init({ env: "webusb", fetchConfig: true, debug: false });
const [{ connectId }] = await ukeySdk.searchDevices();
const deviceId = (await ukeySdk.getFeatures(connectId)).payload?.device_id;
适用面
场景 1:读取并确认地址
const path = "m/44'/501'/3'/0'";
const opResult = await ukeySdk.solGetAddress(connectId, deviceId, {
path,
showOnUKey: true,
});
// 返回字段参考片段:opResult.payload.address, publicKey?, path
入参清单
path: Solana 账户路径,需保持44'/501'/account'/0'这种全硬化结构。showOnUKey?: 需要用户在设备上核对地址时开启,例如首次绑定或敏感操作前确认。
返回
Promise<{ success; payload: { address; path; publicKey? } }>;
场景 2:确认提交交易给设备签名
const opResult = await ukeySdk.solSignTransaction(connectId, deviceId, {
path: "m/44'/501'/3'/0'",
rawTx,
keepSession: true,
});
// 返回值参考片段:opResult.payload.signature
入参清单
path: 与构造交易所用地址一致的 Solana 账户路径。rawTx: 序列化后的交易十六进制字符串,0x前缀可带可不带;如果来源是Transaction.serialize(),请先转成 hex。keepSession?: 保留当前会话,让连续签名过程更顺畅。
返回
Promise<{ success; payload: { signature } }>;
拿到签名后,将其附加回交易对象,完成本地校验后再广播。
交互状态
- SDK 方法通过 Promise 返回最终结果;解锁设备、打开 Solana App、确认交易等中间步骤会通过
UI_REQUEST事件通知应用展示提示。 - 同一台设备请按顺序调用接口,不要并发发起多个签名请求。
- 批量或连续签名时,可结合
keepSession让交互更顺滑。
示范
参考片段:交易签名
import ukeySdk from "@ukeyfe/hardware-common-connect-sdk";
await ukeySdk.init({ env: "webusb", debug: false });
const [{ connectId }] = await ukeySdk.searchDevices();
const deviceId = (await ukeySdk.getFeatures(connectId)).payload?.device_id;
const path = "m/44'/501'/3'/0'";
await ukeySdk.solGetAddress(connectId, deviceId, {
path,
showOnUKey: true,
});
// 如果已经拿到 Transaction / VersionedTransaction,先转成十六进制再处理:
const rawTxFromObject = Buffer.from(transaction.serialize()).toString('hex');
// 下面这个例子里的 demoRawTx hex 已经序列化完成,可直接交给设备确认。
const demoRawTx =
"5f3b1d7a9c2e4f6081a3b5d7f90c2e4f6081a3b5d7f90c2e4f6081a3b5d7f90c2e4f6081a3b5d7f90c2e4f60";
const { success, payload } = await ukeySdk.solSignTransaction(
connectId,
deviceId,
{
path,
rawTx: demoRawTx,
keepSession: true,
},
);
if (success) {
console.info("signature:", payload.signature);
}
末尾核对
| 核验项 | 建议处理 |
|---|---|
| 路径说明规则 | 路径说明要维持 44'/501'/account'/0' 这种全硬化形式,其他写法可能会被设备拒绝。 |
| 交易材料 | 先准备好有效的 recentBlockhash、完整账户列表和稳定的指令步骤。 |
| 屏幕一致性 | 账户、目标地址、金额和费用摘要要和设备屏幕上的内容对得上。 |
| 签名回填 | 用 @solana/web3.js 回填签名后,确认签名公钥与 solGetAddress 返回的地址一致。 |
| 负载控制 | 交易太大或包含设备暂不支持的指令时,先拆分或简化。 |
| 交互结束 | 用户拒绝或超时后,给出重新发起入口,不要在后台自动重播同一笔交易。 |
继续查看具体 API:solSignTransaction。