x402-bch-axios 2.1.1 → 2.2.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.
package/index.js CHANGED
@@ -164,16 +164,130 @@ export async function createPaymentHeader (
164
164
  return JSON.stringify(paymentHeader)
165
165
  }
166
166
 
167
- async function sendPayment (signer, paymentRequirements, bchServerConfig = {}) {
167
+ // Send the payment using bch.fullstack.cash. In this case, we can use bch-js to execute
168
+ // the payment in a more optimized way.
169
+ // Assumption: A UTXO that is equal to or larger than paymentAmountSats is not a SLP token UTXO.
170
+ async function sendPaymentFullstack (signer, paymentRequirements, bchServerConfig = {}) {
168
171
  try {
169
- const { apiType, bchServerURL } = bchServerConfig
172
+ const { apiType, bchServerURL, bearerToken } = bchServerConfig
173
+
174
+ // Private key in WIF format.
175
+ const wif = signer.wif
176
+ const payToAddr = paymentRequirements.payTo
177
+
170
178
  // Support both v1 (minAmountRequired) and v2 (amount) field names
171
179
  const amountRequired = paymentRequirements.amount || paymentRequirements.minAmountRequired
172
180
  const paymentAmountSats = signer.paymentAmountSats || amountRequired
173
181
 
182
+ // Get bch-js
174
183
  const bchWallet = new dependencies.BCHWallet(signer.wif, {
175
184
  interface: apiType,
176
- restURL: bchServerURL
185
+ restURL: bchServerURL,
186
+ bearerToken
187
+ })
188
+ await bchWallet.walletInfoPromise
189
+ const bchjs = bchWallet.bchjs
190
+
191
+ // Generate the bitcoincash: address from the private key.
192
+ const ecPair = bchjs.ECPair.fromWIF(wif)
193
+ const payFromAddr = bchjs.ECPair.toCashAddress(ecPair)
194
+ // console.log(`payFromAddr: ${payFromAddr}`)
195
+
196
+ // console.log('bchjs.restURL: ', bchjs.restURL)
197
+
198
+ // Get the UTXOs controlled by the private key.
199
+ let utxos = []
200
+ let utxoData
201
+ try {
202
+ utxoData = await bchjs.Electrumx.utxo(payFromAddr)
203
+ } catch (err) {
204
+ throw new Error(`Error retrieving UTXOs for address ${payFromAddr}: ${err.message}`)
205
+ }
206
+ if (utxoData.utxos && Array.isArray(utxoData.utxos)) {
207
+ utxos = utxoData.utxos
208
+ }
209
+
210
+ // Filter out UTXOs that are equal to or greater than the paymentAmountSats
211
+ utxos = utxos.filter(utxo => utxo.value >= paymentAmountSats)
212
+ // console.log(`UTXOs available for payment: ${JSON.stringify(utxos, null, 2)}`)
213
+
214
+ // Choose the first UTXO that is big enough to pay for the transaction.
215
+ const utxo = utxos[0]
216
+
217
+ // instance of transaction builder
218
+ const transactionBuilder = new bchjs.TransactionBuilder()
219
+
220
+ // Essential variables of a transaction.
221
+ const satoshisToSend = paymentAmountSats
222
+ const originalAmount = utxo.value
223
+ const vout = utxo.tx_pos
224
+ const txid = utxo.tx_hash
225
+
226
+ // add input with txid and index of vout
227
+ transactionBuilder.addInput(txid, vout)
228
+
229
+ // get byte count to calculate fee. paying 1.2 sat/byte
230
+ const byteCount = bchjs.BitcoinCash.getByteCount({ P2PKH: 1 }, { P2PKH: 2 })
231
+ // console.log(`Transaction byte count: ${byteCount}`)
232
+ const satoshisPerByte = 1.2
233
+ const txFee = Math.floor(satoshisPerByte * byteCount)
234
+ // console.log(`Transaction fee: ${txFee}`)
235
+
236
+ // amount to send back to the sending address.
237
+ // It's the original amount - 1 sat/byte for tx size
238
+ const remainder = originalAmount - satoshisToSend - txFee
239
+
240
+ if (remainder < 0) {
241
+ throw new Error('Not enough BCH to complete transaction!')
242
+ }
243
+
244
+ // add output w/ address and amount to send
245
+ transactionBuilder.addOutput(payToAddr, satoshisToSend)
246
+ transactionBuilder.addOutput(payFromAddr, remainder)
247
+
248
+ // Sign the transaction with the HD node.
249
+ let redeemScript
250
+ transactionBuilder.sign(
251
+ 0,
252
+ ecPair,
253
+ redeemScript,
254
+ transactionBuilder.hashTypes.SIGHASH_ALL,
255
+ originalAmount
256
+ )
257
+
258
+ // build tx
259
+ const tx = transactionBuilder.build()
260
+ // output rawhex
261
+ const hex = tx.toHex()
262
+ // console.log(`TX hex: ${hex}`);
263
+ console.log(' ')
264
+
265
+ // Broadcast transation to the network
266
+ const txid2 = await bchjs.RawTransactions.sendRawTransaction([hex])
267
+ // console.log(`Transaction ID: ${txid2}`)
268
+
269
+ return {
270
+ txid: txid2,
271
+ vout: 0,
272
+ satsSent: paymentAmountSats
273
+ }
274
+ } catch (err) {
275
+ console.error('Error in x402-bch-axios/sendPaymentFullstack(): ', err)
276
+ throw err
277
+ }
278
+ }
279
+
280
+ async function sendPaymentGeneric (signer, paymentRequirements, bchServerConfig = {}) {
281
+ try {
282
+ const { apiType, bchServerURL, bearerToken } = bchServerConfig
283
+ // Support both v1 (minAmountRequired) and v2 (amount) field names
284
+ const amountRequired = paymentRequirements.amount || paymentRequirements.minAmountRequired
285
+ const paymentAmountSats = signer.paymentAmountSats || amountRequired
286
+
287
+ const bchWallet = new dependencies.BCHWallet(signer.wif, {
288
+ interface: apiType,
289
+ restURL: bchServerURL,
290
+ bearerToken
177
291
  })
178
292
  // console.log(`sendPayment() - interface: ${apiType}, restURL: ${bchServerURL}, wif: ${signer.wif}, payTo: ${paymentRequirements.payTo}, paymentAmountSats: ${paymentAmountSats}`)
179
293
  console.log(`Sending ${paymentAmountSats} for x402 API payment to ${paymentRequirements.payTo}`)
@@ -219,6 +333,17 @@ async function sendPayment (signer, paymentRequirements, bchServerConfig = {}) {
219
333
  }
220
334
  }
221
335
 
336
+ // Route the payment to the appropriate function based on the BCH server URL.
337
+ async function sendPayment (signer, paymentRequirements, bchServerConfig = {}) {
338
+ const { bchServerURL } = bchServerConfig
339
+ if (bchServerURL.includes('bch.fullstack.cash')) {
340
+ // If the BCH server URL is a Fullstack server, use an optimized payment function.
341
+ return sendPaymentFullstack(signer, paymentRequirements, bchServerConfig)
342
+ } else {
343
+ return sendPaymentGeneric(signer, paymentRequirements, bchServerConfig)
344
+ }
345
+ }
346
+
222
347
  /**
223
348
  * Adds a payment interceptor to an axios instance.
224
349
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-bch-axios",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Axios wrapper for x402 payment protocol with Bitcoin Cash (BCH) support.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -525,7 +525,7 @@ describe('#index.js', () => {
525
525
  }
526
526
  }
527
527
 
528
- it('should successfully send payment and return txid, vout, and satsSent', async () => {
528
+ it('should route to sendPaymentGeneric when URL is not fullstack', async () => {
529
529
  const signer = createSignerStub()
530
530
  const paymentRequirements = createPaymentRequirementsStub()
531
531
  const bchServerConfig = {
@@ -562,7 +562,8 @@ describe('#index.js', () => {
562
562
  assert.deepEqual(BCHWalletStub.firstCall.args[0], 'test-wif')
563
563
  assert.deepEqual(BCHWalletStub.firstCall.args[1], {
564
564
  interface: 'rest-api',
565
- restURL: 'https://api.example.com'
565
+ restURL: 'https://api.example.com',
566
+ bearerToken: undefined
566
567
  })
567
568
  assert.isTrue(mockBchWallet.initialize.calledOnce)
568
569
  assert.isTrue(RetryQueueStub.calledOnce)
@@ -580,10 +581,94 @@ describe('#index.js', () => {
580
581
  __resetDependencies()
581
582
  })
582
583
 
584
+ it('should route to sendPaymentFullstack when URL contains bch.fullstack.cash', async () => {
585
+ const signer = createSignerStub()
586
+ const paymentRequirements = createPaymentRequirementsStub()
587
+ const bchServerConfig = {
588
+ apiType: 'rest-api',
589
+ bchServerURL: 'https://bch.fullstack.cash/v5/'
590
+ }
591
+
592
+ const mockEcPair = { ecpair: true }
593
+ const mockUtxos = [{
594
+ tx_hash: 'utxo-txid',
595
+ tx_pos: 1,
596
+ value: 5000
597
+ }]
598
+
599
+ const mockTransactionBuilder = {
600
+ addInput: sandbox.stub(),
601
+ addOutput: sandbox.stub(),
602
+ sign: sandbox.stub(),
603
+ build: sandbox.stub().returns({
604
+ toHex: sandbox.stub().returns('raw-hex')
605
+ }),
606
+ hashTypes: {
607
+ SIGHASH_ALL: 1
608
+ }
609
+ }
610
+
611
+ const mockBchjs = {
612
+ ECPair: {
613
+ fromWIF: sandbox.stub().returns(mockEcPair),
614
+ toCashAddress: sandbox.stub().returns('bitcoincash:qptest')
615
+ },
616
+ Electrumx: {
617
+ utxo: sandbox.stub().resolves({ utxos: mockUtxos })
618
+ },
619
+ TransactionBuilder: sandbox.stub().returns(mockTransactionBuilder),
620
+ BitcoinCash: {
621
+ getByteCount: sandbox.stub().returns(250)
622
+ },
623
+ RawTransactions: {
624
+ sendRawTransaction: sandbox.stub().resolves('tx123')
625
+ }
626
+ }
627
+
628
+ const mockBchWallet = {
629
+ walletInfoPromise: Promise.resolve(),
630
+ bchjs: mockBchjs
631
+ }
632
+
633
+ const BCHWalletStub = sandbox.stub().returns(mockBchWallet)
634
+
635
+ __setDependencies({
636
+ BCHWallet: BCHWalletStub
637
+ })
638
+
639
+ const result = await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
640
+
641
+ assert.deepEqual(result, {
642
+ txid: 'tx123',
643
+ vout: 0,
644
+ satsSent: 2000
645
+ })
646
+
647
+ assert.isTrue(BCHWalletStub.calledOnce)
648
+ assert.deepEqual(BCHWalletStub.firstCall.args[0], 'test-wif')
649
+ assert.deepEqual(BCHWalletStub.firstCall.args[1], {
650
+ interface: 'rest-api',
651
+ restURL: 'https://bch.fullstack.cash/v5/',
652
+ bearerToken: undefined
653
+ })
654
+ assert.isTrue(mockBchjs.ECPair.fromWIF.calledOnce)
655
+ assert.isTrue(mockBchjs.Electrumx.utxo.calledOnce)
656
+ assert.isTrue(mockBchjs.TransactionBuilder.calledOnce)
657
+ assert.isTrue(mockTransactionBuilder.addInput.calledOnce)
658
+ assert.isTrue(mockTransactionBuilder.addOutput.calledTwice)
659
+ assert.isTrue(mockTransactionBuilder.sign.calledOnce)
660
+ assert.isTrue(mockBchjs.RawTransactions.sendRawTransaction.calledOnce)
661
+ assert.deepEqual(mockBchjs.RawTransactions.sendRawTransaction.firstCall.args[0], ['raw-hex'])
662
+
663
+ __resetDependencies()
664
+ })
665
+
583
666
  it('should throw "Insufficient balance" error when sendWithRetry returns null', async () => {
584
667
  const signer = createSignerStub()
585
668
  const paymentRequirements = createPaymentRequirementsStub()
586
- const bchServerConfig = {}
669
+ const bchServerConfig = {
670
+ bchServerURL: 'https://api.example.com'
671
+ }
587
672
 
588
673
  const mockBchWallet = {
589
674
  initialize: sandbox.stub().resolves(),
@@ -627,6 +712,9 @@ describe('#index.js', () => {
627
712
  it('should handle "Insufficient balance" error in sendWithRetry wrapper', async () => {
628
713
  const signer = createSignerStub()
629
714
  const paymentRequirements = createPaymentRequirementsStub()
715
+ const bchServerConfig = {
716
+ bchServerURL: 'https://api.example.com'
717
+ }
630
718
 
631
719
  const insufficientBalanceError = new Error('Insufficient balance')
632
720
  const mockBchWallet = {
@@ -650,7 +738,7 @@ describe('#index.js', () => {
650
738
  })
651
739
 
652
740
  try {
653
- await __internals.sendPayment(signer, paymentRequirements)
741
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
654
742
  assert.fail('Expected "Insufficient balance" error to be thrown')
655
743
  } catch (err) {
656
744
  assert.equal(err.message, 'Insufficient balance')
@@ -721,6 +809,9 @@ describe('#index.js', () => {
721
809
  const signer = createSignerStub()
722
810
  signer.paymentAmountSats = 5000
723
811
  const paymentRequirements = createPaymentRequirementsStub()
812
+ const bchServerConfig = {
813
+ bchServerURL: 'https://api.example.com'
814
+ }
724
815
 
725
816
  const mockBchWallet = {
726
817
  initialize: sandbox.stub().resolves(),
@@ -739,7 +830,7 @@ describe('#index.js', () => {
739
830
  RetryQueue: RetryQueueStub
740
831
  })
741
832
 
742
- await __internals.sendPayment(signer, paymentRequirements)
833
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
743
834
 
744
835
  const receivers = mockRetryQueue.addToQueue.firstCall.args[1]
745
836
  assert.equal(receivers[0].amountSat, 5000)
@@ -752,6 +843,9 @@ describe('#index.js', () => {
752
843
  delete signer.paymentAmountSats
753
844
  const paymentRequirements = createPaymentRequirementsStub()
754
845
  paymentRequirements.amount = '3000'
846
+ const bchServerConfig = {
847
+ bchServerURL: 'https://api.example.com'
848
+ }
755
849
 
756
850
  const mockBchWallet = {
757
851
  initialize: sandbox.stub().resolves(),
@@ -770,7 +864,7 @@ describe('#index.js', () => {
770
864
  RetryQueue: RetryQueueStub
771
865
  })
772
866
 
773
- await __internals.sendPayment(signer, paymentRequirements)
867
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
774
868
 
775
869
  const receivers = mockRetryQueue.addToQueue.firstCall.args[1]
776
870
  assert.equal(receivers[0].amountSat, '3000')
@@ -817,6 +911,9 @@ describe('#index.js', () => {
817
911
  it('should propagate errors from wallet initialization', async () => {
818
912
  const signer = createSignerStub()
819
913
  const paymentRequirements = createPaymentRequirementsStub()
914
+ const bchServerConfig = {
915
+ bchServerURL: 'https://api.example.com'
916
+ }
820
917
 
821
918
  const initError = new Error('Failed to initialize wallet')
822
919
  const mockBchWallet = {
@@ -833,7 +930,7 @@ describe('#index.js', () => {
833
930
  })
834
931
 
835
932
  try {
836
- await __internals.sendPayment(signer, paymentRequirements)
933
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
837
934
  assert.fail('Expected initialization error to be thrown')
838
935
  } catch (err) {
839
936
  assert.equal(err.message, 'Failed to initialize wallet')
@@ -844,5 +941,252 @@ describe('#index.js', () => {
844
941
 
845
942
  __resetDependencies()
846
943
  })
944
+
945
+ it('should support v1 minAmountRequired field in sendPaymentGeneric', async () => {
946
+ const signer = createSignerStub()
947
+ const paymentRequirements = createPaymentRequirementsStub()
948
+ delete paymentRequirements.amount
949
+ paymentRequirements.minAmountRequired = 2500
950
+ const bchServerConfig = {
951
+ bchServerURL: 'https://api.example.com'
952
+ }
953
+
954
+ const mockBchWallet = {
955
+ initialize: sandbox.stub().resolves(),
956
+ send: sandbox.stub().resolves('tx-v1')
957
+ }
958
+
959
+ const mockRetryQueue = {
960
+ addToQueue: sandbox.stub().resolves('tx-v1')
961
+ }
962
+
963
+ const BCHWalletStub = sandbox.stub().returns(mockBchWallet)
964
+ const RetryQueueStub = sandbox.stub().returns(mockRetryQueue)
965
+
966
+ __setDependencies({
967
+ BCHWallet: BCHWalletStub,
968
+ RetryQueue: RetryQueueStub
969
+ })
970
+
971
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
972
+
973
+ const receivers = mockRetryQueue.addToQueue.firstCall.args[1]
974
+ // Should use paymentAmountSats from signer (2000) when available, not minAmountRequired
975
+ assert.equal(receivers[0].amountSat, 2000)
976
+
977
+ __resetDependencies()
978
+ })
979
+
980
+ it('should handle UTXO retrieval errors in sendPaymentFullstack', async () => {
981
+ const signer = createSignerStub()
982
+ const paymentRequirements = createPaymentRequirementsStub()
983
+ const bchServerConfig = {
984
+ apiType: 'rest-api',
985
+ bchServerURL: 'https://bch.fullstack.cash/v5/'
986
+ }
987
+
988
+ const mockEcPair = { ecpair: true }
989
+ const mockBchjs = {
990
+ ECPair: {
991
+ fromWIF: sandbox.stub().returns(mockEcPair),
992
+ toCashAddress: sandbox.stub().returns('bitcoincash:qptest')
993
+ },
994
+ Electrumx: {
995
+ utxo: sandbox.stub().rejects(new Error('Network error'))
996
+ }
997
+ }
998
+
999
+ const mockBchWallet = {
1000
+ walletInfoPromise: Promise.resolve(),
1001
+ bchjs: mockBchjs
1002
+ }
1003
+
1004
+ const BCHWalletStub = sandbox.stub().returns(mockBchWallet)
1005
+
1006
+ __setDependencies({
1007
+ BCHWallet: BCHWalletStub
1008
+ })
1009
+
1010
+ try {
1011
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
1012
+ assert.fail('Expected error to be thrown')
1013
+ } catch (err) {
1014
+ assert.match(err.message, /Error retrieving UTXOs/)
1015
+ assert.match(err.message, /Network error/)
1016
+ }
1017
+
1018
+ __resetDependencies()
1019
+ })
1020
+
1021
+ it('should throw error when insufficient balance in sendPaymentFullstack', async () => {
1022
+ const signer = createSignerStub()
1023
+ const paymentRequirements = createPaymentRequirementsStub()
1024
+ const bchServerConfig = {
1025
+ apiType: 'rest-api',
1026
+ bchServerURL: 'https://bch.fullstack.cash/v5/'
1027
+ }
1028
+
1029
+ const mockEcPair = { ecpair: true }
1030
+ const mockUtxos = [{
1031
+ tx_hash: 'utxo-txid',
1032
+ tx_pos: 1,
1033
+ value: 1000 // Less than paymentAmountSats (2000)
1034
+ }]
1035
+
1036
+ const mockBchjs = {
1037
+ ECPair: {
1038
+ fromWIF: sandbox.stub().returns(mockEcPair),
1039
+ toCashAddress: sandbox.stub().returns('bitcoincash:qptest')
1040
+ },
1041
+ Electrumx: {
1042
+ utxo: sandbox.stub().resolves({ utxos: mockUtxos })
1043
+ }
1044
+ }
1045
+
1046
+ const mockBchWallet = {
1047
+ walletInfoPromise: Promise.resolve(),
1048
+ bchjs: mockBchjs
1049
+ }
1050
+
1051
+ const BCHWalletStub = sandbox.stub().returns(mockBchWallet)
1052
+
1053
+ __setDependencies({
1054
+ BCHWallet: BCHWalletStub
1055
+ })
1056
+
1057
+ try {
1058
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
1059
+ assert.fail('Expected error to be thrown')
1060
+ } catch (err) {
1061
+ // Should fail because utxos[0] is undefined after filtering
1062
+ assert.isDefined(err)
1063
+ }
1064
+
1065
+ __resetDependencies()
1066
+ })
1067
+
1068
+ it('should handle transaction building errors in sendPaymentFullstack', async () => {
1069
+ const signer = createSignerStub()
1070
+ const paymentRequirements = createPaymentRequirementsStub()
1071
+ const bchServerConfig = {
1072
+ apiType: 'rest-api',
1073
+ bchServerURL: 'https://bch.fullstack.cash/v5/'
1074
+ }
1075
+
1076
+ const mockEcPair = { ecpair: true }
1077
+ const mockUtxos = [{
1078
+ tx_hash: 'utxo-txid',
1079
+ tx_pos: 1,
1080
+ value: 5000
1081
+ }]
1082
+
1083
+ const mockTransactionBuilder = {
1084
+ addInput: sandbox.stub(),
1085
+ addOutput: sandbox.stub(),
1086
+ sign: sandbox.stub(),
1087
+ build: sandbox.stub().throws(new Error('Build failed')),
1088
+ hashTypes: {
1089
+ SIGHASH_ALL: 1
1090
+ }
1091
+ }
1092
+
1093
+ const mockBchjs = {
1094
+ ECPair: {
1095
+ fromWIF: sandbox.stub().returns(mockEcPair),
1096
+ toCashAddress: sandbox.stub().returns('bitcoincash:qptest')
1097
+ },
1098
+ Electrumx: {
1099
+ utxo: sandbox.stub().resolves({ utxos: mockUtxos })
1100
+ },
1101
+ TransactionBuilder: sandbox.stub().returns(mockTransactionBuilder),
1102
+ BitcoinCash: {
1103
+ getByteCount: sandbox.stub().returns(250)
1104
+ }
1105
+ }
1106
+
1107
+ const mockBchWallet = {
1108
+ walletInfoPromise: Promise.resolve(),
1109
+ bchjs: mockBchjs
1110
+ }
1111
+
1112
+ const BCHWalletStub = sandbox.stub().returns(mockBchWallet)
1113
+
1114
+ __setDependencies({
1115
+ BCHWallet: BCHWalletStub
1116
+ })
1117
+
1118
+ try {
1119
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
1120
+ assert.fail('Expected error to be thrown')
1121
+ } catch (err) {
1122
+ assert.equal(err.message, 'Build failed')
1123
+ }
1124
+
1125
+ __resetDependencies()
1126
+ })
1127
+
1128
+ it('should throw error when remainder is negative in sendPaymentFullstack', async () => {
1129
+ const signer = createSignerStub()
1130
+ signer.paymentAmountSats = 10000 // Large amount
1131
+ const paymentRequirements = createPaymentRequirementsStub()
1132
+ const bchServerConfig = {
1133
+ apiType: 'rest-api',
1134
+ bchServerURL: 'https://bch.fullstack.cash/v5/'
1135
+ }
1136
+
1137
+ const mockEcPair = { ecpair: true }
1138
+ // UTXO value must be >= paymentAmountSats (10000) to pass filter
1139
+ // But remainder = value - paymentAmountSats - txFee must be negative
1140
+ // With txFee = 1.2 * 250 = 300, we need value < 10300
1141
+ // So use value = 10200: remainder = 10200 - 10000 - 300 = -100 < 0
1142
+ const mockUtxos = [{
1143
+ tx_hash: 'utxo-txid',
1144
+ tx_pos: 1,
1145
+ value: 10200 // >= paymentAmountSats but insufficient after fee
1146
+ }]
1147
+
1148
+ const mockTransactionBuilder = {
1149
+ addInput: sandbox.stub(),
1150
+ addOutput: sandbox.stub(),
1151
+ sign: sandbox.stub(),
1152
+ hashTypes: {
1153
+ SIGHASH_ALL: 1
1154
+ }
1155
+ }
1156
+
1157
+ const mockBchjs = {
1158
+ ECPair: {
1159
+ fromWIF: sandbox.stub().returns(mockEcPair),
1160
+ toCashAddress: sandbox.stub().returns('bitcoincash:qptest')
1161
+ },
1162
+ Electrumx: {
1163
+ utxo: sandbox.stub().resolves({ utxos: mockUtxos })
1164
+ },
1165
+ TransactionBuilder: sandbox.stub().returns(mockTransactionBuilder),
1166
+ BitcoinCash: {
1167
+ getByteCount: sandbox.stub().returns(250)
1168
+ }
1169
+ }
1170
+
1171
+ const mockBchWallet = {
1172
+ walletInfoPromise: Promise.resolve(),
1173
+ bchjs: mockBchjs
1174
+ }
1175
+
1176
+ const BCHWalletStub = sandbox.stub().returns(mockBchWallet)
1177
+
1178
+ __setDependencies({
1179
+ BCHWallet: BCHWalletStub
1180
+ })
1181
+
1182
+ try {
1183
+ await __internals.sendPayment(signer, paymentRequirements, bchServerConfig)
1184
+ assert.fail('Expected error to be thrown')
1185
+ } catch (err) {
1186
+ assert.equal(err.message, 'Not enough BCH to complete transaction!')
1187
+ }
1188
+
1189
+ __resetDependencies()
1190
+ })
847
1191
  })
848
1192
  })