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:
- Filter economical UTXOs - Remove those costing more to spend than worth
- Try exact match - Find single UTXO close to target + fee
- Branch and bound - Optimal selection minimizing waste
- 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:
- Calculate effective value (value - cost to spend)
- Recursively try including/excluding each UTXO
- Track best solution with minimal waste
- 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
| Algorithm | Use Case | Privacy | Fee Efficiency |
|---|
| Efficient | Standard payments | Medium | High |
| Branch & Bound | Small transactions | Medium | Highest |
| STONEWALL | Privacy-conscious | High | Lower |
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:
- Common Input Ownership - Inputs likely from same wallet
- Change Detection - Identify change vs payment outputs
- Round Numbers - Human-entered vs. change amounts
- 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
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
For 1000+ UTXOs:
- Enable “Show Selected Only” mode
- Reduce animation frame rate
- Use list view instead
- Consider UTXO consolidation
Best Practices
- Regular Consolidation - During low-fee periods
- Label Everything - Track UTXO origins
- Avoid Dust - Set minimum receive amounts
- Privacy First - Use CoinJoin when possible
- Monitor Age - Separate old and new coins
- Freeze Carefully - Document why UTXOs frozen
- 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.