• TokenSoft

Signing Bitcoin Transactions on Ledger with bcoin's Bledger

by Randall Leung, Developer, TokenSoft


If you are already using bcoin as part of your bitcoin infrastructure and need to sign transactions on a Ledger, you have come to the right place.

We’ve found that Bledger, which is found in bcoin’s open source library, works well to connect to the ledger hardware wallet.

Bledger is an API layer that plays nicely with Bcoin’s transaction objects. In this post, I will walk through the implementation to help simplify the process of signing a bitcoin transaction.

Why is this important? By signing a transaction with your ledger, you can prove that you control a particular Bitcoin address and hence assert the ownership of funds.

Connecting to the Ledger

First, let's connect to the bledger app:

import bledger from 'bledger'
const { Device } = bledger.HID
const { LedgerBcoin } = bledger
// Get all available devices via bledger
const device = await Device.getDevices()
// Get the first device found
let device = new Device({
  device: devices[0],
  timeout: 60000 // 60 seconds
// Connect to the device's interface
await device.open()
// Connect to the ledger device via bledger
const bcoinApp = await new LedgerBcoin({ device })

Signing Implementation

To start, we need the bcoin transaction that needs to be signed (bcoinTransaction) and the inputs (ledgerTxInputsData) that we wish to include in the transaction. Read more about it here.

The bcoin transaction contains the following structure:

bcoinTransaction = {
  fee: String,
  hash: String,
  hex: String,
  inputs: Array,
  locktime: Int,
  mtime: Int,
  outputs: Array,
  rate: Int,
  version: Int,
  witnessHash: String

and the tx input’s structure:

ledgerTxInputsData = {
  hash: String,
  index: String,
  path: String,
  rawTx: String,
  redeemScript: String,
  witness: Boolean

Since the transactions in bcoin are immutable, we’ll need to create a mutable transaction:

const MTX = bcoin.MTX
const createdTx = MTX.fromJSON(bcoinTransaction)

The transaction inputs are the reference to an output from a previous transaction. A transaction often has multiple inputs. We’ll need to build the ledger input object for each input that needs to be signed.

// Bledger's wrapper on the ledger transaction input class
const { LedgerTXInput } = bledger
for (let i = 0; i < ledgerTxInputsData.length; i++) {
 let input = ledgerTxInputsData[i]
 // Set up the TX
  const tx = TX.fromRaw(Buffer.from(input.rawTx, 'hex'))
 // Get the public key from the ledger
  const hdPubKey = await bcoinApp.getPublicKey(input.path)
  const publicKey = hdPubKey.publicKey
 // If multisig, it will have a redeemscript
  let redeem = input.redeemScript && Buffer.from(input.redeemScript, 'hex')
 // Push the new object to the list
    new LedgerTXInput({
      witness: true, // segwit or not
      index: input.index,
      path: input.path,

Now that we have set up our ledger transaction inputs, we can use bcoinApp to sign the transaction:

await bcoinApp.signTransaction(createdTx, ledgerInputTxs)

At this point, the ledger will ask the user to confirm the output details.

Once the user confirms, we can verify it is a valid transaction:

console.log(`Valid Transaction: ${createdTx.verify()}.`)

If everything is correct, the transaction is signed.

const signedTransaction = createdTx.toJSON()

With the signed transaction, it is now ready to be broadcast to the blockchain!

Deployment with Electron

One of the problems we ran into was compiling bledger in an electron app. One of bledger’s dependencies, bcrypto, statically links with OpenSSL libs by default.

If you are unable to link Electron’s headers with the native modules, you may get the following error:

dyld: lazy symbol binding failed: Symbol not found: _SHA256_Init

To get around this, update your webpack plugins and replace environment variables at build time:

plugins: [
  new webpack.EnvironmentPlugin({
    // This is to force bcrypto to use JS only implementation
    NODE_BACKEND: 'js'

This will force bcrypto to use full JS implementation instead of linking against native OpenSSL libs. 👍


Feel free to reach out to me if you have any questions. Also, we’re hiring at TokenSoft if you want to check us out.

Otherwise, the bcoin’s slack channel has been very responsive and helpful. Check them out at: