UTXO Coin Selection in Production
On account-based chains a balance is a number. On Bitcoin it is a set of unspent outputs, and every withdrawal is a small optimization problem: which coins do you spend, what does it cost, what does it leak, and what mess does it leave for the next withdrawal. Coin selection is where those trade-offs stop being theoretical.
The problem nobody states out loud
A withdrawal of 0.5 BTC is not "subtract 0.5 from balance". It is: pick a subset of your UTXOs summing to at least 0.5 plus fees, where the fee depends on how many inputs you picked (each input adds bytes, each byte costs the current feerate), produce a change output for the remainder, and accept that your choice just changed the shape of your wallet for every future withdrawal. Greedy answers to this create three distinct production problems: fees, dust, and privacy.
Fees: inputs are the expensive part
- Every input costs bytes; bytes cost money. A withdrawal funded by fifty small deposits can cost multiples of one funded by a single large UTXO. At high feerates, small UTXOs become economically unspendable: the fee to consume them exceeds their value.
- Exact-match selection avoids change. Branch-and-bound style selection (popularized by Bitcoin Core) looks for input sets that hit the target without a change output. No change means fewer bytes, lower fees and one less output to track. Worth having in the toolbox, worth not obsessing over: with a busy wallet the hit rate is modest.
- Consolidate when fees are low. Fragmentation is deposit-driven: every client deposit adds a UTXO. The wallets that stay cheap to operate sweep small outputs into larger ones during low-fee windows, as deliberate scheduled maintenance. The wallets that skip this discover the problem during a fee spike, which is precisely when consolidation is unaffordable and withdrawals are most expensive.
Dust: small enough to ignore, sharp enough to cut
Dust is any output too small to be worth spending. It arrives two ways: as change you created yourself, and as deposits you did not ask for. Both need policy:
- Your own change: if the would-be change output falls below the dust threshold, fold it into the fee. A satoshi-perfect ledger entry explains the difference; a dust output just becomes a liability with a maintenance cost.
- Dust you receive: dusting is also a tracking technique. An attacker sends tiny amounts to many addresses and watches which ones get spent together, clustering wallets. A custody wallet needs an explicit stance: quarantine dust by default, exclude it from selection, consolidate it deliberately (if at all) through a path that accepts the linkage.
Privacy: every selection is a statement
Spending UTXOs together links them publicly and forever: the common-input-ownership heuristic is the bread and butter of chain analytics. For an institution this is not about hiding, it is about not broadcasting your entire operational graph:
- Change goes to a fresh address, always. HD derivation exists for this. Address reuse converts your transaction history into a public dashboard of your flows.
- Consolidation is the loudest thing you do. Merging hundreds of UTXOs into one output publicly stamps them with common ownership. Fine, if it is a decision; expensive, if it is an accident.
The part tutorials skip: concurrency
Real custody systems process withdrawals in parallel, and a UTXO can fund exactly one transaction. Without reservation, two concurrent withdrawals select overlapping inputs and one becomes an accidental double-spend attempt: rejected by the network, flagged by monitoring, alarming in an audit trail even when harmless.
Two operational corollaries. First, reserve fee-bumping capacity: if every confirmed UTXO is committed to pending withdrawals, there is nothing left to CPFP a stuck transaction with, exactly when it is needed. Second, batching (one transaction, many withdrawal outputs) is a huge fee win, but it couples customers: one stuck batch is forty stuck withdrawals with one shared fee-bump decision. Batch, but with a bounded blast radius.
Production rules that held up
- Balance is a set. Report spendable balance from selectable UTXOs, not from the raw sum; the difference (dust, unconfirmed, reserved) is real.
- Selection is fee-aware: it knows the feerate before choosing, not after.
- Change below dust threshold becomes fee, with a ledger entry explaining it.
- Fresh change address every time, from HD derivation.
- Incoming dust is quarantined, excluded from selection by default.
- Consolidation is scheduled maintenance in low-fee windows, with the privacy linkage accepted explicitly.
- UTXO reservation with holds, releases and crash recovery. Tested, not assumed.
- Keep spare UTXOs for CPFP; never commit the whole wallet to in-flight spends.
- Batch withdrawals with a bounded blast radius.
Coin selection looks like an algorithm question. In production it is an inventory management problem, a cost problem, a privacy problem and a concurrency problem wearing the same trench coat. Design for all four.