UTXO Chain Clients

THORChain supports several UTXO-based chains such as Bitcoin, Litecoin, Bitcoin Cash, and Dogecoin. These chains share a common implementation based on utxo/client.go, which handles block scanning, mempool observations, signing, consolidation, and solvency reporting.

Shared Architecture

UTXO chains use a shared client located at:

/bifrost/pkg/chainclients/utxo/client.go

Each chain (e.g. BTC, LTC) wraps this logic with minimal overrides:

/bifrost/pkg/chainclients/bitcoin/client.go
/bifrost/pkg/chainclients/litecoin/client.go

The shared logic handles:

  • Block and mempool scanning
  • UTXO input/output analysis
  • Re-org tracking (BlockCache)
  • Child-pays-for-parent (CPFP) support
  • Fee rate reporting (sats/byte)
  • Transaction signing with btcutil
  • Vault balance tracking and solvency reporting

Inbound Observations

Inbound UTXOs are detected by scanning new blocks and mempool data. A transaction is valid for observation if:

  • It has at least one output to an Asgard vault
  • It includes an OP_RETURN output with a valid memo

The observer parses these and pushes a TxIn to THORChain once confirmed.

See Inbound Transactions. For wallet developers, see Sending UTXO Transactions for required transaction structure, dust thresholds, OP_RETURN handling, and examples of short and long memos.

Confirmation Counting

THORChain dynamically delays finality based on transaction value and miner incentives.

See Finality & Confirmation Counting.

UTXO Specifics

  • txValue is the sum of all vault-receiving outputs
  • blockReward includes the subsidy + miner fees
  • If no coinbase tx is found, defaults to 3.125 (for BTC). Rewards for each UTXO chain are defined DefaultCoinbase().

Gas Tracking

Fee is calculated as:

Fee = total inputs - total outputs

Reported to THORChain as sats/byte based on the previous block and max of last 20.

See Gas Tracking.


Vault Address Derivation

UTXO vaults use compressed ECDSA public keys to derive P2WPKH addresses.

btcec.PublicKey.SerializeCompressed()

Reorg Handling

UTXO clients cache the last BlockCacheSize = 144 blocks. When a block reappears at an existing height, previously observed transactions are checked. If missing, they are re-orged and an ErrataTx is submitted to revert state.

See Re-orgs & Errata.

UTXO Consolidation

Bitcoin and similar chains limit unconfirmed transaction ancestry to ~20 ancestors. Additionally, signing large transactions with many inputs leads to long, resource-heavy ceremonies. While TSS signing is parallelized, this can still create bottlenecks.

To manage this:

  • Trigger: Consolidation is initiated when a vault holds more than MaxUTXOsToSpend UTXOs. This threshold is chain-specific and controlled via Mimir.
  • Frequency: Evaluated once per block, but only if no consolidation is already pending.
  • Process: Bifrost generates a consolidation transaction that sends funds from the vault back to itself with memo consolidate.

THORChain validates these transactions using the MsgConsolidate handler, ensuring:

  • Inputs belong to the vault
  • Output address matches the vault
  • Memo is correct

If validation fails, the vault is slashed for the attempted consolidation amount.

Consolidation is initiated by Bifrost but validated by THORChain. This logic ensures inputs are managed proactively to prevent large, unmanageable signing payloads.

RBF Handling

Replace-by-fee (RBF) is supported for user-initiated deposits to Asgard vaults. This allows users to bump the fee on a stuck transaction by rebroadcasting it with a higher fee. Vault-generated transactions do not use RBF. Instead, fee bumping (if required) is handled via other mechanisms such as child-pays-for-parent (CPFP).