npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

bitcoin-tx-lib

v3.1.2

Published

A Typescript library for building and signing Bitcoin transactions

Downloads

671

Readme

bitcoin-tx-lib

A TypeScript library for building and signing Bitcoin transactions (P2PKH and P2WPKH), focused on compatibility across any TypeScript environment, with minimal dependencies and built in pure TypeScript. Fully compatible with React, React Native, and any TypeScript project, with no reliance on native modules.

Index

Install

npm install bitcoin-tx-lib

Manage Pair Key

How to create and import key pairs from different sources

import { ECPairKey } from 'bitcoin-tx-lib'

// Generate a random key pair (mainnet)
const pairKey = new ECPairKey()

// Generate a random key pair (testnet)
const pairKey = new ECPairKey({ network: "testnet" })

// Import from raw private key (hex)
const pairKey = ECPairKey.fromHex(
    "0c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471be89827e19d72aa1d",
    "testnet"
)

// Import from WIF (Wallet Import Format)
// Both compressed (52-char) and uncompressed (51-char) WIFs are supported
const pairKey = ECPairKey.fromWif("cNk5Vf4VwDPSUFqn4JzwGJHpNMm5mWnHSTRFDWJZm7jdHrr5Uef")

How to extract key pair information

const pairKey = ECPairKey.fromWif("cNk5Vf4VwDPSUFqn4JzwGJHpNMm5mWnHSTRFDWJZm7jdHrr5Uef")

// Get private key as Uint8Array (use getPrivateKeyHex() for hex string)
const privateKey: Uint8Array = pairKey.getPrivateKey()

// Get compressed public key as Uint8Array (use getPublicKeyHex() for hex string)
const publicKey: Uint8Array = pairKey.getPublicKey()

// Get address (P2WPKH native SegWit by default)
const addressSegwit = pairKey.getAddress()          // "p2wpkh" is the default
const addressLegacy = pairKey.getAddress("p2pkh")

// Export WIF (compressed format, compatible with standard wallets)
const wif = pairKey.getWif()

How to set up a transaction

Supported script types: P2PKH (legacy) and P2WPKH (native SegWit). The library automatically detects the type from the scriptPubKey of each input.

Transaction (single key)

Use Transaction when all inputs are controlled by the same key pair.

import { ECPairKey, Transaction } from 'bitcoin-tx-lib'

const pairKey = ECPairKey.fromWif("cNk5Vf4VwDPSUFqn4JzwGJHpNMm5mWnHSTRFDWJZm7jdHrr5Uef")

const tx = new Transaction(pairKey)

tx.addInput({
    txid: "157da15b3cdb2561602bd889d578227aa089915e3945c6d26569d27aecb9a4f7",
    vout: 1,
    value: 15197,
    // scriptPubKey is optional — auto-derived from the key pair if omitted
    scriptPubKey: "0014a8439c50793b033df810de257b313144a8f7edc9"
})

tx.addOutput({
    address: "tb1q4mqy9h6km8wzltgtxra0vt4efuruhg7vh8hlvf",
    amount: 14197  // value minus fee
})

tx.sign()

const rawHex = tx.getRawHex()  // hex-encoded signed transaction
const txid   = tx.getTxid()    // correct txid (non-witness hash for SegWit, BIP 141)

Note: Multiple UTXOs from the same parent transaction (same txid, different vout) are fully supported. Only spending the exact same UTXO (txid + vout) twice is rejected.

HDTransaction (per-input key)

Use HDTransaction when inputs are signed by different HD-derived key pairs.

import { HDTransaction, HDWallet } from 'bitcoin-tx-lib'

const { wallet } = HDWallet.create()  // or HDWallet.import(mnemonic)

const tx = new HDTransaction()

tx.addInput({
    txid: "157da15b3cdb2561602bd889d578227aa089915e3945c6d26569d27aecb9a4f7",
    vout: 0,
    value: 10000
}, wallet.getPairKey(0))  // key for this specific input

tx.addInput({
    txid: "157da15b3cdb2561602bd889d578227aa089915e3945c6d26569d27aecb9a4f7",
    vout: 1,
    value: 20000
}, wallet.getPairKey(1))  // different key for this input

tx.addOutput({
    address: "tb1q4mqy9h6km8wzltgtxra0vt4efuruhg7vh8hlvf",
    amount: 29500
})

tx.sign()

const rawHex = tx.getRawHex()
const txid   = tx.getTxid()

Network fee

Pass whoPayTheFee and fee (sat/vbyte) when creating the transaction. Call resolveFee() to deduct the fee from the designated output(s), then call sign() to rebuild the transaction with the updated amounts.

resolveFee() is idempotent — calling it multiple times has no additional effect.

const tx = new Transaction(pairKey, {
    whoPayTheFee: "tb1q4mqy9h6km8wzltgtxra0vt4efuruhg7vh8hlvf",
    fee: 2,  // 2 sat/vbyte
})

tx.addInput({ txid: "...", vout: 1, value: 30000 })
tx.addOutput({ address: "tb1q4mqy9h6km8wzltgtxra0vt4efuruhg7vh8hlvf", amount: 15000 })
tx.addOutput({ address: "tb1q4ppec5re8vpnm7qsmcjhkvf3gj500mwfw0yxaj", amount: 15000 })

tx.resolveFee()  // deducts fee from the first address only
tx.sign()        // rebuild with fee-adjusted amounts

const raw = tx.getRawHex()

Set whoPayTheFee to "everyone" to split the fee evenly among all outputs:

const tx = new Transaction(pairKey, {
    whoPayTheFee: "everyone",
    fee: 2,
})
// ...
tx.resolveFee()
tx.sign()

Use getFeeSats() to inspect the fee without deducting it:

tx.sign()
const fee = tx.getFeeSats()  // total fee in satoshis

Transaction size helpers

tx.sign()

const weight = tx.weight()   // BIP 141 weight units
const vbytes = tx.vBytes()   // virtual bytes (ceil(weight / 4))

Replace-By-Fee (RBF): By default every input uses sequence = 0xfffffffd, enabling RBF (BIP 125). To disable RBF for an input, set sequence: "ffffffff".

Changelog

0.6.0

  • Fix: HDWallet.listAddresses() in watch-only mode now passes the wallet's network to Address.fromPubkey(). Previously, watch-only wallets always generated mainnet addresses regardless of the configured network.
  • Fix: HDWallet.import() and HDWallet.create() now propagate network to HDKManager.fromMnemonic(). Previously, network: "testnet" in options was silently ignored, causing getXPriv()/getXPub() to always return mainnet-prefixed keys (xprv/xpub) even for testnet wallets.
  • Feature: HDKManager.fromXPriv() and fromXPub() now support all four extended key formats: xprv/xpub (BIP44 mainnet), tprv/tpub (BIP44 testnet), zprv/zpub (BIP84 mainnet), vprv/vpub (BIP84 testnet). Previously, only xprv/xpub were accepted; other formats caused a "Version mismatch" error from the underlying BIP32 library.
  • Feature: HDKManager.fromXPriv() and fromXPub() now automatically infer purpose (44 or 84) and network (mainnet or testnet) from the key prefix. These inferred values can be overridden via pathParams.
  • Feature: HDWallet.import() now accepts zprv/zpub and vprv/vpub in addition to xprv/tprv and xpub/tpub. Network and purpose are inferred from the key prefix automatically.
  • Feature: HDKManager now stores a network property. When creating from mnemonic or seed with network: "testnet", derived pair keys inherit testnet by default without requiring an explicit network argument on each derivatePairKey() call.
  • Feature: getXPriv()/getXPub() now return the correct prefix for the configured network and purpose. For example, a BIP84 testnet wallet exports vprv/vpub instead of always returning zprv/zpub.
  • Fix: Flaky test in txbuilder.test.ts ("legacy transaction: raw equals txid build") corrected to allow ±1 byte variation caused by non-deterministic DER signature length (extraEntropy: true). The test was re-signing the transaction on each build() call, producing occasional mismatches.

0.5.0

  • Fix: HDTransaction.sign() now correctly uses the stripped (non-witness) serialization for txid computation, satisfying BIP 141. Previously, SegWit txids were computed over the witness-inclusive bytes.
  • Fix: HDKManager.fromMnemonic() and fromMasterSeed() now forward purpose, coinType, account, and change options to the constructor. Previously, purpose: 44 was silently ignored, causing BIP84 paths to be used instead of BIP44.
  • Fix: ECPairKey.getWif() now appends the 0x01 compressed-key flag byte before the checksum, producing standard compressed WIF (52-char). Previously, exported WIFs were interpreted by external wallets as uncompressed keys, deriving incorrect addresses.
  • Fix: generateScriptSig() now uses each input's own sequence value in the signing preimage. Previously, all inputs in the preimage inherited the sequence of the input being signed, producing invalid signatures for multi-input legacy transactions.
  • Fix: resolveFee() is now idempotent (calling twice no longer double-deducts the fee) and invalidates the cached raw transaction after adjusting output amounts, so getRawHex() always returns the fee-adjusted transaction.
  • Fix: validateInput() now checks txid + vout for duplicates instead of txid alone, allowing multiple UTXOs from the same parent transaction and correctly rejecting only double-spends of the exact same output.
  • Fix: HDTransactionBase.build() now encodes the legacy scriptSig length as a proper Bitcoin varint instead of a fixed 1-byte value, fixing malformed transactions when scriptSig exceeds 252 bytes.
  • Fix: scriptPubkeyToScriptCode() now correctly identifies P2WSH scripts (OP_0 <32-byte-hash>, prefix 0x00 0x20) enabling witness generation for P2WSH inputs. The previous check used opcode 0x79 (OP_SWAP) which never matched any real script.
  • Fix: ECPairKey.signDER() no longer loops until exactly 70 bytes. All valid DER signatures (70–72 bytes) are now accepted, eliminating an unnecessary restriction and potential long loops for certain key/message combinations.
  • Fix: hexToBytes() loop bound corrected from <= to <, eliminating a silent out-of-bounds write on typed arrays.
  • Fix: OP_PUSHBYTES_20 and OP_PUSHBYTES_32 corrected to 0x14 (20) and 0x20 (32) respectively.
  • Improvement: weight() now derives witness size from the cached serialized transaction (txraw vs txidraw diff) instead of re-signing all inputs on each call, making it consistent and eliminating redundant signing.
  • Test: Added idempotency tests for resolveFee().
  • Test: Added test covering multiple inputs from the same parent transaction.