Class: BSV::Wallet::CoinSelector

Inherits:
Object
  • Object
show all
Defined in:
lib/bsv/wallet_interface/coin_selector.rb

Overview

Selects UTXOs from an available pool to fund a transaction.

Given a target amount and a fee model, the selector chooses the minimum set of UTXOs needed to cover ‘target + fee`. It supports two strategies:

  • ‘:standard` — tries an exact match, then smallest-sufficient, then falls back to largest-first accumulation.

  • ‘:largest_first` — skips exact/smallest checks and goes straight to largest-first accumulation.

Fee estimation uses FeeEstimator with P2PKH size constants. During accumulation a potential change output is included in the fee estimate, so the caller can always produce change if needed.

Examples:

estimator = BSV::Wallet::FeeEstimator.new(sats_per_kb: 1)
selector  = BSV::Wallet::CoinSelector.new(fee_estimator: estimator)
result    = selector.select(available: utxos, target_satoshis: 1000, num_outputs: 1)
# => { inputs: [...], fee: 1, total_satoshis: 1001, excess: 0 }

Constant Summary collapse

VALID_STRATEGIES =
%i[standard largest_first].freeze

Instance Method Summary collapse

Constructor Details

#initialize(fee_estimator: nil, fee_model: nil, strategy: :standard) ⇒ CoinSelector

Returns a new instance of CoinSelector.

Parameters:

  • fee_estimator (BSV::Wallet::FeeEstimator) (defaults to: nil)

    fee estimation model (also accepted as fee_model: for backwards compatibility)

  • strategy (Symbol) (defaults to: :standard)

    selection strategy — ‘:standard` or `:largest_first`

Raises:

  • (ArgumentError)

    if strategy is not recognised



31
32
33
34
35
36
37
# File 'lib/bsv/wallet_interface/coin_selector.rb', line 31

def initialize(fee_estimator: nil, fee_model: nil, strategy: :standard)
  raise ArgumentError, "unknown strategy #{strategy.inspect}; use :standard or :largest_first" unless VALID_STRATEGIES.include?(strategy)
  raise ArgumentError, 'provide fee_estimator: or fee_model:, not both' if fee_estimator && fee_model

  @fee_model = fee_estimator || fee_model || raise(ArgumentError, 'fee_estimator: (or fee_model:) is required')
  @strategy  = strategy
end

Instance Method Details

#select(available:, target_satoshis:, num_outputs:) ⇒ Hash

Selects UTXOs to cover target_satoshis plus estimated fees.

Parameters:

  • available (Array<Hash>)

    pool of UTXOs; each hash must have a :satoshis key

  • target_satoshis (Integer)

    amount to fund (excluding fee)

  • num_outputs (Integer)

    number of recipient outputs in the transaction

Returns:

  • (Hash)

    with keys :inputs, :fee, :total_satoshis, :excess

Raises:

  • (BSV::Wallet::InsufficientFundsError)

    when the pool cannot cover target + fees



46
47
48
49
50
51
52
53
54
55
# File 'lib/bsv/wallet_interface/coin_selector.rb', line 46

def select(available:, target_satoshis:, num_outputs:)
  raise InsufficientFundsError.new(available: 0, required: target_satoshis) if available.empty?

  result = case @strategy
           when :standard then standard_select(available, target_satoshis, num_outputs)
           when :largest_first then accumulate(available, target_satoshis, num_outputs)
           end

  result || raise_insufficient(available, target_satoshis)
end