Standard Transactions
Complete examples for creating standard BSV transaction types including P2PKH, P2PK, and their variations.
Overview
BSV supports several standard transaction types that define how bitcoins are locked and unlocked. The most common is Pay-to-Public-Key-Hash (P2PKH), but understanding all standard types is essential for building robust applications. This guide covers P2PKH, P2PK (Pay-to-Public-Key), and multi-output patterns.
Related SDK Components:
P2PKH Transactions
import { Transaction, PrivateKey, P2PKH, Script } from '@bsv/sdk'
/**
* Pay-to-Public-Key-Hash (P2PKH) Transactions
*
* The most common transaction type on BSV
*/
class P2PKHTransactions {
/**
* Create a standard P2PKH payment transaction
*/
async createP2PKHPayment(
senderKey: PrivateKey,
recipientAddress: string,
amount: number,
utxo: {
txid: string
vout: number
satoshis: number
script: Script
}
): Promise<Transaction> {
try {
if (amount < 546) {
throw new Error('Amount below dust threshold (546 satoshis)')
}
if (amount > utxo.satoshis) {
throw new Error('Insufficient funds in UTXO')
}
const tx = new Transaction()
// Add input with P2PKH unlocking script
tx.addInput({
sourceTXID: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(senderKey),
sequence: 0xffffffff
})
// Add payment output with P2PKH locking script
tx.addOutput({
satoshis: amount,
lockingScript: Script.fromAddress(recipientAddress)
})
// Calculate and add change output
const fee = 500
const change = utxo.satoshis - amount - fee
if (change >= 546) {
tx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(senderKey.toPublicKey().toHash())
})
}
await tx.sign()
console.log('P2PKH payment created')
console.log('Transaction ID:', tx.id('hex'))
console.log('Amount:', amount)
console.log('Recipient:', recipientAddress)
return tx
} catch (error) {
throw new Error(`P2PKH payment failed: ${error.message}`)
}
}
/**
* Create P2PKH transaction with multiple recipients
*/
async createMultiRecipientPayment(
senderKey: PrivateKey,
recipients: Array<{ address: string; amount: number }>,
utxos: Array<{
txid: string
vout: number
satoshis: number
script: Script
}>
): Promise<Transaction> {
try {
// Validate recipients
for (const recipient of recipients) {
if (recipient.amount < 546) {
throw new Error(`Amount for ${recipient.address} below dust threshold`)
}
}
const totalAmount = recipients.reduce((sum, r) => sum + r.amount, 0)
const totalInput = utxos.reduce((sum, u) => sum + u.satoshis, 0)
const fee = 500 + (utxos.length * 150) + (recipients.length * 50)
if (totalInput < totalAmount + fee) {
throw new Error('Insufficient funds')
}
const tx = new Transaction()
// Add all inputs
for (const utxo of utxos) {
tx.addInput({
sourceTXID: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(senderKey),
sequence: 0xffffffff
})
}
// Add recipient outputs
for (const recipient of recipients) {
tx.addOutput({
satoshis: recipient.amount,
lockingScript: Script.fromAddress(recipient.address)
})
}
// Add change output
const change = totalInput - totalAmount - fee
if (change >= 546) {
tx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(senderKey.toPublicKey().toHash())
})
}
await tx.sign()
console.log('Multi-recipient payment created')
console.log('Recipients:', recipients.length)
console.log('Total amount:', totalAmount)
console.log('Transaction ID:', tx.id('hex'))
return tx
} catch (error) {
throw new Error(`Multi-recipient payment failed: ${error.message}`)
}
}
/**
* Create P2PKH transaction with custom fee rate
*/
async createWithCustomFee(
senderKey: PrivateKey,
recipientAddress: string,
amount: number,
feeRate: number, // satoshis per byte
utxo: {
txid: string
vout: number
satoshis: number
script: Script
}
): Promise<Transaction> {
try {
// Estimate transaction size
const estimatedSize =
10 + // version, locktime
148 + // input (typical P2PKH)
34 + // payment output
34 // change output
const fee = Math.ceil(estimatedSize * feeRate)
const tx = new Transaction()
tx.addInput({
sourceTXID: utxo.txid,
sourceOutputIndex: utxo.vout,
unlockingScriptTemplate: new P2PKH().unlock(senderKey),
sequence: 0xffffffff
})
tx.addOutput({
satoshis: amount,
lockingScript: Script.fromAddress(recipientAddress)
})
const change = utxo.satoshis - amount - fee
if (change >= 546) {
tx.addOutput({
satoshis: change,
lockingScript: new P2PKH().lock(senderKey.toPublicKey().toHash())
})
}
await tx.sign()
console.log('P2PKH payment with custom fee')
console.log('Fee rate:', feeRate, 'sat/byte')
console.log('Total fee:', fee, 'satoshis')
console.log('Transaction ID:', tx.id('hex'))
return tx
} catch (error) {
throw new Error(`Custom fee payment failed: ${error.message}`)
}
}
}
/**
* Usage Example
*/
async function p2pkhExample() {
const p2pkh = new P2PKHTransactions()
const privateKey = PrivateKey.fromRandom()
const utxo = {
txid: 'funding-tx...',
vout: 0,
satoshis: 100000,
script: new P2PKH().lock(privateKey.toPublicKey().toHash())
}
// Simple payment
const tx1 = await p2pkh.createP2PKHPayment(
privateKey,
'1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
50000,
utxo
)
// Multi-recipient payment
const tx2 = await p2pkh.createMultiRecipientPayment(
privateKey,
[
{ address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa', amount: 10000 },
{ address: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2', amount: 20000 }
],
[utxo]
)
}P2PK Transactions
Transaction Templates
Address Validation and Conversion
Related Examples
See Also
SDK Components:
P2PKH - P2PKH script template
Transaction - Transaction building
Script - Script operations
Script Templates - Template system
Learning Paths:
Last updated
