UTXO Management

Complete examples for managing Unspent Transaction Outputs (UTXOs) efficiently.

Overview

UTXO management is critical for wallet performance and transaction building. This code feature demonstrates practical UTXO tracking, selection strategies, and optimization techniques.

Related SDK Components:

UTXO Tracker

import { Transaction, PrivateKey, PublicKey, Script } from '@bsv/sdk'

interface UTXO {
  txid: string
  vout: number
  satoshis: number
  script: Script
  confirmations?: number
}

/**
 * UTXO Tracker
 *
 * Track and manage UTXOs for a wallet
 */
class UTXOTracker {
  private utxos: Map<string, UTXO> = new Map()

  /**
   * Add UTXO to tracker
   */
  addUTXO(utxo: UTXO): void {
    const key = `${utxo.txid}:${utxo.vout}`
    this.utxos.set(key, utxo)
  }

  /**
   * Remove spent UTXO
   */
  removeUTXO(txid: string, vout: number): void {
    const key = `${txid}:${vout}`
    this.utxos.delete(key)
  }

  /**
   * Get all UTXOs
   */
  getAllUTXOs(): UTXO[] {
    return Array.from(this.utxos.values())
  }

  /**
   * Get total balance
   */
  getTotalBalance(): number {
    return Array.from(this.utxos.values())
      .reduce((sum, utxo) => sum + utxo.satoshis, 0)
  }

  /**
   * Get confirmed balance
   */
  getConfirmedBalance(minConfirmations: number = 6): number {
    return Array.from(this.utxos.values())
      .filter(utxo => (utxo.confirmations || 0) >= minConfirmations)
      .reduce((sum, utxo) => sum + utxo.satoshis, 0)
  }

  /**
   * Get UTXOs by minimum value
   */
  getUTXOsByMinValue(minValue: number): UTXO[] {
    return Array.from(this.utxos.values())
      .filter(utxo => utxo.satoshis >= minValue)
  }

  /**
   * Process transaction to update UTXO set
   */
  processTransaction(
    tx: Transaction,
    publicKeyHash: Buffer
  ): void {
    // Remove spent UTXOs (inputs)
    for (const input of tx.inputs) {
      this.removeUTXO(input.sourceTXID!, input.sourceOutputIndex!)
    }

    // Add new UTXOs (outputs)
    tx.outputs.forEach((output, index) => {
      if (this.isOwnedByKey(output.lockingScript, publicKeyHash)) {
        this.addUTXO({
          txid: tx.id('hex'),
          vout: index,
          satoshis: output.satoshis!,
          script: output.lockingScript,
          confirmations: 0
        })
      }
    })
  }

  /**
   * Check if output is owned by key
   */
  private isOwnedByKey(script: Script, publicKeyHash: Buffer): boolean {
    // Simple P2PKH detection
    const chunks = script.chunks
    if (chunks.length !== 5) return false

    if (chunks[0].op !== OP.OP_DUP) return false
    if (chunks[1].op !== OP.OP_HASH160) return false
    if (chunks[3].op !== OP.OP_EQUALVERIFY) return false
    if (chunks[4].op !== OP.OP_CHECKSIG) return false

    return chunks[2].buf?.equals(publicKeyHash) || false
  }
}

UTXO Selection Strategies

UTXO Consolidation

See Also

SDK Components:

Learning Paths:

Last updated