Standard Transactions
Overview
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
Last updated
