
Bitcoin Js
- 7 installs
- 4 repo stars
- Updated February 25, 2026
- hairyf/blockchain-master
Build and sign Bitcoin transactions in JavaScript with bitcoinjs-lib: payments, addresses, PSBT, script, and Taproot.
About
A reference for bitcoinjs-lib v7 covering payment/address types, PSBT construction, transaction/script building, and ECC setup. A developer uses it to create Bitcoin addresses and sign transactions in Node or the browser.
- P2PKH/P2SH/P2WPKH/P2WSH/P2TR payments and PSBT sign/finalize/extract
- Networks, initEccLib for Taproot, and ecpair/bip32/bip39 key handling
Bitcoin Js by the numbers
- 7 all-time installs (skills.sh)
- Ranked #331 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
- Data as of Jul 13, 2026 (Skillselion catalog sync)
npx skills add https://github.com/hairyf/blockchain-master --skill bitcoin-jsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 7 |
|---|---|
| repo stars | ★ 4 |
| Last updated | February 25, 2026 |
| Repository | hairyf/blockchain-master ↗ |
What it does
Build and sign Bitcoin transactions in JavaScript with bitcoinjs-lib: payments, addresses, PSBT, script, and Taproot.
Files
Skill based on bitcoinjs-lib v7.x, generated 2026-02-09. Docs: https://bitcoinjs.github.io/bitcoinjs-lib/
bitcoinjs-lib is a client-side Bitcoin library for Node and browsers: build/sign transactions via PSBT, create addresses and payment outputs (P2PKH, P2SH, P2WPKH, P2WSH, P2TR), decode/encode addresses. Keys come from ecpair and bip32; ECC must be initialized with initEccLib for signing and Taproot.
Core References
| Topic | Description | Reference |
|---|---|---|
| Payments & Addresses | p2pkh, p2sh, p2wpkh, p2wsh, p2tr; toOutputScript / fromOutputScript | core-payments-addresses |
| PSBT | Create, add I/O, sign, validate, finalize, extract transaction | core-psbt |
| Transaction & Script | Transaction parse/build, script compile/decompile, opcodes, networks | core-transaction-script |
Features
Networks & ECC
| Topic | Description | Reference |
|---|---|---|
| Networks, ECC, Keys | bitcoin/testnet/regtest, initEccLib, ecpair, bip32, bip39 | features-networks-ecc |
Best Practices
| Topic | Description | Reference |
|---|---|---|
| Security | RNG, no address reuse, no xpub sharing, verify before broadcast | best-practices-security |
External Links
Generation Info
- Source:
sources/bitcoin-js - Git SHA:
dc8d9e26f2b9c7380aec7877155bde97594a9ade - Generated: 2026-02-09
Security and Best Practices
Critical points for agents and implementers: RNG, key handling, and user verification.
RNG (random number generation)
- Default RNG is randombytes (Node
crypto.randomBytes/ browsercrypto.getRandomValues). Do not rely on default without verifying the environment. - Never use `Math.random()` for keys, nonces, or anything security-sensitive.
- ECC uses RFC6979 (deterministic k) where applicable; bugs in Buffer or RNG can still cause key or nonce reuse and catastrophic fund loss. Run the library’s test suite in your target environment.
Key and address discipline
- Do not reuse addresses — use a new address per receive where possible (privacy and safety).
- Do not share BIP32 extended public keys (xpubs) without understanding: one leaked extended private key or implementation bug can compromise all derived keys.
- Do not ask users to invent mnemonics (brain wallets); use BIP39 or similar with proper RNG.
Verification before broadcast
- Always have users verify a human-readable representation of the transaction (amounts, addresses, fee) before broadcast. The library does not enforce this; it is the integrator’s responsibility.
Production and trust
- Use tagged releases from npm/GitHub, not the master branch. Master is development-only.
- Audit and verify the library and dependencies; “don’t trust, verify.” Check that npm package matches a verified clone if possible.
Summary for agents
When generating or reviewing code that uses bitcoinjs-lib: ensure RNG is cryptographically secure, avoid address reuse and xpub sharing in UX, and enforce manual verification of transactions before broadcast. Prefer TypeScript and run tests in the target environment.
<!-- Source references:
- https://github.com/bitcoinjs/bitcoinjs-lib README (Usage, Can I trust this code?, Should I use this in production?)
-->
Payments and Addresses
Create payment outputs and addresses from pubkeys or scripts; decode addresses to output scripts. Keys (ECPair, BIP32) are from separate packages: ecpair, bip32.
Payment types
- p2pkh — Pay-to-PubKey-Hash (legacy, prefix
1) - p2sh — Pay-to-Script-Hash (e.g. multisig, P2SH-wrapped; prefix
3) - p2wpkh — SegWit native (prefix
bc1q) - p2wsh — SegWit script hash
- p2tr — Taproot (BIP341, key-path or script-path)
All live under bitcoin.payments.*. Each returns a Payment with at least { output?, address?, ... }. Pass network for mainnet/testnet/regtest (default mainnet).
Usage
import * as bitcoin from 'bitcoinjs-lib';
import ECPairFactory from 'ecpair';
import * as ecc from 'tiny-secp256k1';
const ECPair = ECPairFactory(ecc);
// Single-key P2PKH
const keyPair = ECPair.fromWIF('KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn');
const { address, output } = bitcoin.payments.p2pkh({ pubkey: keyPair.publicKey });
// address: '1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH', output: scriptPubKey Buffer
// P2WPKH (SegWit)
const { address } = bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey });
// 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
// P2SH-wrapped P2WPKH
const { address } = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({ pubkey: keyPair.publicKey }),
});
// 2-of-3 multisig (P2SH)
const pubkeys = [hex1, hex2, hex3].map(h => Buffer.from(h, 'hex'));
const { address } = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2ms({ m: 2, pubkeys }),
});
// Testnet
const { address } = bitcoin.payments.p2pkh({
pubkey: keyPair.publicKey,
network: bitcoin.networks.testnet,
});Address ↔ script
- toOutputScript(address, network?) — address string → scriptPubKey (Uint8Array). Network required for bech32 prefix resolution.
- fromOutputScript(scriptPubKey, network?) — scriptPubKey → address string.
const script = bitcoin.address.toOutputScript('1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH');
const addr = bitcoin.address.fromOutputScript(script);Key points
- Keys are not in bitcoinjs-lib: use
ecpair(single key) andbip32(HD). Passnetworkto ECPair/BIP32 when using testnet/regtest. - For Taproot (p2tr), ECC must be initialized:
bitcoin.initEccLib(eccLib)(e.g.tiny-secp256k1); browser Taproot may need@bitcoin-js/tiny-secp256k1-asmjsor@bitcoinerlab/secp256k1. - Payment
outputis the scriptPubKey; use it when building transactions or PSBTs if you want to avoid passing addresses.
<!-- Source references:
- https://github.com/bitcoinjs/bitcoinjs-lib
- README.md, ts_src/address.ts, ts_src/payments/index.ts, test/integration/addresses.spec.ts
-->
PSBT (Partially Signed Bitcoin Transactions)
bitcoin.Psbt implements BIP174: create a PSBT, add inputs/outputs, sign (single or all), validate signatures, finalize, then extract the final Transaction. Supports non-segwit and segwit (P2WPKH, P2WSH, P2TR).
Roles (BIP174)
- Creator:
new bitcoin.Psbt(opts?)— optional{ network, maximumFeeRate }. - Updater:
addInput,addOutput,updateInput,updateOutput,updateGlobal. - Signer:
signInput(index, signer)orsignAllInputs(signer); async variantssignInputAsync,signAllInputsAsyncfor HDSignerAsync. - Finalizer:
finalizeAllInputs()orfinalizeInput(index). - Extractor:
extractTransaction()— returns aTransaction(then.toHex()or broadcast).
Adding inputs
Each input needs:
- hash (txid of the prior tx; string or 32-byte Buffer; if Buffer it is internal byte order) and index (vout).
- nonWitnessUtxo: full previous transaction as Buffer (required for non-segwit).
- witnessUtxo:
{ script, value }(scriptPubKey and value in satoshis) for segwit; can replace nonWitnessUtxo for segwit. - redeemScript for P2SH; witnessScript for P2WSH; Taproot uses library’s internal handling.
Adding outputs
- addOutput({ address, value }) — value in satoshis (bigint or number);
networkin Psbt opts used to encode address to script. - addOutput({ script, value }) — skip address encoding; no network needed.
Signing
- signInput(i, signer) — signer must implement
sign(hash, lowR?)(and for Taproot, optional script path). ECPair fromecpairworks. - validateSignaturesOfInput(i, validator) — validator(pubkey, msghash, signature) returns boolean (e.g. use ECPair.fromPublicKey(pubkey).verify(msghash, signature)).
Usage
import * as bitcoin from 'bitcoinjs-lib';
import ECPairFactory from 'ecpair';
import * as ecc from 'tiny-secp256k1';
const ECPair = ECPairFactory(ecc);
const alice = ECPair.fromWIF('L2uPYXe17xSTqbCjZvL2DsyXPCbXspvcu5mHLDYUgzdUbZGSKrSr');
const psbt = new bitcoin.Psbt()
.addInput({
hash: '7d067b4a697a09d2c3cff7d4d9506c9955e93bff41bf82d439da7d030382bc3e',
index: 0,
nonWitnessUtxo: Buffer.from('0200000001...', 'hex'), // full prev tx
})
.addOutput({ address: '1KRMKfeZcmosxALVYESdPNez1AP1mEtywp', value: 80000n });
psbt.signInput(0, alice);
psbt.validateSignaturesOfInput(0, (pubkey, msghash, sig) =>
ECPair.fromPublicKey(pubkey).verify(msghash, sig)
);
psbt.finalizeAllInputs();
const tx = psbt.extractTransaction();
const hex = tx.toHex();Key points
- nonWitnessUtxo is required for non-segwit inputs; for segwit, witnessUtxo (and redeem/witness script if P2SH/P2WSH) is enough.
- hash: if string, treat as txid (reversed from on-chain); if Buffer, use 32-byte tx hash (internal order).
- combine(psbt2, ...) merges multiple PSBTs; the caller’s data wins on conflict. Base transaction (version, locktime, inputs/outputs layout) must match.
- For HD signing use
signAllInputsAsync(hdSigner)with an object that implementssignSchnorr/signand derivation (e.g. bip32 instance).
<!-- Source references:
- https://github.com/bitcoinjs/bitcoinjs-lib
- ts_src/psbt.ts, test/integration/transactions.spec.ts
-->
Transaction and Script
Low-level building blocks: Transaction class for parsing and building txs, and script module for scriptPubKey/redeemScript handling and opcodes.
Transaction
- Transaction.fromBuffer(buf) — deserialize; returns
Transactionwithins,out,version,locktime. - new Transaction() — build manually: set
version,locktime, push toins/out. - tx.toBuffer() / tx.toHex() — serialize.
- tx.ins:
Input[]({ hash, index, script, sequence, witness }); tx.outs:Output[]({ script, value: bigint }). - Static:
Transaction.SIGHASH_*,Transaction.DEFAULT_SEQUENCE,Transaction.ADVANCED_TRANSACTION_*for segwit marker/flag.
In practice, most code uses Psbt to build and sign; use Transaction when you need to parse or modify raw txs.
Script
- script.compile(stack) — stack (array of Buffers or opcode numbers) → script Buffer.
- script.decompile(buffer) — script Buffer → stack or null.
- script.toASM(buffer) — script to ASM string; script.fromASM(asm) — ASM to Buffer.
- script.isPushOnly(stack) — whether stack is push-only.
- opcodes —
bitcoin.opcodesorscript.OPS(OP_DUP, OP_HASH160, OP_CHECKSIG, etc.).
Use payments for high-level scriptPubKey construction (p2pkh, p2sh, etc.); use script when you need to compile custom redeem/witness scripts or inspect scripts.
Networks
- bitcoin.networks.bitcoin (mainnet), bitcoin.networks.testnet, bitcoin.networks.regtest — used by payments and address encoding.
- Custom network:
{ messagePrefix, bech32, bip32: { public, private }, pubKeyHash, scriptHash, wif }.
Key points
- Output value is bigint (sats); input
hashis 32-byte Uint8Array (reversed from txid string). - For signing flow, prefer Psbt; use Transaction when parsing blocks/RPC or implementing custom serialization.
<!-- Source references:
- https://github.com/bitcoinjs/bitcoinjs-lib
- ts_src/transaction.ts, ts_src/script.ts, ts_src/networks.ts
-->
Networks, ECC, and Key Libraries
bitcoinjs-lib does not ship key generation or storage; it relies on ecpair (single keys) and bip32 (HD keys). Networks define address prefixes and BIP32 version bytes.
Networks
- bitcoin.networks.bitcoin — mainnet (P2PKH
1, P2SH3, bech32bc). - bitcoin.networks.testnet — testnet (P2PKH
m/n, bech32tb). - bitcoin.networks.regtest — regtest (same prefixes as testnet, bech32
bcrt).
Pass network into payments.* and new Psbt({ network }) when using non-mainnet or when encoding/decoding addresses.
ECC library (required for signing / Taproot)
- bitcoin.initEccLib(eccLib) — call once before using signing or Taproot. Typical choice:
tiny-secp256k1(Node/bundled). - Node:
import * as ecc from 'tiny-secp256k1'; bitcoin.initEccLib(ecc); - Browser Taproot:
tiny-secp256k1uses WASM; if needed use@bitcoin-js/tiny-secp256k1-asmjsor@bitcoinerlab/secp256k1(needs globalBigInt).
Key libraries (external)
- ecpair:
ECPairFactory(ecc)thenfromWIF(wif),makeRandom({ rng, network }),fromPublicKey(pubkey). Implementssign(hash),publicKey,verify(hash, signature). - bip32:
BIP32Factory(ecc)thenfromSeed(seed),derivePath(path). Implements HDSigner:derive(path),publicKey,sign(hash),signSchnorr(hash)for Taproot. Use withsignAllInputsAsyncfor HD flows. - bip39: mnemonic → seed; use with bip32
fromSeed.
import * as bitcoin from 'bitcoinjs-lib';
import * as ecc from 'tiny-secp256k1';
import ECPairFactory from 'ecpair';
import BIP32Factory from 'bip32';
bitcoin.initEccLib(ecc);
const ECPair = ECPairFactory(ecc);
const bip32 = BIP32Factory(ecc);
const key = ECPair.fromWIF('...');
const node = bip32.fromSeed(seed);
const child = node.derivePath("m/84'/0'/0'/0/0");
// use child as signer with PsbtKey points
- Always call initEccLib before signing or Taproot; one call per process.
- Use Node LTS or supported env; Buffer/crypto behavior differs in browsers (RNG, Buffer polyfill).
- For Taproot in browser, prefer documented ECC alternatives and run tests in target environment.
<!-- Source references:
- https://github.com/bitcoinjs/bitcoinjs-lib (README Installation, Usage, Browser)
- ts_src/ecc_lib.ts, ts_src/networks.ts
-->