Skip to Content

BTC Signer

Bitcoin UTXO signing guide. Covers address confirmation and P2PKH / P2SH-P2WPKH / P2WPKH / Taproot signing with strict path–script matching and complete refTxs. Behavior aligned with hardware-js-sdk (Promise responses + UI events).

Index

  1. How it works
  2. Installation
  3. Initialization
  4. Common scenarios
  5. Interaction & Status
  6. Examples
  7. Validation & Troubleshooting

How it works

OneKey BTC signer uses HardwareSDK and the OneKey protocol to request UTXO review and signing on the device.

  • Paths must match script purpose (44/49/84/86).
  • Provide complete previous transactions requested by the signing flow in refTxs; the requirements depend on input type and firmware.
  • The device shows inputs, outputs, change, and fee; after confirmation it returns signatures or a serialized tx.

Installation

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

Initialization

The examples use the standard wallet consistently. For a hidden wallet, replace useEmptyPassphrase: true with the verified passphraseState on every address and signing call. Register UI handlers before protected calls, and check success before using any payload. Examples containing declared transaction inputs are preparation fragments, not standalone runnable applications.

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");

Common scenarios

Scenario 1: Get Address

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.

Parameters

  • path: required; must match purpose.
  • coin: optional for address requests; e.g., btc, test.
  • showOnOneKey?: optional; display and confirm on device.

Returns

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

Scenario 2: Sign Transaction

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.

Parameters

  • coin: required; selects network rules.
  • inputs: required array with:
    • address_n (path matched to script)
    • prev_hash (txid hex)
    • prev_index (vout)
    • amount (string satoshi)
    • script_type (SPENDADDRESS / SPENDP2SHWITNESS / SPENDWITNESS / SPENDTAPROOT)
  • outputs: required array with:
    • Recipient: address + amount + script_type (e.g., PAYTOADDRESS)
    • Change: address_n + amount + script_type aligned to purpose
  • refTxs: required array; supply all previous transactions requested by firmware. Use an empty array only when the signing flow needs none.
  • locktime?: optional.

Returns

type SignResult = | { success: true; payload: { serializedTx: string; signatures: string[]; txid?: string } } | { success: false; payload: { error: string; code?: string | number } };

Interaction & Status

  • APIs resolve on completion; user prompts arrive via UI_REQUEST (unlock device, confirm inputs/outputs/change/fee).
  • Run calls serially per device; use showOnOneKey to confirm receive/change paths before signing.

Examples

P2SH-P2WPKH transaction

This is a preparation fragment. Replace the illustrative UTXO/output data with your transaction and load matching complete refTxs before calling the SDK.

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.

Validation & Troubleshooting

  • Path ↔ script: 44'→P2PKH, 49'→P2SH-P2WPKH, 84'→P2WPKH, 86'→P2TR.
  • refTxs must contain every previous transaction requested by the firmware; missing requested data is rejected.
  • Change output should stay within the same account path.
  • Verify on device: inputs, outputs, change, fee must match your UI.
  • Host verify: rebuild with bitcoinjs-lib / @scure/btc-signer, check serializedTx/txid and signatures.
  • Common issues:
    • Too many inputs: report the limit. Splitting or simplifying a transaction requires rebuilding it and obtaining a new user review.
    • Fee anomaly: stop signing. Recalculate outputs, change, and dust limits with the user; do not silently alter a reviewed transaction.
    • User reject/timeout: offer retry/cancel; no auto-resubmission.

See method docs: btcSignTransaction · btcGetAddress.

Last updated on