本页目录
BTC Signer
比特币 UTXO 硬件签名全链路指南,覆盖地址确认、P2PKH / P2SH-P2WPKH / P2WPKH / Taproot 交易签名,强调“路径与脚本匹配、refTxs 完整、设备可验证”。
目录
工作原理
OneKey BTC signer 通过 HardwareSDK 与设备交互,通过 OneKey 协议请求 UTXO 审核与签名。核心要点:
- 路径必须与脚本 purpose 对应(44/49/84/86)。
- 在
refTxs中提供签名流程请求的完整前序交易;具体要求取决于输入类型与固件。 - 设备展示支持的交易确认信息,用户确认后返回签名或序列化交易。具体可展示字段取决于请求、设备与固件,应用应核对实际屏幕。
安装
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.btcGetAddress(connectId, deviceId, {
useEmptyPassphrase: true,
path: "m/84'/0'/0'/0/0",
coin: 'btc',
showOnOneKey: true,
});
if (!res.success) throw new Error(res.payload.error);
// res.payload.address and res.payload.path are strings.参数
path: 必填,BIP44 路径或数组;需与脚本 purpose 对应。coin: 获取地址时可选,网络标识(如btc/test)。showOnOneKey?: 可选,是否在设备上展示并确认。
返回
type AddressResult =
| { success: true; payload: { address: string; path: string } }
| { success: false; payload: { error: string; code?: string | number } };场景 2:签名交易
const { success, payload } = await HardwareSDK.btcSignTransaction(connectId, deviceId, {
useEmptyPassphrase: true,
coin: 'btc',
inputs,
outputs,
refTxs,
locktime: 0,
});
if (!success) throw new Error(payload.error);
// Verify payload.serializedTx and payload.signatures before broadcasting.参数
coin: 必填,决定网络规则与前缀。inputs: 必填,数组,字段包含(当前公共类型要求每个输入提供amount):address_n(路径,与脚本匹配)prev_hash(UTXO txid,hex)prev_index(UTXO vout)amount(字符串 satoshi)script_type(SPENDADDRESS/SPENDP2SHWITNESS/SPENDWITNESS/SPENDTAPROOT)
outputs: 必填,数组,字段包含:- 收款地址输出:
address+amount+script_type(如PAYTOADDRESS) - 找零输出:
address_n+amount+script_type对应脚本
- 收款地址输出:
refTxs: 必填数组,包含固件请求的完整前序交易;仅在签名流程不请求前序交易时可为空(需包含hash、version、lock_time、inputs、bin_outputs或outputs,按前序交易实际结构填写)locktime?: 可选,数字
返回
type SignResult =
| { success: true; payload: { serializedTx: string; signatures: string[]; txid?: string } }
| { success: false; payload: { error: string; code?: string | number } };交互与状态
- 接口以 Promise 结束;用户提示通过
UI_REQUEST事件触发(解锁设备、确认输入/输出/找零/手续费)。 - 同一设备请串行调用;签名前用
showOnOneKey确认收款/找零路径。
示例
示例:P2SH-P2WPKH 交易(嵌套 SegWit)
以下是准备参数的片段。调用前须将示意 UTXO/输出替换为实际交易,并加载与之匹配的完整 refTxs。
import HardwareSDK from '@onekeyfe/hd-common-connect-sdk';
import type { BTCSignTransactionParams } from '@onekeyfe/hd-core';
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 path49 = [(49 | 0x80000000) >>> 0, (0 | 0x80000000) >>> 0, (0 | 0x80000000) >>> 0, 0, 0];
const confirmedAddress = await HardwareSDK.btcGetAddress(connectId, deviceId, {
path: path49, coin: 'btc',
showOnOneKey: true,
useEmptyPassphrase: true,
});
if (!confirmedAddress.success) throw new Error(confirmedAddress.payload.error);
const inputs: BTCSignTransactionParams['inputs'] = [
{
address_n: path49,
prev_index: 0,
prev_hash: 'b035d89d4543ce5713c553d69431698116a822c57c03ddacf3f04b763d1999ac',
amount: '3382047',
script_type: 'SPENDP2SHWITNESS',
},
];
const outputs: BTCSignTransactionParams['outputs'] = [
{
address_n: [(49 | 0x80000000) >>> 0, (0 | 0x80000000) >>> 0, (0 | 0x80000000) >>> 0, 1, 1],
amount: '3181747',
script_type: 'PAYTOP2SHWITNESS', // change
},
{
address: '18WL2iZKmpDYWk1oFavJapdLALxwSjcSk2',
amount: '200000',
script_type: 'PAYTOADDRESS', // recipient
},
];
// Load complete previous transactions matching each requested prev_hash.
declare const refTxs: BTCSignTransactionParams['refTxs'];
const { success, payload } = await HardwareSDK.btcSignTransaction(connectId, deviceId, {
useEmptyPassphrase: true,
coin: 'btc',
inputs,
outputs,
refTxs,
locktime: 0,
});
if (!success) throw new Error(payload.error);
// Verify the signed transaction before submitting it through your Bitcoin client.验签与排障
- 路径 ↔ 脚本:
44'→ P2PKH49'→ P2SH-P2WPKH84'→ P2WPKH86'→ P2TR
refTxs必须包含固件请求的完整前序交易;缺失被请求的数据会被拒签。- 找零:确保
address_n属于同一账户路径,防止用户混淆。 - 设备确认:输入、输出、找零、手续费需与前端显示一致。
- 验签:用
bitcoinjs-lib/@scure/btc-signer重建交易,校验 txid 与serializedTx。 - 常见问题:
- 输入过多:向用户报告限制;拆分或调整交易后,必须重新构造请求并让用户核对。
- 手续费异常:停止当前签名;经用户确认后重新计算输出、找零与 dust 限制,不自动改写已确认的交易。
- 拒签/超时:提供重试/取消,不要自动重播。
参见方法文档:btcSignTransaction · btcGetAddress。
最后更新于