Module: BSV::Wallet::Client::Transaction

Included in:
BSV::Wallet::Client
Defined in:
lib/bsv/wallet/client/brc100/transaction.rb

Overview

Transaction Operations concern — the 7 BRC-100 transaction public methods and all their private helpers.

Included into BSV::Wallet::Client; methods share the instance’s scope and access all instance variables initialised in #initialize.

Constant Summary collapse

ANCESTOR_DEPTH_CAP =

Maximum ancestor depth to traverse when wiring source transactions.

64
STALE_CHECK_INTERVAL =

Rate-limits stale pending recovery to avoid O(n) scans on every auto-fund call.

30

Instance Method Summary collapse

Instance Method Details

#abort_action(args, originator: nil) ⇒ Hash

Aborts a pending signable transaction.

Parameters:

  • args (Hash)

Options Hash (args):

  • :reference (String)

    base64 reference to abort

Returns:

  • (Hash)

    { aborted: true }

Raises:



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/bsv/wallet/client/brc100/transaction.rb', line 100

def abort_action(args, originator: nil)
  return @substrate.abort_action(args, originator: originator) if @substrate

  reference = args[:reference]
  raise WalletError, 'Transaction not found for the given reference' unless @pending.key?(reference)

  pending_entry = @pending.delete(reference)
  txid = pending_entry[:tx]&.txid_hex
  @pending_by_txid.delete(txid) if txid
  rollback_pending_action(
    pending_entry[:locked_outpoints],
    pending_entry[:change_outpoints],
    txid,
    reference
  )
  { aborted: true }
end

#create_action(args, originator: nil) ⇒ Hash

Creates a new Bitcoin transaction.

Parameters:

  • args (Hash)

    transaction parameters

  • _originator (String, nil)

    FQDN of the originating application

Returns:

  • (Hash)

    finalised result or signable_transaction



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/bsv/wallet/client/brc100/transaction.rb', line 30

def create_action(args, originator: nil)
  return @substrate.create_action(args, originator: originator) if @substrate

  validate_create_action!(args)
  validate_broadcast_configuration!(args)

  send_with_txids = Array(args.dig(:options, :send_with))

  outputs = args[:outputs] || []
  inputs  = args[:inputs]

  if !send_with_txids.empty? && outputs.empty? && (inputs.nil? || inputs.empty?)
    raise WalletError, 'A broadcaster is required to use send_with' unless broadcast_enabled?

    return { send_with_results: broadcast_send_with(send_with_txids) }
  end

  if (inputs.nil? || inputs.empty?) && !outputs.empty? && (args[:auto_fund] || spendable_pool_eligible?)
    result = auto_fund_and_create(args, outputs)
    unless send_with_txids.empty?
      raise WalletError, 'A broadcaster is required to use send_with' unless broadcast_enabled?

      result[:send_with_results] = broadcast_send_with(send_with_txids)
    end
    return result
  end

  beef = parse_input_beef(args[:input_beef])
  tx = build_transaction(args, beef)

  if needs_signing?(args[:inputs])
    create_signable(tx, args, beef)
  else
    finalize_action(tx, args)
  end
end

#internalize_action(args, originator: nil) ⇒ Hash

Accepts an incoming transaction for wallet internalization.

Parameters:

  • args (Hash)

Options Hash (args):

  • :tx (Array<Integer>)

    Atomic BEEF-formatted transaction as byte array

  • :outputs (Array<Hash>)

    output metadata

  • :description (String)

    5-50 char description

Returns:

  • (Hash)

    { accepted: true }

Raises:



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/bsv/wallet/client/brc100/transaction.rb', line 171

def internalize_action(args, originator: nil)
  return @substrate.internalize_action(args, originator: originator) if @substrate

  validate_internalize_action!(args)
  beef_binary = args[:tx].pack('C*')
  beef = BSV::Transaction::Beef.from_binary(beef_binary)

  raise WalletError, 'BEEF verification failed: the bundle is structurally invalid' unless beef.verify(nil)

  tx = extract_subject_transaction(beef)

  store_proofs_from_beef(beef)
  process_internalize_outputs(tx, args[:outputs])
  has_proof = !beef.find_bump(tx.txid).nil?
  store_action(tx, args, status: has_proof ? 'completed' : 'unproven')
  { accepted: true }
end

#list_actions(args, originator: nil) ⇒ Hash

Lists stored actions matching the given labels.

Parameters:

  • args (Hash)

Options Hash (args):

  • :labels (Array<String>) — default: required

    labels to filter by

Returns:

  • (Hash)

    { total_actions: Integer, actions: Array }



123
124
125
126
127
128
129
130
131
# File 'lib/bsv/wallet/client/brc100/transaction.rb', line 123

def list_actions(args, originator: nil)
  return @substrate.list_actions(args, originator: originator) if @substrate

  validate_list_actions!(args)
  query = build_action_query(args)
  total = @storage.count_actions(query)
  actions = @storage.find_actions(query)
  { total_actions: total, actions: strip_action_fields(actions, args) }
end

#list_outputs(args, originator: nil) ⇒ Hash

Lists spendable outputs in a basket.

Parameters:

  • args (Hash)

Options Hash (args):

  • :basket (String) — default: required

    basket name

Returns:

  • (Hash)

    { total_outputs: Integer, outputs: Array }



138
139
140
141
142
143
144
145
146
# File 'lib/bsv/wallet/client/brc100/transaction.rb', line 138

def list_outputs(args, originator: nil)
  return @substrate.list_outputs(args, originator: originator) if @substrate

  validate_list_outputs!(args)
  query = build_output_query(args)
  total = @storage.count_outputs(query)
  outputs = @storage.find_outputs(query)
  { total_outputs: total, outputs: strip_output_fields(outputs, args) }
end

#relinquish_output(args, originator: nil) ⇒ Hash

Removes an output from basket tracking.

Parameters:

  • args (Hash)

Options Hash (args):

  • :basket (String)

    basket name

  • :output (String)

    outpoint string

Returns:

  • (Hash)

    { relinquished: true }

Raises:



154
155
156
157
158
159
160
161
162
# File 'lib/bsv/wallet/client/brc100/transaction.rb', line 154

def relinquish_output(args, originator: nil)
  return @substrate.relinquish_output(args, originator: originator) if @substrate

  Validators.validate_basket!(args[:basket])
  Validators.validate_outpoint!(args[:output])
  raise WalletError, 'Output not found' unless @storage.delete_output(args[:output])

  { relinquished: true }
end

#sign_action(args, originator: nil) ⇒ Hash

Signs a previously created signable transaction.

Parameters:

  • args (Hash)

Options Hash (args):

  • :spends (Hash)

    map of input index to unlocking data

  • :reference (String)

    base64 reference from create_action

Returns:

  • (Hash)

    with :txid and :tx (BEEF bytes)

Raises:



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/bsv/wallet/client/brc100/transaction.rb', line 73

def sign_action(args, originator: nil)
  return @substrate.sign_action(args, originator: originator) if @substrate

  reference = args[:reference]
  pending = @pending[reference]
  raise WalletError, 'Transaction not found for the given reference' unless pending

  tx = pending[:tx]
  apply_spends(tx, args[:spends])
  @pending.delete(reference)

  merged_args = if args[:options]
                  pending[:args].merge(options: (pending[:args][:options] || {}).merge(args[:options]))
                else
                  pending[:args]
                end

  validate_broadcast_configuration!(merged_args)

  finalize_action(tx, merged_args)
end