Transaction Chains

Complete examples for creating transaction chains using the BEEF (Background Evaluation Extended Format) standard, enabling efficient SPV verification and zero-confirmation transaction chains.

Overview

Transaction chains allow you to create sequences of dependent transactions where outputs from one transaction are spent in subsequent transactions, all before any confirmations. The BEEF format packages these chains with their Merkle proofs, enabling efficient SPV verification without requiring the full blockchain.

Related SDK Components:

Basic Transaction Chain

import { Transaction, PrivateKey, P2PKH, BEEF } from '@bsv/sdk'

/**
 * Basic Transaction Chain Builder
 *
 * Create a simple chain of transactions where each transaction
 * spends outputs from the previous transaction.
 */
class TransactionChainBuilder {
  /**
   * Create a chain of transactions
   *
   * @param privateKey - Private key to sign transactions
   * @param initialUTXO - Starting UTXO
   * @param chainLength - Number of transactions in the chain
   * @param amountPerTx - Amount to transfer in each transaction
   * @returns Array of chained transactions
   */
  async buildSimpleChain(
    privateKey: PrivateKey,
    initialUTXO: {
      txid: string
      vout: number
      satoshis: number
      script: Script
    },
    chainLength: number,
    amountPerTx: number
  ): Promise<Transaction[]> {
    try {
      if (chainLength < 2) {
        throw new Error('Chain length must be at least 2')
      }

      const chain: Transaction[] = []
      let currentUTXO = initialUTXO

      for (let i = 0; i < chainLength; i++) {
        const tx = new Transaction()

        // Add input from previous transaction (or initial UTXO)
        tx.addInput({
          sourceTXID: currentUTXO.txid,
          sourceOutputIndex: currentUTXO.vout,
          unlockingScriptTemplate: new P2PKH().unlock(privateKey),
          sequence: 0xffffffff
        })

        // Calculate fee
        const fee = 500

        // Validate sufficient funds
        if (currentUTXO.satoshis < amountPerTx + fee) {
          throw new Error(`Insufficient funds at chain position ${i}`)
        }

        // Add output for next transaction in chain
        const outputScript = new P2PKH().lock(privateKey.toPublicKey().toHash())
        tx.addOutput({
          satoshis: amountPerTx,
          lockingScript: outputScript
        })

        // Add change output if needed
        const change = currentUTXO.satoshis - amountPerTx - fee

        if (change > 546) {
          tx.addOutput({
            satoshis: change,
            lockingScript: outputScript
          })
        }

        // Sign transaction
        await tx.sign()

        chain.push(tx)

        // Update current UTXO for next iteration
        currentUTXO = {
          txid: tx.id('hex'),
          vout: 0,
          satoshis: amountPerTx,
          script: outputScript
        }

        console.log(`Created transaction ${i + 1}/${chainLength} in chain`)
      }

      return chain
    } catch (error) {
      throw new Error(`Transaction chain building failed: ${error.message}`)
    }
  }

  /**
   * Create a branching chain (one input, multiple outputs, each spent separately)
   */
  async buildBranchingChain(
    privateKey: PrivateKey,
    initialUTXO: {
      txid: string
      vout: number
      satoshis: number
      script: Script
    },
    branches: number
  ): Promise<{ root: Transaction; branches: Transaction[] }> {
    try {
      const outputScript = new P2PKH().lock(privateKey.toPublicKey().toHash())

      // Create root transaction with multiple outputs
      const rootTx = new Transaction()

      rootTx.addInput({
        sourceTXID: initialUTXO.txid,
        sourceOutputIndex: initialUTXO.vout,
        unlockingScriptTemplate: new P2PKH().unlock(privateKey),
        sequence: 0xffffffff
      })

      // Calculate amount per branch
      const fee = 500 + (branches * 50)
      const amountPerBranch = Math.floor((initialUTXO.satoshis - fee) / branches)

      if (amountPerBranch < 546) {
        throw new Error('Not enough satoshis for branches')
      }

      // Create outputs for each branch
      for (let i = 0; i < branches; i++) {
        rootTx.addOutput({
          satoshis: amountPerBranch,
          lockingScript: outputScript
        })
      }

      await rootTx.sign()

      // Create branch transactions
      const branchTxs: Transaction[] = []

      for (let i = 0; i < branches; i++) {
        const branchTx = new Transaction()

        branchTx.addInput({
          sourceTXID: rootTx.id('hex'),
          sourceOutputIndex: i,
          unlockingScriptTemplate: new P2PKH().unlock(privateKey),
          sequence: 0xffffffff
        })

        const branchFee = 500
        const branchOutput = amountPerBranch - branchFee

        branchTx.addOutput({
          satoshis: branchOutput,
          lockingScript: outputScript
        })

        await branchTx.sign()
        branchTxs.push(branchTx)
      }

      console.log(`Created branching chain: 1 root → ${branches} branches`)

      return { root: rootTx, branches: branchTxs }
    } catch (error) {
      throw new Error(`Branching chain creation failed: ${error.message}`)
    }
  }
}

/**
 * Usage Example
 */
async function basicChainExample() {
  const builder = new TransactionChainBuilder()
  const privateKey = PrivateKey.fromWif('your-wif-key')

  const initialUTXO = {
    txid: 'initial-txid...',
    vout: 0,
    satoshis: 100000,
    script: new P2PKH().lock(privateKey.toPublicKey().toHash())
  }

  // Create a chain of 5 transactions
  const chain = await builder.buildSimpleChain(privateKey, initialUTXO, 5, 15000)

  console.log('Chain created:')
  chain.forEach((tx, i) => {
    console.log(`  ${i + 1}. ${tx.id('hex')}`)
  })
}

BEEF Format Transaction Chain

Advanced Chain Patterns

Chain Validation and Management

See Also

SDK Components:

Last updated