bsv-sdk 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 27489947d80a203e32fad08b53cfee4edabca7df61f244dff2ca12c5fa45e261
4
- data.tar.gz: d3bd5ead19a4fa67f4111e9bd3a203a1cad3c083ad4b8e174ee114b5b7fdb4bd
3
+ metadata.gz: a76170f9d5dcdfc76945c20c541b556a02f3fb5a7214a8d6f777eb589f048461
4
+ data.tar.gz: 57176276d41d430d654682dd22aef0f93bee9e2e3c57d4fb76d435dbd8c3e705
5
5
  SHA512:
6
- metadata.gz: 5ebdd5176602d584debb675d470dc31079cb16a29bb858c806123227efe03783c7c7f77eccf38174e254b7579fae9bad71d17c9e71791c21a694653647e15ad6
7
- data.tar.gz: cedddebaec0eeb9c2fa037af4227ad7307492146c2b4dc18d3a7f4dfc7f3179c20428245424d0a8542db97bbad08831a334d7821f17fd3c05264d38289271552
6
+ metadata.gz: fb7257bda0b14c14ab55d8da3a19fdc27a8eca113cfeb857487079d4e0f042ebbaef184e81382d50aed7a9efd69dc4d2cb2d77594b6e1ae5f3f6d047c01d2ab9
7
+ data.tar.gz: 20f9e8fe53476ddd87900d1b58d00811da403703d5ae8cf6dc0f6d4a4836ab8a12e42ddce88c7323bd281e710770f4b7b6805cb842ab4f164c4b17559afa46e7
data/CHANGELOG.md CHANGED
@@ -5,6 +5,19 @@ All notable changes to the `bsv-sdk` gem are documented here.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
6
6
  and this gem adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## 0.16.0 — 2026-05-01
9
+
10
+ ### Breaking Changes
11
+ - Removed `bsv-wallet` and `bsv-wallet-postgres` gems from the monorepo (#662)
12
+ - Unified `BSV::Wallet` namespace with canonical BRC-100 interface, error reconciliation, and idiomatic ProtoWallet (#664)
13
+ - Hard-deprecated `Network::ARC` and `Network::WhatsOnChain` facades (#659)
14
+
15
+ ### Added
16
+ - `BSV::Wallet::ProtoWallet` for SDK-native cryptographic operations (signing, encryption, HMAC, key derivation) (#662)
17
+
18
+ ### Fixed
19
+ - Guard nil `status_code` in MCP error messages (#659)
20
+
8
21
  ## 0.15.0 — 2026-04-29
9
22
 
10
23
  ### Added
@@ -180,12 +180,12 @@ module BSV
180
180
  response_nonce = ::Base64.strict_encode64(::SecureRandom.random_bytes(32))
181
181
  key_id = "#{response_nonce} #{session.peer_nonce}"
182
182
 
183
- sig_result = @wallet.create_signature({
184
- data: response_payload_bin.bytes,
185
- protocol_id: Peer::AUTH_PROTOCOL,
186
- key_id: key_id,
187
- counterparty: identity_key
188
- })
183
+ sig_result = @wallet.create_signature(
184
+ data: response_payload_bin.bytes,
185
+ protocol_id: Peer::AUTH_PROTOCOL,
186
+ key_id: key_id,
187
+ counterparty: identity_key
188
+ )
189
189
 
190
190
  our_identity_key = @peer.identity_key
191
191
  sig_hex_out = sig_result[:signature].pack('C*').unpack1('H*')
@@ -166,27 +166,27 @@ module BSV
166
166
 
167
167
  # Verify the certificate's signature.
168
168
  #
169
- # Uses a fresh +'anyone'+ Client as the verifier, which matches the
169
+ # Uses a fresh +'anyone'+ ProtoWallet as the verifier, which matches the
170
170
  # TS SDK behaviour. If no signature is present, raises +ArgumentError+.
171
171
  #
172
172
  # @param verifier_wallet [#verify_signature, nil] wallet to verify with;
173
- # defaults to +BSV::Wallet::Client.new('anyone', storage: BSV::Wallet::Store::Memory.new)+
173
+ # defaults to +BSV::Wallet::ProtoWallet.new('anyone')+
174
174
  # @return [Boolean] +true+ if the signature is valid
175
175
  # @raise [ArgumentError] if the certificate has no signature
176
176
  def verify(verifier_wallet = nil)
177
177
  raise ArgumentError, 'certificate has no signature to verify' if @signature.nil? || @signature.empty?
178
178
 
179
- verifier_wallet ||= BSV::Wallet::Client.new('anyone', storage: BSV::Wallet::Store::Memory.new, allow_memory_store: true)
179
+ verifier_wallet ||= BSV::Wallet::ProtoWallet.new('anyone')
180
180
  preimage = to_binary(include_signature: false)
181
181
  sig_bytes = [@signature].pack('H*').unpack('C*')
182
182
 
183
- result = verifier_wallet.verify_signature({
184
- data: preimage.unpack('C*'),
185
- signature: sig_bytes,
186
- protocol_id: CERT_SIG_PROTOCOL,
187
- key_id: "#{@type} #{@serial_number}",
188
- counterparty: @certifier
189
- })
183
+ result = verifier_wallet.verify_signature(
184
+ data: preimage.unpack('C*'),
185
+ signature: sig_bytes,
186
+ protocol_id: CERT_SIG_PROTOCOL,
187
+ key_id: "#{@type} #{@serial_number}",
188
+ counterparty: @certifier
189
+ )
190
190
 
191
191
  result.is_a?(Hash) && result[:valid] == true
192
192
  rescue BSV::Wallet::InvalidSignatureError
@@ -203,14 +203,14 @@ module BSV
203
203
  def sign(certifier_wallet)
204
204
  raise ArgumentError, "certificate has already been signed: #{@signature}" if @signature && !@signature.empty?
205
205
 
206
- @certifier = certifier_wallet.get_public_key({ identity_key: true })[:public_key]
206
+ @certifier = certifier_wallet.get_public_key(identity_key: true)[:public_key]
207
207
 
208
208
  preimage = to_binary(include_signature: false)
209
- result = certifier_wallet.create_signature({
210
- data: preimage.unpack('C*'),
211
- protocol_id: CERT_SIG_PROTOCOL,
212
- key_id: "#{@type} #{@serial_number}"
213
- })
209
+ result = certifier_wallet.create_signature(
210
+ data: preimage.unpack('C*'),
211
+ protocol_id: CERT_SIG_PROTOCOL,
212
+ key_id: "#{@type} #{@serial_number}"
213
+ )
214
214
  @signature = result[:signature].pack('C*').unpack1('H*')
215
215
  end
216
216
 
@@ -128,7 +128,7 @@ module BSV
128
128
  privileged_reason: privileged_reason
129
129
  }.merge(Certificate.certificate_field_encryption_details(field_name))
130
130
 
131
- result = creator_wallet.encrypt(enc_args)
131
+ result = creator_wallet.encrypt(**enc_args)
132
132
  master_keyring[field_name] = Base64.strict_encode64(result[:ciphertext].pack('C*'))
133
133
  end
134
134
 
@@ -187,7 +187,7 @@ module BSV
187
187
  privileged_reason: privileged_reason
188
188
  }.merge(Certificate.certificate_field_encryption_details(field_name, serial_number))
189
189
 
190
- result = subject_wallet.encrypt(enc_args)
190
+ result = subject_wallet.encrypt(**enc_args)
191
191
  keyring[field_name] = Base64.strict_encode64(result[:ciphertext].pack('C*'))
192
192
  end
193
193
 
@@ -218,7 +218,7 @@ module BSV
218
218
  keyring = result[:master_keyring]
219
219
 
220
220
  subject_key = if subject == 'self'
221
- certifier_wallet.get_public_key({ identity_key: true })[:public_key]
221
+ certifier_wallet.get_public_key(identity_key: true)[:public_key]
222
222
  else
223
223
  subject
224
224
  end
@@ -229,7 +229,7 @@ module BSV
229
229
  "#{'00' * 32}.0"
230
230
  end
231
231
 
232
- certifier_key = certifier_wallet.get_public_key({ identity_key: true })[:public_key]
232
+ certifier_key = certifier_wallet.get_public_key(identity_key: true)[:public_key]
233
233
 
234
234
  cert = new(
235
235
  type: certificate_type,
@@ -309,7 +309,7 @@ module BSV
309
309
  privileged_reason: privileged_reason
310
310
  }.merge(Certificate.certificate_field_encryption_details(field_name))
311
311
 
312
- field_revelation_key = wallet.decrypt(dec_args)[:plaintext]
312
+ field_revelation_key = wallet.decrypt(**dec_args)[:plaintext]
313
313
 
314
314
  sym_key = BSV::Primitives::SymmetricKey.new(field_revelation_key.pack('C*'))
315
315
  decrypted_bytes = sym_key.decrypt(Base64.strict_decode64(field_value))
@@ -34,12 +34,12 @@ module BSV
34
34
  first_half = SecureRandom.random_bytes(RANDOM_BYTES)
35
35
  key_id = decode_as_utf8(first_half)
36
36
 
37
- result = wallet.create_hmac({
38
- data: first_half.bytes,
39
- protocol_id: PROTOCOL_ID,
40
- key_id: key_id,
41
- counterparty: counterparty
42
- })
37
+ result = wallet.create_hmac(
38
+ data: first_half.bytes,
39
+ protocol_id: PROTOCOL_ID,
40
+ key_id: key_id,
41
+ counterparty: counterparty
42
+ )
43
43
 
44
44
  nonce_bytes = first_half + result[:hmac].pack('C*')
45
45
  ::Base64.strict_encode64(nonce_bytes)
@@ -60,13 +60,13 @@ module BSV
60
60
  hmac_bytes = nonce_bytes.byteslice(RANDOM_BYTES, nonce_bytes.bytesize - RANDOM_BYTES)
61
61
  key_id = decode_as_utf8(first_half)
62
62
 
63
- wallet.verify_hmac({
64
- data: first_half.bytes,
65
- hmac: hmac_bytes.bytes,
66
- protocol_id: PROTOCOL_ID,
67
- key_id: key_id,
68
- counterparty: counterparty
69
- })
63
+ wallet.verify_hmac(
64
+ data: first_half.bytes,
65
+ hmac: hmac_bytes.bytes,
66
+ protocol_id: PROTOCOL_ID,
67
+ key_id: key_id,
68
+ counterparty: counterparty
69
+ )
70
70
 
71
71
  true
72
72
  rescue BSV::Wallet::InvalidHmacError
data/lib/bsv/auth/peer.rb CHANGED
@@ -180,7 +180,7 @@ module BSV
180
180
  #
181
181
  # @return [String] compressed public key hex
182
182
  def identity_key
183
- @identity_key ||= @wallet.get_public_key({ identity_key: true })[:public_key]
183
+ @identity_key ||= @wallet.get_public_key(identity_key: true)[:public_key]
184
184
  end
185
185
 
186
186
  # Checks whether we have an authenticated session with a peer.
@@ -318,12 +318,12 @@ module BSV
318
318
  key_id = key_id_for(request_nonce, session.peer_nonce)
319
319
  data = JSON.generate(certificates_to_request).encode('UTF-8').bytes
320
320
 
321
- sig_result = @wallet.create_signature({
322
- data: data,
323
- protocol_id: AUTH_PROTOCOL,
324
- key_id: key_id,
325
- counterparty: session.peer_identity_key
326
- })
321
+ sig_result = @wallet.create_signature(
322
+ data: data,
323
+ protocol_id: AUTH_PROTOCOL,
324
+ key_id: key_id,
325
+ counterparty: session.peer_identity_key
326
+ )
327
327
 
328
328
  session.last_update = current_time_ms
329
329
  @session_manager.update_session(session)
@@ -355,12 +355,12 @@ module BSV
355
355
  cert_data = certificates.map { |c| c.respond_to?(:to_h) ? c.to_h : c }
356
356
  data = JSON.generate(cert_data).encode('UTF-8').bytes
357
357
 
358
- sig_result = @wallet.create_signature({
359
- data: data,
360
- protocol_id: AUTH_PROTOCOL,
361
- key_id: key_id,
362
- counterparty: session.peer_identity_key
363
- })
358
+ sig_result = @wallet.create_signature(
359
+ data: data,
360
+ protocol_id: AUTH_PROTOCOL,
361
+ key_id: key_id,
362
+ counterparty: session.peer_identity_key
363
+ )
364
364
 
365
365
  session.last_update = current_time_ms
366
366
  @session_manager.update_session(session)
@@ -394,12 +394,12 @@ module BSV
394
394
  request_nonce = ::Base64.strict_encode64(SecureRandom.random_bytes(32))
395
395
  key_id = key_id_for(request_nonce, session.peer_nonce)
396
396
 
397
- sig_result = @wallet.create_signature({
398
- data: payload,
399
- protocol_id: AUTH_PROTOCOL,
400
- key_id: key_id,
401
- counterparty: session.peer_identity_key
402
- })
397
+ sig_result = @wallet.create_signature(
398
+ data: payload,
399
+ protocol_id: AUTH_PROTOCOL,
400
+ key_id: key_id,
401
+ counterparty: session.peer_identity_key
402
+ )
403
403
 
404
404
  session.last_update = current_time_ms
405
405
  @session_manager.update_session(session)
@@ -455,12 +455,12 @@ module BSV
455
455
  sig_data = b64_decode(their_nonce) + b64_decode(our_nonce)
456
456
  key_id = key_id_for(their_nonce, our_nonce)
457
457
 
458
- sig_result = @wallet.create_signature({
459
- data: sig_data,
460
- protocol_id: AUTH_PROTOCOL,
461
- key_id: key_id,
462
- counterparty: peer_key
463
- })
458
+ sig_result = @wallet.create_signature(
459
+ data: sig_data,
460
+ protocol_id: AUTH_PROTOCOL,
461
+ key_id: key_id,
462
+ counterparty: peer_key
463
+ )
464
464
 
465
465
  @last_interacted_peer ||= peer_key if @auto_persist_last_session
466
466
 
@@ -498,13 +498,13 @@ module BSV
498
498
  sig_data = b64_decode(our_nonce) + b64_decode(their_nonce)
499
499
  key_id = key_id_for(our_nonce, their_nonce)
500
500
 
501
- @wallet.verify_signature({
502
- data: sig_data,
503
- signature: signature,
504
- protocol_id: AUTH_PROTOCOL,
505
- key_id: key_id,
506
- counterparty: peer_key
507
- })
501
+ @wallet.verify_signature(
502
+ data: sig_data,
503
+ signature: signature,
504
+ protocol_id: AUTH_PROTOCOL,
505
+ key_id: key_id,
506
+ counterparty: peer_key
507
+ )
508
508
 
509
509
  # Authentication complete
510
510
  session.peer_nonce = their_nonce
@@ -574,13 +574,13 @@ module BSV
574
574
  # Verify signature: signed over payload with key_id "msg_nonce session_nonce"
575
575
  key_id = key_id_for(msg_nonce, session.session_nonce)
576
576
 
577
- @wallet.verify_signature({
578
- data: payload,
579
- signature: signature,
580
- protocol_id: AUTH_PROTOCOL,
581
- key_id: key_id,
582
- counterparty: session.peer_identity_key
583
- })
577
+ @wallet.verify_signature(
578
+ data: payload,
579
+ signature: signature,
580
+ protocol_id: AUTH_PROTOCOL,
581
+ key_id: key_id,
582
+ counterparty: session.peer_identity_key
583
+ )
584
584
 
585
585
  session.last_update = current_time_ms
586
586
  @session_manager.update_session(session)
@@ -610,13 +610,13 @@ module BSV
610
610
  data = JSON.generate(requested).encode('UTF-8').bytes
611
611
  key_id = key_id_for(msg_nonce, session.session_nonce)
612
612
 
613
- @wallet.verify_signature({
614
- data: data,
615
- signature: signature,
616
- protocol_id: AUTH_PROTOCOL,
617
- key_id: key_id,
618
- counterparty: session.peer_identity_key
619
- })
613
+ @wallet.verify_signature(
614
+ data: data,
615
+ signature: signature,
616
+ protocol_id: AUTH_PROTOCOL,
617
+ key_id: key_id,
618
+ counterparty: session.peer_identity_key
619
+ )
620
620
 
621
621
  session.last_update = current_time_ms
622
622
  @session_manager.update_session(session)
@@ -655,13 +655,13 @@ module BSV
655
655
  data = JSON.generate(certs).encode('UTF-8').bytes
656
656
  key_id = key_id_for(msg_nonce, session.session_nonce)
657
657
 
658
- @wallet.verify_signature({
659
- data: data,
660
- signature: signature,
661
- protocol_id: AUTH_PROTOCOL,
662
- key_id: key_id,
663
- counterparty: session.peer_identity_key
664
- })
658
+ @wallet.verify_signature(
659
+ data: data,
660
+ signature: signature,
661
+ protocol_id: AUTH_PROTOCOL,
662
+ key_id: key_id,
663
+ counterparty: session.peer_identity_key
664
+ )
665
665
 
666
666
  if certs.is_a?(Array) && !certs.empty?
667
667
  validation_msg = { identity_key: session.peer_identity_key, certificates: certs }
@@ -130,7 +130,7 @@ module BSV
130
130
  privileged_reason: privileged_reason
131
131
  }.merge(Certificate.certificate_field_encryption_details(field_name, @serial_number))
132
132
 
133
- field_revelation_key = verifier_wallet.decrypt(dec_args)[:plaintext]
133
+ field_revelation_key = verifier_wallet.decrypt(**dec_args)[:plaintext]
134
134
 
135
135
  sym_key = BSV::Primitives::SymmetricKey.new(field_revelation_key.pack('C*'))
136
136
  decrypted_bytes = sym_key.decrypt(Base64.strict_decode64(@fields[field_name]))
@@ -71,7 +71,7 @@ module BSV
71
71
  args[:limit] = limit unless limit.nil?
72
72
  args[:offset] = offset unless offset.nil?
73
73
 
74
- result = @wallet.discover_by_identity_key(args, originator: @originator)
74
+ result = @wallet.discover_by_identity_key(**args, originator: @originator)
75
75
  parse_certificates(result)
76
76
  end
77
77
 
@@ -89,7 +89,7 @@ module BSV
89
89
  args[:limit] = limit unless limit.nil?
90
90
  args[:offset] = offset unless offset.nil?
91
91
 
92
- result = @wallet.discover_by_attributes(args, originator: @originator)
92
+ result = @wallet.discover_by_attributes(**args, originator: @originator)
93
93
  parse_certificates(result)
94
94
  end
95
95
 
@@ -117,7 +117,7 @@ module BSV
117
117
  # Prove the certificate to the "anyone" verifier (PrivateKey(1) public key)
118
118
  anyone_pubkey = BSV::Script::PushDropTemplate::GENERATOR_PUBKEY_HEX
119
119
  prove_result = @wallet.prove_certificate(
120
- { certificate: certificate, fields_to_reveal: fields_to_reveal, verifier: anyone_pubkey },
120
+ certificate: certificate, fields_to_reveal: fields_to_reveal, verifier: anyone_pubkey,
121
121
  originator: @originator
122
122
  )
123
123
  keyring = prove_result[:keyring_for_verifier]
@@ -155,17 +155,15 @@ module BSV
155
155
 
156
156
  # Create the transaction
157
157
  create_result = @wallet.create_action(
158
- {
159
- description: 'Create a new Identity Token',
160
- outputs: [
161
- {
162
- satoshis: @options.token_amount,
163
- locking_script: locking_script.to_hex,
164
- output_description: 'Identity Token'
165
- }
166
- ],
167
- options: { randomize_outputs: false }
168
- },
158
+ description: 'Create a new Identity Token',
159
+ outputs: [
160
+ {
161
+ satoshis: @options.token_amount,
162
+ locking_script: locking_script.to_hex,
163
+ output_description: 'Identity Token'
164
+ }
165
+ ],
166
+ options: { randomize_outputs: false },
169
167
  originator: @originator
170
168
  )
171
169
 
@@ -215,18 +213,16 @@ module BSV
215
213
  # Create a spending transaction; use unlocking_script_length so the wallet
216
214
  # produces a signable transaction that can then be signed and broadcast.
217
215
  create_result = @wallet.create_action(
218
- {
219
- description: 'Spend certificate revelation token',
220
- input_beef: beef_bytes,
221
- inputs: [
222
- {
223
- input_description: 'Revelation token',
224
- outpoint: outpoint,
225
- unlocking_script_length: BSV::Script::PushDropTemplate::Unlocker::ESTIMATED_LENGTH
226
- }
227
- ],
228
- options: { randomize_outputs: false, no_send: true }
229
- },
216
+ description: 'Spend certificate revelation token',
217
+ input_beef: beef_bytes,
218
+ inputs: [
219
+ {
220
+ input_description: 'Revelation token',
221
+ outpoint: outpoint,
222
+ unlocking_script_length: BSV::Script::PushDropTemplate::Unlocker::ESTIMATED_LENGTH
223
+ }
224
+ ],
225
+ options: { randomize_outputs: false, no_send: true },
230
226
  originator: @originator
231
227
  )
232
228
 
@@ -248,11 +244,9 @@ module BSV
248
244
  unlocking_script = unlocker.sign(partial_tx, spending_input_idx)
249
245
 
250
246
  sign_result = @wallet.sign_action(
251
- {
252
- reference: signable[:reference],
253
- spends: { spending_input_idx => { unlocking_script: unlocking_script.to_hex } },
254
- options: { no_send: true }
255
- },
247
+ reference: signable[:reference],
248
+ spends: { spending_input_idx => { unlocking_script: unlocking_script.to_hex } },
249
+ options: { no_send: true },
256
250
  originator: @originator
257
251
  )
258
252
 
@@ -343,7 +337,7 @@ module BSV
343
337
  #
344
338
  # @return [Symbol] :mainnet or :testnet
345
339
  def wallet_network
346
- result = @wallet.get_network({}, originator: @originator)
340
+ result = @wallet.get_network(originator: @originator)
347
341
  net_str = result[:network] || result['network'] || 'mainnet'
348
342
  net_str.to_sym
349
343
  end
@@ -91,8 +91,16 @@ module BSV
91
91
  private_key = BSV::Primitives::PrivateKey.from_wif(wif)
92
92
  sender_address = private_key.public_key.address(network: net_sym)
93
93
 
94
- woc = BSV::Network::WhatsOnChain.new(network: net_sym)
95
- all_utxos = woc.fetch_utxos(sender_address)
94
+ woc = BSV::Network::Providers::WhatsOnChain.default(network: net_sym)
95
+ utxo_result = woc.call(:get_utxos_all, sender_address)
96
+ return Helpers.error_response("UTXO fetch failed: #{utxo_result.message}") unless utxo_result.success?
97
+
98
+ all_utxos = utxo_result.data.map do |entry|
99
+ BSV::Network::UTXO.new(
100
+ tx_hash: entry[:tx_hash], tx_pos: entry[:tx_pos],
101
+ satoshis: entry[:satoshis], height: entry[:height]
102
+ )
103
+ end
96
104
 
97
105
  return Helpers.error_response('No UTXOs found for sender address — the address may have no funds') if all_utxos.empty?
98
106
 
@@ -104,11 +112,12 @@ module BSV
104
112
  tx = build_transaction(selected, satoshis, to_address, sender_address, private_key)
105
113
 
106
114
  arc = build_arc(net_sym, server_context)
107
- broadcast_result = arc.broadcast(tx)
115
+ arc_result = arc.call(:broadcast, tx)
116
+ return Helpers.error_response("Broadcast failed: #{arc_result.message}") unless arc_result.success?
108
117
 
109
118
  result = {
110
- txid: broadcast_result.txid,
111
- tx_status: broadcast_result.tx_status,
119
+ txid: arc_result.data[:txid],
120
+ tx_status: arc_result.data[:tx_status],
112
121
  hex: tx.to_hex
113
122
  }
114
123
 
@@ -118,10 +127,6 @@ module BSV
118
127
  )
119
128
  rescue ArgumentError => e
120
129
  Helpers.error_response(e.message)
121
- rescue BSV::Network::ChainProviderError => e
122
- Helpers.error_response("UTXO fetch failed: #{e.message}")
123
- rescue BSV::Network::BroadcastError => e
124
- Helpers.error_response("Broadcast failed: #{e.message}")
125
130
  end
126
131
 
127
132
  # Select UTXOs greedily until the total meets or exceeds the target.
@@ -196,13 +201,14 @@ module BSV
196
201
  end
197
202
  private_class_method :p2pkh_lock_for
198
203
 
199
- # Build an ARC broadcaster using server config when available.
204
+ # Build an ARC protocol instance using server config when available.
200
205
  # @api private
201
206
  def self.build_arc(net_sym, server_context)
202
207
  testnet = net_sym == :testnet
203
208
  opts = {}
204
209
  opts[:api_key] = server_context[:arc_api_key] if server_context.is_a?(Hash) && server_context[:arc_api_key]
205
- BSV::Network::ARC.default(testnet: testnet, **opts)
210
+ provider = BSV::Network::Providers::GorillaPool.default(testnet: testnet, **opts)
211
+ provider.protocol_for(:broadcast)
206
212
  end
207
213
  private_class_method :build_arc
208
214
  end
@@ -59,8 +59,22 @@ module BSV
59
59
  net_sym = Helpers.resolve_network_sym(network, server_context)
60
60
  address = resolve_address(address_or_wif, net_sym)
61
61
 
62
- woc = BSV::Network::WhatsOnChain.new(network: net_sym)
63
- utxos = woc.fetch_utxos(address)
62
+ provider = BSV::Network::Providers::WhatsOnChain.default(network: net_sym)
63
+ utxo_result = provider.call(:get_utxos_all, address)
64
+
65
+ unless utxo_result.success?
66
+ code = utxo_result.metadata[:status_code]
67
+ msg = utxo_result.message
68
+ msg = "#{msg} (HTTP #{code})" if code
69
+ return Helpers.error_response(msg)
70
+ end
71
+
72
+ utxos = utxo_result.data.map do |entry|
73
+ BSV::Network::UTXO.new(
74
+ tx_hash: entry[:tx_hash], tx_pos: entry[:tx_pos],
75
+ satoshis: entry[:satoshis], height: entry[:height]
76
+ )
77
+ end
64
78
 
65
79
  balance = utxos.sum(&:satoshis)
66
80
  result = {
@@ -77,8 +91,6 @@ module BSV
77
91
  )
78
92
  rescue ArgumentError => e
79
93
  Helpers.error_response(e.message)
80
- rescue BSV::Network::ChainProviderError => e
81
- Helpers.error_response("#{e.message} (HTTP #{e.status_code})")
82
94
  end
83
95
 
84
96
  # Resolve a WIF key or address string to a P2PKH address.
@@ -49,8 +49,17 @@ module BSV
49
49
 
50
50
  def self.call(txid:, network: nil, server_context: nil)
51
51
  net_sym = Helpers.resolve_network_sym(network, server_context)
52
- woc = BSV::Network::WhatsOnChain.new(network: net_sym)
53
- tx = woc.fetch_transaction(txid)
52
+ provider = BSV::Network::Providers::WhatsOnChain.default(network: net_sym)
53
+ fetch_result = provider.call(:get_tx, txid)
54
+
55
+ unless fetch_result.success?
56
+ code = fetch_result.metadata[:status_code]
57
+ msg = fetch_result.message
58
+ msg = "#{msg} (HTTP #{code})" if code
59
+ return Helpers.error_response(msg)
60
+ end
61
+
62
+ tx = BSV::Transaction::Transaction.from_hex(fetch_result.data)
54
63
 
55
64
  result = {
56
65
  hex: tx.to_hex,
@@ -61,8 +70,6 @@ module BSV
61
70
  [::MCP::Content::Text.new(result.to_json)],
62
71
  structured_content: result
63
72
  )
64
- rescue BSV::Network::ChainProviderError => e
65
- Helpers.error_response("#{e.message} (HTTP #{e.status_code})")
66
73
  rescue ArgumentError => e
67
74
  Helpers.error_response(e.message)
68
75
  end
@@ -50,8 +50,22 @@ module BSV
50
50
 
51
51
  def self.call(address:, network: nil, server_context: nil)
52
52
  net_sym = Helpers.resolve_network_sym(network, server_context)
53
- woc = BSV::Network::WhatsOnChain.new(network: net_sym)
54
- utxos = woc.fetch_utxos(address)
53
+ provider = BSV::Network::Providers::WhatsOnChain.default(network: net_sym)
54
+ utxo_result = provider.call(:get_utxos_all, address)
55
+
56
+ unless utxo_result.success?
57
+ code = utxo_result.metadata[:status_code]
58
+ msg = utxo_result.message
59
+ msg = "#{msg} (HTTP #{code})" if code
60
+ return Helpers.error_response(msg)
61
+ end
62
+
63
+ utxos = utxo_result.data.map do |entry|
64
+ BSV::Network::UTXO.new(
65
+ tx_hash: entry[:tx_hash], tx_pos: entry[:tx_pos],
66
+ satoshis: entry[:satoshis], height: entry[:height]
67
+ )
68
+ end
55
69
 
56
70
  result = {
57
71
  address: address,
@@ -63,8 +77,6 @@ module BSV
63
77
  [::MCP::Content::Text.new(result.to_json)],
64
78
  structured_content: result
65
79
  )
66
- rescue BSV::Network::ChainProviderError => e
67
- Helpers.error_response("#{e.message} (HTTP #{e.status_code})")
68
80
  end
69
81
  end
70
82
  end