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
truewhen broadcast is available. -
#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.
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 211 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.
507 508 509 510 511 512 513 514 515 516 517 518 519 520 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 507 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.
426 427 428 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 426 def balance(basket: nil) @storage.find_spendable_outputs(basket: basket).sum { |o| o[:satoshis].to_i } end |
#broadcast_enabled? ⇒ Boolean
Returns true when broadcast is available.
Delegates to the broadcast queue so that queue-embedded broadcasters (e.g. SolidQueueAdapter.new(broadcaster: arc)) are recognised even when no broadcaster: was passed directly to WalletClient.
102 103 104 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 102 def broadcast_enabled? @broadcast_queue.broadcast_enabled? 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.
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 161 162 163 164 165 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 126 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] # 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
690 691 692 693 694 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 690 def create_hmac(args, originator: nil) return @substrate.create_hmac(args, originator: originator) if @substrate super end |
#create_signature(args, originator: nil) ⇒ Object
702 703 704 705 706 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 702 def create_signature(args, originator: nil) return @substrate.create_signature(args, originator: originator) if @substrate super end |
#decrypt(args, originator: nil) ⇒ Object
684 685 686 687 688 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 684 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.
644 645 646 647 648 649 650 651 652 653 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 644 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.
623 624 625 626 627 628 629 630 631 632 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 623 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
678 679 680 681 682 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 678 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.
338 339 340 341 342 343 344 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 338 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.
327 328 329 330 331 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 327 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.
350 351 352 353 354 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 350 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.
660 661 662 663 664 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 660 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.
360 361 362 363 364 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 360 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.
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 298 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]) has_proof = !beef.find_bump(tx.txid).nil? store_action(tx, args, status: has_proof ? 'completed' : 'unproven') { 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.
472 473 474 475 476 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 472 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.
238 239 240 241 242 243 244 245 246 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 238 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.
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 530 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.
258 259 260 261 262 263 264 265 266 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 258 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.
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 558 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.
600 601 602 603 604 605 606 607 608 609 610 611 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 600 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.
275 276 277 278 279 280 281 282 283 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 275 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
666 667 668 669 670 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 666 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
672 673 674 675 676 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 672 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.
458 459 460 461 462 463 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 458 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.
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 175 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 # Re-validate broadcast configuration on merged args — options may have # been flipped between create_action and sign_action (e.g. no_send toggled). validate_broadcast_configuration!(merged_args) 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.
440 441 442 443 444 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 440 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.
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 404 405 406 407 408 409 410 411 412 413 414 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 379 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
696 697 698 699 700 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 696 def verify_hmac(args, originator: nil) return @substrate.verify_hmac(args, originator: originator) if @substrate super end |
#verify_signature(args, originator: nil) ⇒ Object
708 709 710 711 712 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 708 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.
483 484 485 486 487 |
# File 'lib/bsv/wallet_interface/wallet_client.rb', line 483 def wait_for_authentication(args = {}, originator: nil) return @substrate.wait_for_authentication(args, originator: originator) if @substrate { authenticated: true } end |