Complete BIP174 implementation supporting all PSBT roles: Creator, Updater, Signer, Combiner, Input Finalizer, and Transaction Extractor. PSBTs enable collaborative transaction construction and signing across multiple parties and devices.
interface PsbtTxInput extends TransactionInput {
hash: Buffer;
}
interface PsbtTxOutput extends TransactionOutput {
address: string | undefined;
}
interface TransactionInput {
hash: string | Buffer;
index: number;
sequence?: number;
}
interface TransactionOutput {
script: Buffer;
value: number;
}
interface Signer {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Buffer;
signSchnorr?(hash: Buffer): Buffer;
getPublicKey?(): Buffer;
}
interface SignerAsync {
publicKey: Buffer;
network?: any;
sign(hash: Buffer, lowR?: boolean): Promise<Buffer>;
signSchnorr?(hash: Buffer): Promise<Buffer>;
getPublicKey?(): Buffer;
}
interface HDSigner {
publicKey: Buffer;
fingerprint: Buffer;
derivePath(path: string): HDSigner;
sign(hash: Buffer): Buffer;
}
interface HDSignerAsync {
publicKey: Buffer;
fingerprint: Buffer;
derivePath(path: string): HDSignerAsync;
sign(hash: Buffer): Promise<Buffer>;
}
type ValidateSigFunction = (pubkey: Buffer, msghash: Buffer, signature: Buffer) => boolean;Create PSBTs from various sources or start with a new empty PSBT.
class Psbt {
/**
* Create PSBT from base64 string
* @param data - Base64 encoded PSBT
* @param opts - Optional network and fee rate settings
* @returns New Psbt instance
*/
static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt;
/**
* Create PSBT from hex string
* @param data - Hex encoded PSBT
* @param opts - Optional network and fee rate settings
* @returns New Psbt instance
*/
static fromHex(data: string, opts?: PsbtOptsOptional): Psbt;
/**
* Create PSBT from buffer
* @param buffer - PSBT buffer
* @param opts - Optional network and fee rate settings
* @returns New Psbt instance
*/
static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt;
/**
* Create new empty PSBT
* @param opts - Optional network and fee rate settings
*/
constructor(opts?: PsbtOptsOptional);
}
interface PsbtOptsOptional {
network?: Network;
maximumFeeRate?: number;
}Usage Examples:
import { Psbt, networks } from 'bitcoinjs-lib';
// Create new PSBT
const psbt = new Psbt({ network: networks.bitcoin });
// Load from base64
const psbtBase64 = 'cHNidP8BAHECAAAAAf...';
const loadedPsbt = Psbt.fromBase64(psbtBase64);
// Load from hex
const psbtHex = '70736274ff0100710200000001...';
const loadedPsbt2 = Psbt.fromHex(psbtHex);
// Set maximum fee rate (sat/vB)
psbt.setMaximumFeeRate(50);Access basic PSBT information and transaction data.
/** Number of inputs in the PSBT */
get inputCount(): number;
/** Transaction version */
get version(): number;
set version(version: number);
/** Transaction locktime */
get locktime(): number;
set locktime(locktime: number);
/** Array of transaction inputs */
get txInputs(): PsbtTxInput[];
/** Array of transaction outputs */
get txOutputs(): PsbtTxOutput[];Usage Examples:
const psbt = new Psbt();
console.log('Input count:', psbt.inputCount);
console.log('Version:', psbt.version);
console.log('Locktime:', psbt.locktime);
// Set transaction properties
psbt.version = 2;
psbt.locktime = 650000;
// Or use fluent interface
psbt.setVersion(2).setLocktime(650000);Add and manage PSBT inputs with all necessary data for signing.
/**
* Add multiple inputs to PSBT
* @param inputDatas - Array of input data objects
* @returns This PSBT instance for chaining
*/
addInputs(inputDatas: PsbtInputExtended[]): this;
/**
* Add single input to PSBT
* @param inputData - Input data object
* @returns This PSBT instance for chaining
*/
addInput(inputData: PsbtInputExtended): this;
/**
* Set sequence number for existing input
* @param inputIndex - Index of input to modify
* @param sequence - New sequence number
* @returns This PSBT instance for chaining
*/
setInputSequence(inputIndex: number, sequence: number): this;
interface PsbtInputExtended extends PsbtInput, TransactionInput {
// Includes all BIP174 input fields plus transaction input data
}Usage Examples:
import { Psbt, networks } from 'bitcoinjs-lib';
const psbt = new Psbt({ network: networks.bitcoin });
// Add legacy input (P2PKH)
psbt.addInput({
hash: 'abc123def456...',
index: 0,
nonWitnessUtxo: Buffer.from('0100000001...', 'hex') // Full previous transaction
});
// Add SegWit input (P2WPKH)
psbt.addInput({
hash: 'def456abc123...',
index: 1,
witnessUtxo: {
script: Buffer.from('0014abcd...', 'hex'),
value: 50000
}
});
// Add Taproot input
psbt.addInput({
hash: 'fedcba987654...',
index: 0,
witnessUtxo: {
script: Buffer.from('5120abcd...', 'hex'),
value: 100000
},
tapInternalKey: Buffer.from('abc123...', 'hex')
});
// Set custom sequence
psbt.setInputSequence(0, 0xfffffffd); // Enable RBFAdd transaction outputs to the PSBT.
/**
* Add multiple outputs to PSBT
* @param outputDatas - Array of output data objects
* @returns This PSBT instance for chaining
*/
addOutputs(outputDatas: PsbtOutputExtended[]): this;
/**
* Add single output to PSBT
* @param outputData - Output data object
* @returns This PSBT instance for chaining
*/
addOutput(outputData: PsbtOutputExtended): this;
type PsbtOutputExtended = PsbtOutputExtendedAddress | PsbtOutputExtendedScript;
interface PsbtOutputExtendedAddress extends PsbtOutput {
address: string;
value: number;
}
interface PsbtOutputExtendedScript extends PsbtOutput {
script: Buffer;
value: number;
}Usage Examples:
const psbt = new Psbt({ network: networks.bitcoin });
// Add output by address
psbt.addOutput({
address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
value: 50000
});
// Add output by script
psbt.addOutput({
script: Buffer.from('76a914abcd...88ac', 'hex'),
value: 25000
});
// Add multiple outputs
psbt.addOutputs([
{ address: 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4', value: 30000 },
{ address: '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy', value: 20000 }
]);Sign inputs using various key types with support for different signature hash types.
/**
* Sign all inputs with regular key pair
* @param keyPair - Signer object
* @param sighashTypes - Optional array of sighash types per input
* @returns This PSBT instance for chaining
*/
signAllInputs(keyPair: Signer, sighashTypes?: number[]): this;
/**
* Sign all inputs with regular key pair (async)
* @param keyPair - Signer or async signer object
* @param sighashTypes - Optional array of sighash types per input
*/
signAllInputsAsync(keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>;
/**
* Sign specific input with regular key pair
* @param inputIndex - Index of input to sign
* @param keyPair - Signer object
* @param sighashTypes - Optional array of sighash types
* @returns This PSBT instance for chaining
*/
signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
/**
* Sign specific input with regular key pair (async)
* @param inputIndex - Index of input to sign
* @param keyPair - Signer or async signer object
* @param sighashTypes - Optional array of sighash types
*/
signInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, sighashTypes?: number[]): Promise<void>;Usage Examples:
import { Psbt, networks } from 'bitcoinjs-lib';
const psbt = new Psbt({ network: networks.bitcoin });
// ... add inputs and outputs ...
// Example signer (you need to implement this)
const signer = {
publicKey: Buffer.from('03a34b...', 'hex'),
sign: (hash: Buffer) => Buffer.from('3044...', 'hex') // ECDSA signature
};
// Sign all inputs
psbt.signAllInputs(signer);
// Sign specific input
psbt.signInput(0, signer);
// Sign with custom sighash type
psbt.signInput(1, signer, [Transaction.SIGHASH_SINGLE]);
// Async signing
await psbt.signAllInputsAsync(asyncSigner);Sign with hierarchical deterministic keys supporting key derivation.
/**
* Sign all inputs with HD key pair
* @param hdKeyPair - HD signer object
* @param sighashTypes - Optional array of sighash types per input
* @returns This PSBT instance for chaining
*/
signAllInputsHD(hdKeyPair: HDSigner, sighashTypes?: number[]): this;
/**
* Sign all inputs with HD key pair (async)
* @param hdKeyPair - HD signer or async HD signer object
* @param sighashTypes - Optional array of sighash types per input
*/
signAllInputsHDAsync(hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;
/**
* Sign specific input with HD key pair
* @param inputIndex - Index of input to sign
* @param hdKeyPair - HD signer object
* @param sighashTypes - Optional array of sighash types
* @returns This PSBT instance for chaining
*/
signInputHD(inputIndex: number, hdKeyPair: HDSigner, sighashTypes?: number[]): this;
/**
* Sign specific input with HD key pair (async)
* @param inputIndex - Index of input to sign
* @param hdKeyPair - HD signer or async HD signer object
* @param sighashTypes - Optional array of sighash types
*/
signInputHDAsync(inputIndex: number, hdKeyPair: HDSigner | HDSignerAsync, sighashTypes?: number[]): Promise<void>;Specialized signing methods for Taproot inputs supporting both key-path and script-path spending.
/**
* Sign Taproot input with key pair
* @param inputIndex - Index of input to sign
* @param keyPair - Signer object
* @param tapLeafHashToSign - Leaf hash for script-path spending
* @param sighashTypes - Optional array of sighash types
* @returns This PSBT instance for chaining
*/
signTaprootInput(inputIndex: number, keyPair: Signer, tapLeafHashToSign?: Buffer, sighashTypes?: number[]): this;
/**
* Sign Taproot input with key pair (async)
* @param inputIndex - Index of input to sign
* @param keyPair - Signer or async signer object
* @param tapLeafHash - Leaf hash for script-path spending
* @param sighashTypes - Optional array of sighash types
*/
signTaprootInputAsync(inputIndex: number, keyPair: Signer | SignerAsync, tapLeafHash?: Buffer, sighashTypes?: number[]): Promise<void>;Finalize inputs by constructing the final scriptSig and witness data.
/**
* Finalize all inputs
* @returns This PSBT instance for chaining
*/
finalizeAllInputs(): this;
/**
* Finalize specific input
* @param inputIndex - Index of input to finalize
* @param finalScriptsFunc - Optional custom finalization function
* @returns This PSBT instance for chaining
*/
finalizeInput(inputIndex: number, finalScriptsFunc?: FinalScriptsFunc): this;
/**
* Finalize Taproot input
* @param inputIndex - Index of input to finalize
* @param tapLeafHashToFinalize - Specific leaf hash to finalize
* @param finalScriptsFunc - Optional custom finalization function
* @returns This PSBT instance for chaining
*/
finalizeTaprootInput(inputIndex: number, tapLeafHashToFinalize?: Buffer, finalScriptsFunc?: FinalTaprootScriptsFunc): this;Usage Examples:
const psbt = new Psbt({ network: networks.bitcoin });
// ... add inputs, outputs, and sign ...
// Finalize all inputs
psbt.finalizeAllInputs();
// Finalize specific input
psbt.finalizeInput(0);
// Finalize Taproot input for script-path spending
const leafHash = Buffer.from('abc123...', 'hex');
psbt.finalizeTaprootInput(0, leafHash);Extract the final signed transaction from the PSBT.
/**
* Extract final transaction from PSBT
* @param disableFeeCheck - Skip fee validation
* @returns Final Transaction object
* @throws Error if inputs are not finalized or fees are too high
*/
extractTransaction(disableFeeCheck?: boolean): Transaction;
/**
* Calculate total fee for the transaction
* @returns Fee in satoshis
*/
getFee(): number;
/**
* Calculate fee rate for the transaction
* @returns Fee rate in satoshis per virtual byte
*/
getFeeRate(): number;Usage Examples:
const psbt = new Psbt({ network: networks.bitcoin });
// ... build, sign, and finalize PSBT ...
// Check fee before extraction
const fee = psbt.getFee();
const feeRate = psbt.getFeeRate();
console.log(`Fee: ${fee} sats (${feeRate} sat/vB)`);
// Extract final transaction
const finalTx = psbt.extractTransaction();
console.log('Final transaction:', finalTx.toHex());
// Broadcast finalTx.toHex() to networkCombine multiple PSBTs into a single PSBT.
/**
* Combine this PSBT with other PSBTs
* @param those - PSBTs to combine with this one
* @returns This PSBT instance for chaining
* @throws Error if PSBTs are incompatible
*/
combine(...those: Psbt[]): this;
/**
* Clone this PSBT
* @returns New PSBT instance with same data
*/
clone(): Psbt;Usage Examples:
const psbt1 = Psbt.fromBase64('cHNidP8BAHECAAAAAf...');
const psbt2 = Psbt.fromBase64('cHNidP8BAHECAAAAAf...');
const psbt3 = Psbt.fromBase64('cHNidP8BAHECAAAAAf...');
// Combine PSBTs (psbt1 takes precedence in conflicts)
psbt1.combine(psbt2, psbt3);
// Clone for modification
const modifiedPsbt = psbt1.clone();Validate signatures and check input/output properties.
/**
* Validate all signatures in the PSBT
* @param validator - Signature validation function
* @returns True if all signatures are valid
*/
validateSignaturesOfAllInputs(validator: ValidateSigFunction): boolean;
/**
* Validate signatures for specific input
* @param inputIndex - Index of input to validate
* @param validator - Signature validation function
* @param pubkey - Optional specific public key to validate
* @returns True if signatures are valid
*/
validateSignaturesOfInput(inputIndex: number, validator: ValidateSigFunction, pubkey?: Buffer): boolean;
/**
* Check if input has specific public key
* @param inputIndex - Index of input to check
* @param pubkey - Public key to search for
* @returns True if input contains the public key
*/
inputHasPubkey(inputIndex: number, pubkey: Buffer): boolean;
/**
* Check if input has HD key
* @param inputIndex - Index of input to check
* @param root - HD key root to search for
* @returns True if input contains derivation from the HD key
*/
inputHasHDKey(inputIndex: number, root: HDSigner): boolean;Convert PSBTs to various formats for storage and transmission.
/**
* Serialize PSBT to buffer
* @returns PSBT buffer
*/
toBuffer(): Buffer;
/**
* Serialize PSBT to hex string
* @returns Hex-encoded PSBT
*/
toHex(): string;
/**
* Serialize PSBT to base64 string
* @returns Base64-encoded PSBT
*/
toBase64(): string;Usage Examples:
const psbt = new Psbt({ network: networks.bitcoin });
// ... build PSBT ...
// Export formats
const psbtBase64 = psbt.toBase64();
const psbtHex = psbt.toHex();
const psbtBuffer = psbt.toBuffer();
console.log('PSBT Base64:', psbtBase64);Update PSBT global, input, and output data.
/**
* Update global PSBT data
* @param updateData - Global update data
* @returns This PSBT instance for chaining
*/
updateGlobal(updateData: PsbtGlobalUpdate): this;
/**
* Update input data
* @param inputIndex - Index of input to update
* @param updateData - Input update data
* @returns This PSBT instance for chaining
*/
updateInput(inputIndex: number, updateData: PsbtInputUpdate): this;
/**
* Update output data
* @param outputIndex - Index of output to update
* @param updateData - Output update data
* @returns This PSBT instance for chaining
*/
updateOutput(outputIndex: number, updateData: PsbtOutputUpdate): this;PSBTs are designed for hardware wallet workflows:
// 1. Create PSBT on online computer
const psbt = new Psbt({ network: networks.bitcoin });
psbt.addInput({/* input data */});
psbt.addOutput({/* output data */});
// 2. Export for hardware wallet
const psbtForHardware = psbt.toBase64();
// 3. Sign on hardware wallet (this happens externally)
// const signedPsbtBase64 = hardwareWallet.sign(psbtForHardware);
// 4. Import signed PSBT
const signedPsbt = Psbt.fromBase64(signedPsbtBase64);
// 5. Finalize and extract
signedPsbt.finalizeAllInputs();
const finalTx = signedPsbt.extractTransaction();PSBTs enable collaborative signing:
// Party 1: Create and partially sign
const psbt = new Psbt({ network: networks.bitcoin });
// ... add inputs and outputs ...
psbt.signInput(0, party1Signer);
// Party 2: Add their signature
const psbtBase64 = psbt.toBase64();
const party2Psbt = Psbt.fromBase64(psbtBase64);
party2Psbt.signInput(0, party2Signer);
// Combine signatures
const finalPsbt = Psbt.fromBase64(psbt.toBase64());
finalPsbt.combine(party2Psbt);interface Network {
messagePrefix: string;
bech32: string;
bip32: { public: number; private: number };
pubKeyHash: number;
scriptHash: number;
wif: number;
}
type FinalScriptsFunc = (
inputIndex: number,
input: PsbtInput,
script: Buffer,
isSegwit: boolean,
isP2SH: boolean,
isP2WSH: boolean
) => {
finalScriptSig: Buffer | undefined;
finalScriptWitness: Buffer | undefined;
};
type FinalTaprootScriptsFunc = (
inputIndex: number,
input: PsbtInput,
tapLeafHashToFinalize?: Buffer
) => {
finalScriptWitness: Buffer | undefined;
};