Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/satsigner/satsigner/llms.txt

Use this file to discover all available pages before exploring further.

SatSigner provides professional-grade UTXO (Unspent Transaction Output) management with visual tools for optimal transaction construction and privacy.

Understanding UTXOs

A UTXO represents spendable Bitcoin:
type Utxo = {
  txid: string           // Transaction ID
  vout: number          // Output index
  value: number         // Amount in satoshis
  timestamp?: Date      // When received
  label?: string        // User-defined label
  addressTo?: string    // Receiving address
  keychain: 'internal' | 'external'  // Address type
  script?: number[]     // ScriptPubKey
}

Keychain Types

  • External (0/*): Receiving addresses shown to others
  • Internal (1/*): Change addresses for leftover funds

Bubble Chart Visualization

SatSigner’s signature feature is its interactive bubble chart for UTXO visualization:

Visual Representation

  • Bubble size = UTXO value (larger = more sats)
  • Color = Selection state (highlighted when selected)
  • Position = Optimally packed using D3.js algorithms
  • Zoom = Pinch to zoom for detailed view

Implementation

The bubble chart uses D3 hierarchy packing:
// Create UTXO hierarchy for visualization
const utxoHierarchy = hierarchy<UtxoListBubble>({
  id: 'root',
  children: utxoList,
  value: utxoList.reduce((acc, cur) => acc + cur.value, 0)
})
  .sum((d) => d?.value ?? 0)
  .sort((a, b) => (b?.value ?? 0) - (a?.value ?? 0))

// Create packed layout
const createPack = pack<UtxoListBubble>()
  .size([width, height])
  .padding(4)

const packedUtxos = createPack(utxoHierarchy).leaves()

Interactive Features

Gestures:
  • Tap: Select/deselect UTXO
  • Pinch: Zoom in/out
  • Pan: Move around (when zoomed)
  • Double-tap: Reset zoom
Display Modes:
  • All UTXOs: Show complete UTXO set
  • Selected Only: Focus on chosen inputs
  • Dimmed Unselected: Highlight selection context

UTXO Selection Algorithms

SatSigner implements multiple coin selection strategies:

1. Efficient Selection

Optimizes for lowest fees while meeting target amount:
function selectEfficientUtxos(
  utxos: Utxo[],
  targetAmount: number,
  feeRate: number,
  options?: {
    dustThreshold: number    // 546 sats default
    inputSize: number        // 148 bytes for P2PKH
    changeOutputSize: number // 34 bytes
  }
): {
  inputs: Utxo[]
  fee: number
  change: number
  error?: string
}
Selection Process:
  1. Filter economical UTXOs - Remove those costing more to spend than worth
  2. Try exact match - Find single UTXO close to target + fee
  3. Branch and bound - Optimal selection minimizing waste
  4. Accumulative fallback - Start with largest UTXOs

2. Branch and Bound Algorithm

Finds optimal UTXO combination with minimal excess:
function branchAndBoundUtxoSelection(
  utxos: Utxo[],
  targetAmount: number,
  feeRate: number
): {
  inputs: Utxo[]
  fee: number
  change: number
} | null
How It Works:
  1. Calculate effective value (value - cost to spend)
  2. Recursively try including/excluding each UTXO
  3. Track best solution with minimal waste
  4. Return optimal selection or null if impossible
Benefits:
  • Minimizes transaction fees
  • Reduces change outputs when possible
  • Optimal for privacy (fewer UTXOs)

3. STONEWALL Selection

Privacy-focused algorithm mimicking CoinJoin structure:
function selectStonewallUtxos(
  utxos: Utxo[],
  targetAmount: number,
  feeRate: number,
  options: {
    minInputs: 4
    maxInputs: 10
    minOutputs: 2
    maxOutputs: 4
  }
): {
  inputs: Utxo[]
  outputs: ChangeOutput[]
  fee: number
  privacyScore: number
  txSize: number
}
Privacy Features:
  • Multiple inputs: 4-10 inputs standard
  • Multiple outputs: 2-4 change outputs
  • Varied amounts: Unequal output values
  • Script diversity: Mix address types
  • No correlation: Avoid exact value matches
Privacy Scoring:
privacyScore = 
  (numInputs * 10) +           // More inputs = better
  (numOutputs * 15) +          // More outputs = better
  (scriptVariety * 20) +       // Different types = better
  (balanceRatio * 50) +        // Balanced outputs = better
  (noCorrelation * 30)         // No exact matches = better

Selection Strategy Comparison

AlgorithmUse CasePrivacyFee Efficiency
EfficientStandard paymentsMediumHigh
Branch & BoundSmall transactionsMediumHighest
STONEWALLPrivacy-consciousHighLower

Manual UTXO Selection

Transaction Builder Integration

Users can manually select UTXOs via the bubble chart:
// Transaction builder state
const transactionBuilder = {
  inputs: Map<string, Utxo>(),  // Selected UTXOs
  outputs: Output[],             // Destination outputs
  feeRate: number,              // Sat/vB
  fee: number,                  // Total fee
  rbf: boolean                  // Replace-by-fee
}

// Add UTXO to transaction
function addInput(utxo: Utxo) {
  const outpoint = `${utxo.txid}:${utxo.vout}`
  transactionBuilder.inputs.set(outpoint, utxo)
}

// Remove UTXO from transaction
function removeInput(utxo: Utxo) {
  const outpoint = `${utxo.txid}:${utxo.vout}`
  transactionBuilder.inputs.delete(outpoint)
}

UTXO Details View

Tapping a UTXO shows detailed information:
type UtxoDetails = {
  // Identity
  txid: string
  vout: number
  outpoint: string  // txid:vout
  
  // Value
  value: number     // Satoshis
  valueFormatted: string
  valueFiat: string
  
  // Source
  address: string
  addressFormatted: string
  keychain: 'external' | 'internal'
  
  // Metadata
  label?: string
  timestamp?: Date
  age: string       // "2 days ago"
  confirmations: number
  
  // Transaction context
  parentTx: Transaction
  isChange: boolean
}

UTXO Management Features

Labeling UTXOs

Attach labels to track UTXO origins:
// Set UTXO label
setUtxoLabel(accountId, txid, vout, "Salary - January")

// Label inheritance
// UTXO labels inherit to:
// 1. Parent transaction (if unlabeled)
// 2. Source address (if unlabeled)
See Labeling for comprehensive label management.

Freezing UTXOs

Prevent specific UTXOs from being spent:
// Mark UTXO as unspendable
const label: Label = {
  type: 'output',
  ref: `${txid}:${vout}`,
  label: "Do Not Spend - Emergency Fund",
  spendable: false  // Marks as frozen
}
Use Cases:
  • Emergency reserves
  • Tainted coins (pending investigation)
  • Long-term holds
  • Compliance holds

UTXO Consolidation

Combine many small UTXOs during low fee periods: Benefits:
  • Reduce future transaction fees
  • Simplify UTXO set
  • Prepare for efficient spending
Optimal Timing:
  • Weekend low-fee periods
  • 1-2 sat/vB fee rates
  • When UTXO count > 50
// Consolidation example
const smallUtxos = utxos.filter(u => u.value < 10000) // < 10k sats
const consolidationTx = {
  inputs: smallUtxos,
  outputs: [{
    address: selfAddress,  // Send to own address
    amount: totalValue - fee
  }],
  feeRate: 1  // Minimal fee rate
}

UTXO Privacy Considerations

Address Reuse

Problem: Using same address creates trackable transaction graph Solution: SatSigner generates new addresses automatically
// BIP44 address derivation
// External: m/84'/0'/0'/0/0, 0/1, 0/2...
// Internal: m/84'/0'/0'/1/0, 1/1, 1/2...

UTXO Correlation

Common heuristics observers use:
  1. Common Input Ownership - Inputs likely from same wallet
  2. Change Detection - Identify change vs payment outputs
  3. Round Numbers - Human-entered vs. change amounts
  4. Timing Analysis - Related transactions in time
Mitigation Strategies:
  • Use CoinJoin or STONEWALL
  • Avoid combining UTXOs from different sources
  • Use multiple change outputs
  • Add random timing delays
  • Vary fee rates

Dust Limits

Minimize dust creation:
const DUST_THRESHOLD = 546 // satoshis

// Avoid creating dust outputs
if (change < DUST_THRESHOLD) {
  // Add to miner fee instead
  fee += change
  change = 0
}
Why Dust Matters:
  • Uneconomical to spend (costs more in fees)
  • Clutters UTXO set
  • Can reveal wallet software
  • May be considered spam

Advanced UTXO Features

UTXO Age Visualization

Track UTXO age for:
  • Privacy analysis (older = more separate)
  • Tax lot identification (FIFO/LIFO)
  • Coin maturity verification
function getUtxoAge(utxo: Utxo): number {
  if (!utxo.timestamp) return 0
  return Date.now() - utxo.timestamp.getTime()
}

// Visual indicators
const ageCategories = {
  fresh: age < 24 * 60 * 60 * 1000,      // < 1 day
  recent: age < 7 * 24 * 60 * 60 * 1000, // < 1 week
  mature: age < 30 * 24 * 60 * 60 * 1000, // < 1 month
  aged: age >= 30 * 24 * 60 * 60 * 1000   // >= 1 month
}

UTXO Size Meter

Visual representation of UTXO size distribution:
// Size categories
const sizeRanges = [
  { min: 0, max: 10000, label: "< 10k sats", color: "red" },
  { min: 10000, max: 100000, label: "10k-100k", color: "orange" },
  { min: 100000, max: 1000000, label: "100k-1M", color: "yellow" },
  { min: 1000000, max: Infinity, label: "> 1M", color: "green" }
]
Helps identify:
  • Dust accumulation
  • Consolidation opportunities
  • Spending capacity at different fee rates

Performance Optimization

Efficient UTXO Loading

For wallets with many UTXOs:
// Virtual scrolling for UTXO list
const UTXOS_PER_PAGE = 50

// Lazy load UTXOs
const visibleUtxos = utxos.slice(
  page * UTXOS_PER_PAGE,
  (page + 1) * UTXOS_PER_PAGE
)

// Memoize bubble chart data
const packedUtxos = useMemo(
  () => packUtxos(utxos, canvasSize),
  [utxos, canvasSize]
)

Caching Strategies

// Cache UTXO calculations
const cachedUtxoData = {
  totalValue: utxos.reduce((sum, u) => sum + u.value, 0),
  count: utxos.length,
  byKeychain: groupBy(utxos, 'keychain'),
  byLabel: groupBy(utxos, 'label'),
  averageValue: totalValue / count
}

Troubleshooting

UTXOs Not Appearing

Possible causes:
  • Insufficient confirmations
  • Gap limit exceeded
  • Wrong network
  • Sync incomplete
Solutions:
  • Wait for confirmations
  • Force resync
  • Verify network settings
  • Check derivation path

Cannot Select UTXO

Possible causes:
  • UTXO marked unspendable
  • Insufficient value for fee
  • Already selected
  • Mempool conflict
Solutions:
  • Check spendable flag
  • Increase fee budget
  • Verify selection state
  • Wait for confirmation

Bubble Chart Performance

For 1000+ UTXOs:
  • Enable “Show Selected Only” mode
  • Reduce animation frame rate
  • Use list view instead
  • Consider UTXO consolidation

Best Practices

  1. Regular Consolidation - During low-fee periods
  2. Label Everything - Track UTXO origins
  3. Avoid Dust - Set minimum receive amounts
  4. Privacy First - Use CoinJoin when possible
  5. Monitor Age - Separate old and new coins
  6. Freeze Carefully - Document why UTXOs frozen
  7. Review Regularly - Audit UTXO set quarterly

Integration Examples

Selecting UTXOs for Payment

// Automatic selection
const { inputs, fee, change } = selectEfficientUtxos(
  account.utxos,
  paymentAmount,
  currentFeeRate
)

// Build transaction
const tx = await buildTransaction(
  wallet,
  { inputs, outputs, fee, options: { rbf: true } },
  network
)

Privacy-Enhanced Selection

// Use STONEWALL for privacy
const { inputs, outputs, fee, privacyScore } = selectStonewallUtxos(
  account.utxos,
  paymentAmount,
  currentFeeRate,
  {
    minInputs: 4,
    maxInputs: 8,
    minOutputs: 2,
    maxOutputs: 3
  }
)

console.log(`Privacy score: ${privacyScore}/100`)
SatSigner’s UTXO management gives you complete control over your Bitcoin transactions with professional tools previously only available to power users.