Class: BSV::Wallet::WalletClient
- Inherits:
-
ProtoWallet
- Object
- ProtoWallet
- BSV::Wallet::WalletClient
- Defined in:
- lib/bsv/wallet_interface/wallet_client.rb
Overview
BRC-100 transaction operations for the wallet interface.
Implements the 7 transaction-related BRC-100 methods on top of ProtoWallet: create_action, sign_action, abort_action, list_actions, list_outputs, relinquish_output, and internalize_action.
Transactions are built using the SDK’s Transaction::Transaction class. Completed actions and tracked outputs are persisted via a StorageAdapter (defaults to MemoryStore).
Constant Summary collapse
- ANCESTOR_DEPTH_CAP =
Maximum ancestor depth to traverse when wiring source transactions. Guards against stack overflow on pathologically deep or cyclic chains.
64- STALE_CHECK_INTERVAL =
Rate-limits stale pending recovery to avoid O(n) scans on every auto-fund call. Skips if called again within this interval.
30
Instance Attribute Summary collapse
-
#broadcast_queue ⇒ BroadcastQueue
readonly
The broadcast queue used to dispatch transactions.
-
#broadcaster ⇒ #broadcast?
readonly
The optional broadcaster (responds to #broadcast(tx)).
-
#chain_provider ⇒ ChainProvider
readonly
The blockchain data provider.
-
#network ⇒ String
readonly
The network (‘mainnet’ or ‘testnet’).
-
#proof_store ⇒ ProofStore
readonly
The merkle proof persistence store.
-
#storage ⇒ StorageAdapter
readonly
The underlying persistence adapter.
-
#substrate ⇒ Interface?
readonly
The optional substrate for remote wallet delegation.
Attributes inherited from ProtoWallet
Instance Method Summary collapse
-
#abort_action(args, originator: nil) ⇒ Hash
Aborts a pending signable transaction.
-
#acquire_certificate(args, originator: nil) ⇒ Hash
Acquires an identity certificate via direct storage.
-
#balance(basket: nil) ⇒ Integer
Returns the total spendable satoshis across all baskets (or a named basket).
-
#broadcast_enabled? ⇒ Boolean
Returns true when a broadcaster has been configured.
-
#create_action(args, originator: nil) ⇒ Hash
Creates a new Bitcoin transaction.
- #create_hmac(args, originator: nil) ⇒ Object
- #create_signature(args, originator: nil) ⇒ Object
- #decrypt(args, originator: nil) ⇒ Object
-
#discover_by_attributes(args, originator: nil) ⇒ Hash
Discovers certificates matching specific attribute values.
-
#discover_by_identity_key(args, originator: nil) ⇒ Hash
Discovers certificates issued to a given identity key.
- #encrypt(args, originator: nil) ⇒ Object
-
#get_header_for_height(args, originator: nil) ⇒ Hash
Returns the block header at the given height from the chain provider.
-
#get_height(args = {}, originator: nil) ⇒ Hash
Returns the current blockchain height from the chain provider.
-
#get_network(args = {}, originator: nil) ⇒ Hash
Returns the network this wallet is configured for.
-
#get_public_key(args, originator: nil) ⇒ Object
— ProtoWallet crypto method overrides for substrate delegation —.
-
#get_version(args = {}, originator: nil) ⇒ Hash
Returns the wallet version string.
-
#initialize(key, storage: FileStore.new, network: 'mainnet', chain_provider: NullChainProvider.new, proof_store: nil, http_client: nil, fee_estimator: nil, coin_selector: nil, change_generator: nil, broadcaster: nil, broadcast_queue: nil, substrate: nil) ⇒ WalletClient
constructor
A new instance of WalletClient.
-
#internalize_action(args, originator: nil) ⇒ Hash
Accepts an incoming transaction for wallet internalization.
-
#is_authenticated(args = {}, originator: nil) ⇒ Hash
Checks whether the user is authenticated.
-
#list_actions(args, originator: nil) ⇒ Hash
Lists stored actions matching the given labels.
-
#list_certificates(args, originator: nil) ⇒ Hash
Lists identity certificates filtered by certifier and type.
-
#list_outputs(args, originator: nil) ⇒ Hash
Lists spendable outputs in a basket.
-
#prove_certificate(args, originator: nil) ⇒ Hash
Proves select fields of an identity certificate to a verifier.
-
#relinquish_certificate(args, originator: nil) ⇒ Hash
Removes a certificate from the wallet.
-
#relinquish_output(args, originator: nil) ⇒ Hash
Removes an output from basket tracking.
- #reveal_counterparty_key_linkage(args, originator: nil) ⇒ Object
- #reveal_specific_key_linkage(args, originator: nil) ⇒ Object
-
#set_wallet_change_params(count:, satoshis:) ⇒ Object
Configures the target UTXO pool parameters for change generation.
-
#sign_action(args, originator: nil) ⇒ Hash
Signs a previously created signable transaction.
-
#spendable_balance(basket: nil) ⇒ Integer
Returns the total satoshis of outputs the wallet can automatically spend.
-
#sync_utxos ⇒ Integer
Discovers on-chain UTXOs for the wallet’s identity address and imports any that are not already present in storage.
- #verify_hmac(args, originator: nil) ⇒ Object
- #verify_signature(args, originator: nil) ⇒ Object
-
#wait_for_authentication(args = {}, originator: nil) ⇒ Hash
Waits until the user is authenticated.
Constructor Details
#initialize(key, storage: FileStore.new, network: 'mainnet', chain_provider: NullChainProvider.new, proof_store: nil, http_client: nil, fee_estimator: nil, coin_selector: nil, change_generator: nil, broadcaster: nil, broadcast_queue: nil, substrate: nil) ⇒ WalletClient
Returns a new instance of WalletClient.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 64 def initialize( key, storage: FileStore.new, network: 'mainnet', chain_provider: NullChainProvider.new, proof_store: nil, http_client: nil, fee_estimator: nil, coin_selector: nil, change_generator: nil, broadcaster: nil, broadcast_queue: nil, substrate: nil ) super(key) @substrate = substrate @storage = storage @network = network @chain_provider = chain_provider @proof_store = proof_store || LocalProofStore.new(storage) @http_client = http_client @broadcaster = broadcaster @pending = {} @pending_by_txid = {} @injected_fee_estimator = fee_estimator @injected_coin_selector = coin_selector @injected_change_generator = change_generator @broadcast_queue = broadcast_queue || InlineQueue.new( storage: @storage, broadcaster: @broadcaster ) end |
Instance Attribute Details
#broadcast_queue ⇒ BroadcastQueue (readonly)
Returns the broadcast queue used to dispatch transactions.
46 47 48 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 46 def broadcast_queue @broadcast_queue end |
#broadcaster ⇒ #broadcast? (readonly)
Returns the optional broadcaster (responds to #broadcast(tx)).
43 44 45 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 43 def broadcaster @broadcaster end |
#chain_provider ⇒ ChainProvider (readonly)
Returns the blockchain data provider.
34 35 36 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 34 def chain_provider @chain_provider end |
#network ⇒ String (readonly)
Returns the network (‘mainnet’ or ‘testnet’).
37 38 39 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 37 def network @network end |
#proof_store ⇒ ProofStore (readonly)
Returns the merkle proof persistence store.
40 41 42 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 40 def proof_store @proof_store end |
#storage ⇒ StorageAdapter (readonly)
Returns the underlying persistence adapter.
31 32 33 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 31 def storage @storage end |
#substrate ⇒ Interface? (readonly)
Returns the optional substrate for remote wallet delegation.
49 50 51 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 49 def substrate @substrate end |
Instance Method Details
#abort_action(args, originator: nil) ⇒ Hash
Aborts a pending signable transaction.
If the pending entry holds UTXO references (stored by auto-fund), any outputs locked as :pending with that reference are released back to :spendable so they become eligible for future coin selection.
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 201 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 |
#acquire_certificate(args, originator: nil) ⇒ Hash
Acquires an identity certificate via direct storage.
The ‘issuance’ protocol (which requires HTTP to a certifier URL) is not yet supported and raises UnsupportedActionError.
496 497 498 499 500 501 502 503 504 505 506 507 508 509 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 496 def acquire_certificate(args, originator: nil) return @substrate.acquire_certificate(args, originator: originator) if @substrate validate_acquire_certificate!(args) cert = if args[:acquisition_protocol] == 'issuance' acquire_via_issuance(args) else acquire_via_direct(args) end @storage.store_certificate(cert) cert_without_keyring(cert) end |
#balance(basket: nil) ⇒ Integer
Returns the total spendable satoshis across all baskets (or a named basket).
Includes every output in :spendable state — regardless of whether the wallet holds the signing key. This answers “how much does this wallet hold?”, not “how much can it auto-spend?”. Use #spendable_balance for the latter.
415 416 417 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 415 def balance(basket: nil) @storage.find_spendable_outputs(basket: basket).sum { |o| o[:satoshis].to_i } end |
#broadcast_enabled? ⇒ Boolean
Returns true when a broadcaster has been configured.
98 99 100 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 98 def broadcast_enabled? !@broadcaster.nil? end |
#create_action(args, originator: nil) ⇒ Hash
Creates a new Bitcoin transaction.
When the :auto_fund option is true (and :outputs are provided but no :inputs), the wallet automatically selects UTXOs from the default basket, estimates fees, generates change, and returns a complete signed transaction (auto-fund mode).
Without :auto_fund, if all inputs carry unlocking_script values, the transaction is finalised immediately and returned with :txid and :tx (BEEF bytes). If any input specifies only unlocking_script_length, the transaction is held pending and returned as a signable_transaction for external signing via #sign_action.
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 122 def create_action(args, originator: nil) return @substrate.create_action(args, originator: originator) if @substrate validate_create_action!(args) send_with_txids = Array(args.dig(:options, :send_with)) outputs = args[:outputs] || [] inputs = args[:inputs] # When send_with is provided but no new transaction body is specified, # broadcast only the batched no_send transactions. 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) # If send_with was also specified, batch-broadcast those alongside the # current transaction's implicit broadcast. 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 |
#create_hmac(args, originator: nil) ⇒ Object
679 680 681 682 683 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 679 def create_hmac(args, originator: nil) return @substrate.create_hmac(args, originator: originator) if @substrate super end |
#create_signature(args, originator: nil) ⇒ Object
691 692 693 694 695 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 691 def create_signature(args, originator: nil) return @substrate.create_signature(args, originator: originator) if @substrate super end |
#decrypt(args, originator: nil) ⇒ Object
673 674 675 676 677 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 673 def decrypt(args, originator: nil) return @substrate.decrypt(args, originator: originator) if @substrate super end |
#discover_by_attributes(args, originator: nil) ⇒ Hash
Discovers certificates matching specific attribute values.
Searches stored certificates where field values match the given attributes. Only searches certificates belonging to this wallet.
633 634 635 636 637 638 639 640 641 642 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 633 def discover_by_attributes(args, originator: nil) return @substrate.discover_by_attributes(args, originator: originator) if @substrate raise InvalidParameterError.new('attributes', 'a non-empty Hash') unless args[:attributes].is_a?(Hash) && !args[:attributes].empty? query = { attributes: args[:attributes], limit: args[:limit] || 10, offset: args[:offset] || 0 } total = @storage.count_certificates(query) certs = @storage.find_certificates(query) { total_certificates: total, certificates: certs.map { |c| cert_without_keyring(c) } } end |
#discover_by_identity_key(args, originator: nil) ⇒ Hash
Discovers certificates issued to a given identity key.
For a local wallet, searches stored certificates where the subject matches the given identity key.
612 613 614 615 616 617 618 619 620 621 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 612 def discover_by_identity_key(args, originator: nil) return @substrate.discover_by_identity_key(args, originator: originator) if @substrate Validators.validate_pub_key_hex!(args[:identity_key], 'identity_key') query = { subject: args[:identity_key], limit: args[:limit] || 10, offset: args[:offset] || 0 } total = @storage.count_certificates(query) certs = @storage.find_certificates(query) { total_certificates: total, certificates: certs.map { |c| cert_without_keyring(c) } } end |
#encrypt(args, originator: nil) ⇒ Object
667 668 669 670 671 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 667 def encrypt(args, originator: nil) return @substrate.encrypt(args, originator: originator) if @substrate super end |
#get_header_for_height(args, originator: nil) ⇒ Hash
Returns the block header at the given height from the chain provider.
327 328 329 330 331 332 333 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 327 def get_header_for_height(args, originator: nil) return @substrate.get_header_for_height(args, originator: originator) if @substrate raise InvalidParameterError.new('height', 'a positive Integer') unless args[:height].is_a?(Integer) && args[:height].positive? { header: @chain_provider.get_header(args[:height]) } end |
#get_height(args = {}, originator: nil) ⇒ Hash
Returns the current blockchain height from the chain provider.
316 317 318 319 320 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 316 def get_height(args = {}, originator: nil) return @substrate.get_height(args, originator: originator) if @substrate { height: @chain_provider.get_height } end |
#get_network(args = {}, originator: nil) ⇒ Hash
Returns the network this wallet is configured for.
339 340 341 342 343 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 339 def get_network(args = {}, originator: nil) return @substrate.get_network(args, originator: originator) if @substrate { network: @network } end |
#get_public_key(args, originator: nil) ⇒ Object
— ProtoWallet crypto method overrides for substrate delegation —
When a substrate is configured, these methods delegate to it rather than performing local key derivation and cryptographic operations.
649 650 651 652 653 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 649 def get_public_key(args, originator: nil) return @substrate.get_public_key(args, originator: originator) if @substrate super end |
#get_version(args = {}, originator: nil) ⇒ Hash
Returns the wallet version string.
349 350 351 352 353 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 349 def get_version(args = {}, originator: nil) return @substrate.get_version(args, originator: originator) if @substrate { version: "bsv-wallet-#{BSV::Wallet::VERSION}" } end |
#internalize_action(args, originator: nil) ⇒ Hash
Accepts an incoming transaction for wallet internalization.
Parses the BEEF, locates the subject transaction, processes each specified output according to its protocol (wallet payment or basket insertion), and stores the action.
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 288 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) # F8.14: verify the BEEF bundle before trusting its contents. # Pass the chain provider if it supports SPV root verification; # otherwise fall back to structural validation via valid?. chain_tracker = @chain_provider.respond_to?(:valid_root_for_height?) ? @chain_provider : nil raise WalletError, 'BEEF verification failed: the bundle is structurally invalid' unless beef.verify(chain_tracker) tx = extract_subject_transaction(beef) store_proofs_from_beef(beef) @storage.store_transaction(tx.txid_hex, tx.to_hex) process_internalize_outputs(tx, args[:outputs]) store_action(tx, args, status: 'completed') { accepted: true } end |
#is_authenticated(args = {}, originator: nil) ⇒ Hash
Checks whether the user is authenticated. For local wallets with a private key, this is always true.
461 462 463 464 465 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 461 def is_authenticated(args = {}, originator: nil) return @substrate.is_authenticated(args, originator: originator) if @substrate { authenticated: true } end |
#list_actions(args, originator: nil) ⇒ Hash
Lists stored actions matching the given labels.
228 229 230 231 232 233 234 235 236 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 228 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_certificates(args, originator: nil) ⇒ Hash
Lists identity certificates filtered by certifier and type.
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 519 def list_certificates(args, originator: nil) return @substrate.list_certificates(args, originator: originator) if @substrate raise InvalidParameterError.new('certifiers', 'a non-empty Array') unless args[:certifiers].is_a?(Array) && !args[:certifiers].empty? raise InvalidParameterError.new('types', 'a non-empty Array') unless args[:types].is_a?(Array) && !args[:types].empty? query = { certifiers: args[:certifiers], types: args[:types], limit: args[:limit] || 10, offset: args[:offset] || 0 } total = @storage.count_certificates(query) certs = @storage.find_certificates(query) { total_certificates: total, certificates: certs.map { |c| cert_without_keyring(c) } } end |
#list_outputs(args, originator: nil) ⇒ Hash
Lists spendable outputs in a basket.
248 249 250 251 252 253 254 255 256 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 248 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 |
#prove_certificate(args, originator: nil) ⇒ Hash
Proves select fields of an identity certificate to a verifier.
Encrypts each requested field’s keyring entry for the verifier using protocol-derived encryption (BRC-2), allowing the verifier to decrypt only the revealed fields.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 547 def prove_certificate(args, originator: nil) return @substrate.prove_certificate(args, originator: originator) if @substrate cert_arg = args[:certificate] fields_to_reveal = args[:fields_to_reveal] verifier = args[:verifier] raise InvalidParameterError.new('certificate', 'a Hash') unless cert_arg.is_a?(Hash) raise InvalidParameterError.new('fields_to_reveal', 'a non-empty Array') unless fields_to_reveal.is_a?(Array) && !fields_to_reveal.empty? Validators.validate_pub_key_hex!(verifier, 'verifier') # Look up the full certificate (with keyring) from storage stored = find_stored_certificate(cert_arg) raise WalletError, 'Certificate not found in wallet' unless stored raise WalletError, 'Certificate has no keyring' unless stored[:keyring] keyring_for_verifier = {} fields_to_reveal.each do |field_name| key_value = stored[:keyring][field_name] || stored[:keyring][field_name.to_sym] raise WalletError, "Keyring entry not found for field '#{field_name}'" unless key_value # Encrypt the keyring entry for the verifier encrypted = encrypt({ plaintext: key_value.bytes, protocol_id: [2, 'certificate field encryption'], key_id: "#{cert_arg[:serial_number]} #{field_name}", counterparty: verifier }) keyring_for_verifier[field_name] = encrypted[:ciphertext] end { keyring_for_verifier: keyring_for_verifier } end |
#relinquish_certificate(args, originator: nil) ⇒ Hash
Removes a certificate from the wallet.
589 590 591 592 593 594 595 596 597 598 599 600 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 589 def relinquish_certificate(args, originator: nil) return @substrate.relinquish_certificate(args, originator: originator) if @substrate deleted = @storage.delete_certificate( type: args[:type], serial_number: args[:serial_number], certifier: args[:certifier] ) raise WalletError, 'Certificate not found' unless deleted { relinquished: true } end |
#relinquish_output(args, originator: nil) ⇒ Hash
Removes an output from basket tracking.
265 266 267 268 269 270 271 272 273 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 265 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 |
#reveal_counterparty_key_linkage(args, originator: nil) ⇒ Object
655 656 657 658 659 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 655 def reveal_counterparty_key_linkage(args, originator: nil) return @substrate.reveal_counterparty_key_linkage(args, originator: originator) if @substrate super end |
#reveal_specific_key_linkage(args, originator: nil) ⇒ Object
661 662 663 664 665 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 661 def reveal_specific_key_linkage(args, originator: nil) return @substrate.reveal_specific_key_linkage(args, originator: originator) if @substrate super end |
#set_wallet_change_params(count:, satoshis:) ⇒ Object
Configures the target UTXO pool parameters for change generation.
When set, the auto-fund path will use these parameters to decide how many change outputs to produce:
- If the pool is below +:count+, more change outputs are generated
(up to +max_outputs+) to build up the UTXO pool.
- If the pool is at or above +:count+, fewer outputs are generated
(1-2) to avoid fragmenting the pool unnecessarily.
447 448 449 450 451 452 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 447 def set_wallet_change_params(count:, satoshis:) raise InvalidParameterError.new('count', 'a positive Integer') unless count.is_a?(Integer) && count.positive? raise InvalidParameterError.new('satoshis', 'a positive Integer') unless satoshis.is_a?(Integer) && satoshis.positive? @storage.store_setting('change_params', { count: count, satoshis: satoshis }) end |
#sign_action(args, originator: nil) ⇒ Hash
Signs a previously created signable transaction.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 170 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) # Merge sign_action's own options over the original create_action args so # callers can supply accept_delayed_broadcast at sign time. merged_args = if args[:options] pending[:args].merge(options: (pending[:args][:options] || {}).merge(args[:options])) else pending[:args] end finalize_action(tx, merged_args) end |
#spendable_balance(basket: nil) ⇒ Integer
Returns the total satoshis of outputs the wallet can automatically spend.
Only outputs carrying full BRC-29 derivation metadata (derivation_prefix, derivation_suffix, sender_identity_key) are counted — these are the outputs the wallet can sign without external input. Basket-only outputs (e.g. tokens without derivation data) contribute to #balance but not here.
429 430 431 432 433 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 429 def spendable_balance(basket: nil) @storage.find_spendable_outputs(basket: basket) .select { |o| (o[:derivation_prefix] && o[:derivation_suffix] && o[:sender_identity_key]) || o[:derivation_type]&.to_s == 'identity' } .sum { |o| o[:satoshis].to_i } end |
#sync_utxos ⇒ Integer
Discovers on-chain UTXOs for the wallet’s identity address and imports any that are not already present in storage.
Each imported output is stored in the ‘default’ basket with state :spendable and derivation_type: :identity. The :identity derivation type signals to the auto-fund signing path that the root private key should be used directly (these outputs lack BRC-29 derivation metadata).
The method is idempotent — calling it twice with the same UTXO set produces no duplicates.
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 368 def sync_utxos address = identity_address utxos = @chain_provider.get_utxos(address) return 0 if utxos.empty? imported = 0 utxos.each do |utxo| outpoint = "#{utxo[:tx_hash]}.#{utxo[:tx_pos]}" next if output_exists?(outpoint) tx_hex = @chain_provider.get_transaction(utxo[:tx_hash]) tx = BSV::Transaction::Transaction.from_hex(tx_hex) pos = utxo[:tx_pos] unless pos.is_a?(Integer) && pos >= 0 && pos < tx.outputs.length raise WalletError, "Invalid tx_pos #{pos.inspect} for #{utxo[:tx_hash]} (#{tx.outputs.length} outputs)" end locking_script_hex = tx.outputs[pos].locking_script.to_hex @storage.store_output({ outpoint: outpoint, satoshis: utxo[:value], locking_script: locking_script_hex, basket: 'default', tags: [], derivation_type: :identity, state: :spendable, source_tx_hex: tx_hex }) @storage.store_transaction(utxo[:tx_hash], tx_hex) imported += 1 end imported end |
#verify_hmac(args, originator: nil) ⇒ Object
685 686 687 688 689 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 685 def verify_hmac(args, originator: nil) return @substrate.verify_hmac(args, originator: originator) if @substrate super end |
#verify_signature(args, originator: nil) ⇒ Object
697 698 699 700 701 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 697 def verify_signature(args, originator: nil) return @substrate.verify_signature(args, originator: originator) if @substrate super end |
#wait_for_authentication(args = {}, originator: nil) ⇒ Hash
Waits until the user is authenticated. For local wallets, returns immediately.
472 473 474 475 476 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 472 def wait_for_authentication(args = {}, originator: nil) return @substrate.wait_for_authentication(args, originator: originator) if @substrate { authenticated: true } end |