@psf/bch-js 4.22.1 → 5.2.2

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.
@@ -280,10 +280,214 @@ const electrumxUtxos = {
280
280
  ]
281
281
  }
282
282
 
283
+ const fulcrumUtxos01 = {
284
+ success: true,
285
+ utxos: [
286
+ {
287
+ height: 674512,
288
+ tx_hash:
289
+ 'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
290
+ tx_pos: 1,
291
+ value: 546
292
+ },
293
+ {
294
+ height: 674512,
295
+ tx_hash:
296
+ 'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
297
+ tx_pos: 2,
298
+ value: 546
299
+ },
300
+ {
301
+ height: 674512,
302
+ tx_hash:
303
+ 'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b',
304
+ tx_pos: 1,
305
+ value: 546
306
+ },
307
+ {
308
+ height: 674513,
309
+ tx_hash:
310
+ '705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
311
+ tx_pos: 1,
312
+ value: 546
313
+ },
314
+ {
315
+ height: 674513,
316
+ tx_hash:
317
+ '705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
318
+ tx_pos: 2,
319
+ value: 546
320
+ },
321
+ {
322
+ height: 674513,
323
+ tx_hash:
324
+ '705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
325
+ tx_pos: 3,
326
+ value: 38134
327
+ }
328
+ ]
329
+ }
330
+
331
+ const fulcrumUtxos02 = {
332
+ success: true,
333
+ utxos: [
334
+ {
335
+ height: 674513,
336
+ tx_hash:
337
+ '705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
338
+ tx_pos: 3,
339
+ value: 38134
340
+ }
341
+ ]
342
+ }
343
+
344
+ const psfSlpIndexerUtxos01 = {
345
+ balance: {
346
+ utxos: [
347
+ {
348
+ txid:
349
+ 'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
350
+ vout: 1,
351
+ type: 'token',
352
+ qty: '10000000000',
353
+ tokenId:
354
+ 'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
355
+ address: 'bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv'
356
+ },
357
+ {
358
+ txid:
359
+ 'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
360
+ vout: 2,
361
+ type: 'baton',
362
+ tokenId:
363
+ 'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
364
+ address: 'bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv'
365
+ }
366
+ ],
367
+ txs: [
368
+ {
369
+ txid:
370
+ 'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
371
+ height: 674512
372
+ }
373
+ ],
374
+ balances: [
375
+ {
376
+ tokenId:
377
+ 'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
378
+ qty: '10000000000'
379
+ }
380
+ ]
381
+ }
382
+ }
383
+
384
+ const tokenUtxos01 = [
385
+ {
386
+ txid: '384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
387
+ vout: 1,
388
+ type: 'token',
389
+ qty: '10000000',
390
+ tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
391
+ address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
392
+ },
393
+ {
394
+ txid: '4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
395
+ vout: 1,
396
+ type: 'token',
397
+ qty: '100000000',
398
+ tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
399
+ address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
400
+ },
401
+ {
402
+ txid: '42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
403
+ vout: 5,
404
+ type: 'token',
405
+ qty: '4764',
406
+ tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
407
+ address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
408
+ },
409
+ {
410
+ txid: '06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
411
+ vout: 5,
412
+ type: 'token',
413
+ qty: '238',
414
+ tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
415
+ address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
416
+ }
417
+ ]
418
+
419
+ const genesisData01 = {
420
+ tokenData: {
421
+ type: 1,
422
+ ticker: 'sleven',
423
+ name: 'sleven',
424
+ tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
425
+ documentUri: 'sleven',
426
+ documentHash: '',
427
+ decimals: 7,
428
+ mintBatonIsActive: false,
429
+ tokensInCirculationBN: '770059999999',
430
+ tokensInCirculationStr: '770059999999',
431
+ blockCreated: 555483,
432
+ totalBurned: '7711234568',
433
+ totalMinted: '777771234567'
434
+ }
435
+ }
436
+
437
+ const genesisData02 = {
438
+ tokenData: {
439
+ type: 1,
440
+ ticker: 'NAKAMOTO',
441
+ name: 'NAKAMOTO',
442
+ tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
443
+ documentUri: '',
444
+ documentHash: '',
445
+ decimals: 8,
446
+ mintBatonIsActive: false,
447
+ tokensInCirculationBN: '2099260968799900',
448
+ tokensInCirculationStr: '2099260968799900',
449
+ blockCreated: 555671,
450
+ totalBurned: '739031200100',
451
+ totalMinted: '2100000000000000'
452
+ }
453
+ }
454
+
455
+ const genesisData03 = {
456
+ tokenData: {
457
+ type: 1,
458
+ ticker: 'AUDC',
459
+ name: 'AUD Coin',
460
+ tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
461
+ documentUri: 'audcoino@gmail.com',
462
+ documentHash: '',
463
+ decimals: 6,
464
+ mintBatonIsActive: false,
465
+ tokensInCirculationBN: '974791786216512742',
466
+ tokensInCirculationStr: '974791786216512742',
467
+ blockCreated: 603311,
468
+ totalBurned: '1025208213783487258',
469
+ totalMinted: '2000000000000000000'
470
+ }
471
+ }
472
+
473
+ const noUtxoErr = {
474
+ success: false,
475
+ error:
476
+ 'Key not found in database [bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7]'
477
+ }
478
+
283
479
  module.exports = {
284
480
  mockUtxoData,
285
481
  mockHydratedUtxos,
286
482
  mockTwoHydratedAddrs,
287
483
  mockEveryUtxoType,
288
- electrumxUtxos
484
+ electrumxUtxos,
485
+ fulcrumUtxos01,
486
+ fulcrumUtxos02,
487
+ psfSlpIndexerUtxos01,
488
+ tokenUtxos01,
489
+ genesisData01,
490
+ genesisData02,
491
+ genesisData03,
492
+ noUtxoErr
289
493
  }
@@ -38,6 +38,7 @@ describe('#PsfSlpIndexer', () => {
38
38
  assert.include(err.message, 'test error')
39
39
  }
40
40
  })
41
+
41
42
  it('should handle request error', async () => {
42
43
  try {
43
44
  // Stub the network call.
@@ -70,6 +71,7 @@ describe('#PsfSlpIndexer', () => {
70
71
  assert.isArray(result.balance.txs)
71
72
  assert.isArray(result.balance.balances)
72
73
  })
74
+
73
75
  it('should throw an error for improper input', async () => {
74
76
  try {
75
77
  const addr = 12345
@@ -81,6 +83,7 @@ describe('#PsfSlpIndexer', () => {
81
83
  assert.include(err.message, 'Input address must be a string.')
82
84
  }
83
85
  })
86
+
84
87
  it('should handle axios error', async () => {
85
88
  try {
86
89
  // Stub the network call.
@@ -95,6 +98,7 @@ describe('#PsfSlpIndexer', () => {
95
98
  assert.include(err.message, 'test error')
96
99
  }
97
100
  })
101
+
98
102
  it('should handle request error', async () => {
99
103
  try {
100
104
  // Stub the network call.
@@ -138,6 +142,7 @@ describe('#PsfSlpIndexer', () => {
138
142
  assert.property(result.tokenData, 'totalMinted')
139
143
  assert.property(result.tokenData, 'txs')
140
144
  })
145
+
141
146
  it('should throw an error for improper input', async () => {
142
147
  try {
143
148
  const tokenId = 12345
@@ -149,6 +154,7 @@ describe('#PsfSlpIndexer', () => {
149
154
  assert.include(err.message, 'Input tokenId must be a string.')
150
155
  }
151
156
  })
157
+
152
158
  it('should handle axios error', async () => {
153
159
  try {
154
160
  // Stub the network call.
@@ -163,6 +169,7 @@ describe('#PsfSlpIndexer', () => {
163
169
  assert.include(err.message, 'test error')
164
170
  }
165
171
  })
172
+
166
173
  it('should handle request error', async () => {
167
174
  try {
168
175
  // Stub the network call.
@@ -214,6 +221,7 @@ describe('#PsfSlpIndexer', () => {
214
221
  assert.property(result.txData, 'tokenDocHash')
215
222
  assert.property(result.txData, 'isValidSlp')
216
223
  })
224
+
217
225
  it('should throw an error for improper input', async () => {
218
226
  try {
219
227
  const txid = 12345
@@ -225,6 +233,7 @@ describe('#PsfSlpIndexer', () => {
225
233
  assert.include(err.message, 'Input txid must be a string.')
226
234
  }
227
235
  })
236
+
228
237
  it('should handle axios error', async () => {
229
238
  try {
230
239
  // Stub the network call.
@@ -239,20 +248,92 @@ describe('#PsfSlpIndexer', () => {
239
248
  assert.include(err.message, 'test error')
240
249
  }
241
250
  })
242
- it('should handle request error', async () => {
243
- try {
244
- // Stub the network call.
245
- const testErr = new Error()
246
- testErr.response = { data: { status: 422 } }
247
- sandbox.stub(axios, 'post').throws(testErr)
248
251
 
249
- const txid =
250
- 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
251
- await bchjs.PsfSlpIndexer.tx(txid)
252
- assert.equal(true, false, 'Unexpected result!')
253
- } catch (err) {
254
- assert.equal(err.status, 422)
252
+ it('should get tx from full node if not available from slp indexer', async () => {
253
+ // Stub the call to the SLP indexer.
254
+ const testErr = {
255
+ response: { data: { error: 'Key not found in database' } }
255
256
  }
257
+ sandbox.stub(axios, 'post').rejects(testErr)
258
+
259
+ // Stub the call to the full node
260
+ sandbox.stub(bchjs.PsfSlpIndexer, 'checkBlacklist').resolves(false)
261
+ sandbox
262
+ .stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
263
+ .resolves({ txid: 'fakeTxid' })
264
+
265
+ const txid =
266
+ 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
267
+ const result = await bchjs.PsfSlpIndexer.tx(txid)
268
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
269
+
270
+ assert.equal(result.txData.txid, 'fakeTxid')
271
+ assert.equal(result.txData.isValidSlp, false)
272
+ })
273
+
274
+ it('should return isValidSlp=null for blacklisted token', async () => {
275
+ // Stub the call to the SLP indexer.
276
+ const testErr = {
277
+ response: { data: { error: 'Key not found in database' } }
278
+ }
279
+ sandbox.stub(axios, 'post').rejects(testErr)
280
+
281
+ // Stub the call to the full node
282
+ sandbox.stub(bchjs.PsfSlpIndexer, 'checkBlacklist').resolves(true)
283
+ sandbox
284
+ .stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
285
+ .resolves({ txid: 'fakeTxid' })
286
+
287
+ const txid =
288
+ 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
289
+ const result = await bchjs.PsfSlpIndexer.tx(txid)
290
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
291
+
292
+ assert.equal(result.txData.txid, 'fakeTxid')
293
+ assert.equal(result.txData.isValidSlp, null)
294
+ })
295
+ })
296
+
297
+ describe('#checkBlacklist', () => {
298
+ it('should return true if txid contains token in blacklist', async () => {
299
+ // Mock dependencies
300
+ sandbox
301
+ .stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
302
+ .resolves(mockData.tokenData01)
303
+
304
+ const txid =
305
+ '302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
306
+
307
+ const result = await bchjs.PsfSlpIndexer.checkBlacklist(txid)
308
+ // console.log('result: ', result)
309
+
310
+ assert.equal(result, true)
311
+ })
312
+
313
+ it('should return false if there is an error', async () => {
314
+ // Force an error
315
+ sandbox
316
+ .stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
317
+ .rejects(new Error('test error'))
318
+
319
+ const result = await bchjs.PsfSlpIndexer.checkBlacklist()
320
+
321
+ assert.equal(result, false)
322
+ })
323
+
324
+ it('should return false if there is no tokenId match', async () => {
325
+ // Mock dependencies
326
+ mockData.tokenData01.tokenId = 'abc123'
327
+ sandbox
328
+ .stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
329
+ .resolves(mockData.tokenData01)
330
+
331
+ const txid =
332
+ '302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
333
+
334
+ const result = await bchjs.PsfSlpIndexer.checkBlacklist(txid)
335
+
336
+ assert.equal(result, false)
256
337
  })
257
338
  })
258
339
  })
@@ -22,10 +22,10 @@ describe('#TransactionLib', () => {
22
22
  })
23
23
  afterEach(() => sandbox.restore())
24
24
 
25
- describe('#get', () => {
25
+ describe('#getOld', () => {
26
26
  it('should throw an error if txid is not specified', async () => {
27
27
  try {
28
- await bchjs.Transaction.get()
28
+ await bchjs.Transaction.getOld()
29
29
 
30
30
  assert.fail('Unexpected code path!')
31
31
  } catch (err) {
@@ -45,7 +45,7 @@ describe('#TransactionLib', () => {
45
45
  .stub(bchjs.Transaction.rawTransaction, 'getTxData')
46
46
  .resolves(mockData.nonSlpTxDetails)
47
47
 
48
- const result = await bchjs.Transaction.get(txid)
48
+ const result = await bchjs.Transaction.getOld(txid)
49
49
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
50
50
 
51
51
  // Assert that there are stanardized properties.
@@ -84,7 +84,7 @@ describe('#TransactionLib', () => {
84
84
  const txid =
85
85
  '266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
86
86
 
87
- const result = await bchjs.Transaction.get(txid)
87
+ const result = await bchjs.Transaction.getOld(txid)
88
88
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
89
89
 
90
90
  // Assert that there are stanardized properties.
@@ -114,7 +114,7 @@ describe('#TransactionLib', () => {
114
114
  .stub(bchjs.Transaction.rawTransaction, 'getTxData')
115
115
  .rejects(new Error('test error'))
116
116
 
117
- await bchjs.Transaction.get(txid)
117
+ await bchjs.Transaction.getOld(txid)
118
118
 
119
119
  assert.fail('Unexpected code path')
120
120
  } catch (err) {
@@ -147,7 +147,7 @@ describe('#TransactionLib', () => {
147
147
  const txid =
148
148
  '874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e'
149
149
 
150
- const result = await bchjs.Transaction.get(txid)
150
+ const result = await bchjs.Transaction.getOld(txid)
151
151
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
152
152
 
153
153
  // Assert that there are stanardized properties.
@@ -195,7 +195,7 @@ describe('#TransactionLib', () => {
195
195
  const txid =
196
196
  '4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa'
197
197
 
198
- const result = await bchjs.Transaction.get(txid)
198
+ const result = await bchjs.Transaction.getOld(txid)
199
199
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
200
200
 
201
201
  // Assert that there are stanardized properties.
@@ -246,7 +246,7 @@ describe('#TransactionLib', () => {
246
246
  const txid =
247
247
  '6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a'
248
248
 
249
- const result = await bchjs.Transaction.get(txid)
249
+ const result = await bchjs.Transaction.getOld(txid)
250
250
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
251
251
 
252
252
  // Assert that there are stanardized properties.
@@ -705,4 +705,15 @@ describe('#TransactionLib', () => {
705
705
  assert.equal(result.isSlpTx, true)
706
706
  })
707
707
  })
708
+
709
+ describe('#get', () => {
710
+ it('should proxy psf-slp-indexer', async () => {
711
+ // console.log('bchjs.Transaction: ', bchjs.Transaction)
712
+ sandbox.stub(bchjs.Transaction.psfSlpIndexer, 'tx').resolves('test data')
713
+
714
+ const result = await bchjs.Transaction.get()
715
+
716
+ assert.equal(result, 'test data')
717
+ })
718
+ })
708
719
  })
@@ -15,7 +15,7 @@ describe('#utxo', () => {
15
15
  beforeEach(() => (sandbox = sinon.createSandbox()))
16
16
  afterEach(() => sandbox.restore())
17
17
 
18
- describe('#get', () => {
18
+ describe('#getOld', () => {
19
19
  it('should get hydrated and filtered UTXOs for an address', async () => {
20
20
  // Mock dependencies.
21
21
  sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves(mockData.mockUtxoData)
@@ -25,7 +25,7 @@ describe('#utxo', () => {
25
25
 
26
26
  const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
27
27
 
28
- const result = await bchjs.Utxo.get(addr)
28
+ const result = await bchjs.Utxo.getOld(addr)
29
29
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
30
30
 
31
31
  assert.isArray(result)
@@ -46,7 +46,7 @@ describe('#utxo', () => {
46
46
 
47
47
  const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
48
48
 
49
- await bchjs.Utxo.get(addr)
49
+ await bchjs.Utxo.getOld(addr)
50
50
 
51
51
  assert.fail('Unexpected code path')
52
52
  } catch (err) {
@@ -66,7 +66,7 @@ describe('#utxo', () => {
66
66
  'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
67
67
  ]
68
68
 
69
- const result = await bchjs.Utxo.get(addr)
69
+ const result = await bchjs.Utxo.getOld(addr)
70
70
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
71
71
 
72
72
  assert.isArray(result)
@@ -88,7 +88,7 @@ describe('#utxo', () => {
88
88
  addrs.push(addr)
89
89
  }
90
90
 
91
- await bchjs.Utxo.get(addrs)
91
+ await bchjs.Utxo.getOld(addrs)
92
92
 
93
93
  assert.fail('Unexpected code path')
94
94
  } catch (err) {
@@ -105,7 +105,7 @@ describe('#utxo', () => {
105
105
 
106
106
  const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
107
107
 
108
- const result = await bchjs.Utxo.get(addr)
108
+ const result = await bchjs.Utxo.getOld(addr)
109
109
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
110
110
 
111
111
  assert.isArray(result)
@@ -133,7 +133,7 @@ describe('#utxo', () => {
133
133
  const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
134
134
  const useWhitelist = true
135
135
 
136
- const result = await bchjs.Utxo.get(addr, useWhitelist)
136
+ const result = await bchjs.Utxo.getOld(addr, useWhitelist)
137
137
  // console.log(`result: ${JSON.stringify(result, null, 2)}`)
138
138
 
139
139
  assert.isArray(result)
@@ -188,4 +188,114 @@ describe('#utxo', () => {
188
188
  assert.equal(result.satoshis, 800)
189
189
  })
190
190
  })
191
+
192
+ describe('#hydrateTokenData', () => {
193
+ it('should hydrate token UTXOs', async () => {
194
+ // Mock dependencies
195
+ sandbox
196
+ .stub(bchjs.Utxo.psfSlpIndexer, 'tokenStats')
197
+ .onCall(0)
198
+ .resolves(mockData.genesisData01)
199
+ .onCall(1)
200
+ .resolves(mockData.genesisData02)
201
+ .onCall(2)
202
+ .resolves(mockData.genesisData03)
203
+
204
+ const result = await bchjs.Utxo.hydrateTokenData(mockData.tokenUtxos01)
205
+ // console.log('result: ', result)
206
+
207
+ assert.equal(result.length, 4)
208
+ assert.property(result[0], 'qtyStr')
209
+ assert.property(result[0], 'ticker')
210
+ assert.property(result[0], 'name')
211
+ assert.property(result[0], 'documentUri')
212
+ assert.property(result[0], 'documentHash')
213
+ })
214
+
215
+ it('should should catch and throw errors', async () => {
216
+ try {
217
+ // Force error
218
+ sandbox
219
+ .stub(bchjs.Utxo.psfSlpIndexer, 'tokenStats')
220
+ .rejects(new Error('test error'))
221
+
222
+ await bchjs.Utxo.hydrateTokenData(mockData.tokenUtxos01)
223
+
224
+ assert.fail('Unexpected code path')
225
+ } catch (err) {
226
+ assert.equal(err.message, 'test error')
227
+ }
228
+ })
229
+ })
230
+
231
+ describe('#get', () => {
232
+ it('should throw an error if input is not a string', async () => {
233
+ try {
234
+ const addr = 123
235
+
236
+ await bchjs.Utxo.get(addr)
237
+
238
+ assert.fail('Unexpected code path')
239
+ } catch (err) {
240
+ assert.include(err.message, 'address input must be a string')
241
+ }
242
+ })
243
+
244
+ it('should return UTXO information', async () => {
245
+ // mock dependencies
246
+ sandbox
247
+ .stub(bchjs.Utxo.electrumx, 'utxo')
248
+ .resolves(mockData.fulcrumUtxos01)
249
+ sandbox
250
+ .stub(bchjs.Utxo.psfSlpIndexer, 'balance')
251
+ .resolves(mockData.psfSlpIndexerUtxos01)
252
+ // Mock function to return the same input. Good enough for this test.
253
+ sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x)
254
+
255
+ const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
256
+
257
+ const result = await bchjs.Utxo.get(addr)
258
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
259
+
260
+ // Assert expected properties exist
261
+ assert.property(result, 'address')
262
+ assert.property(result, 'bchUtxos')
263
+ assert.property(result, 'slpUtxos')
264
+ assert.property(result.slpUtxos, 'type1')
265
+ assert.property(result.slpUtxos.type1, 'tokens')
266
+ assert.property(result.slpUtxos.type1, 'mintBatons')
267
+ assert.property(result.slpUtxos, 'nft')
268
+ assert.property(result, 'nullUtxos')
269
+
270
+ assert.equal(result.bchUtxos.length, 4)
271
+ assert.equal(result.slpUtxos.type1.tokens.length, 1)
272
+ assert.equal(result.slpUtxos.type1.mintBatons.length, 1)
273
+ assert.equal(result.nullUtxos.length, 0)
274
+ })
275
+
276
+ it('should handle an address with no SLP UTXOs', async () => {
277
+ // mock dependencies
278
+ sandbox
279
+ .stub(bchjs.Utxo.electrumx, 'utxo')
280
+ .resolves(mockData.fulcrumUtxos02)
281
+
282
+ // Force psf-slp-indexer to return no UTXOs
283
+ sandbox
284
+ .stub(bchjs.Utxo.psfSlpIndexer, 'balance')
285
+ .rejects(mockData.noUtxoErr)
286
+
287
+ // Mock function to return the same input. Good enough for this test.
288
+ sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(() => [])
289
+
290
+ const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
291
+
292
+ const result = await bchjs.Utxo.get(addr)
293
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
294
+
295
+ assert.equal(result.bchUtxos.length, 1)
296
+ assert.equal(result.slpUtxos.type1.tokens.length, 0)
297
+ assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
298
+ assert.equal(result.nullUtxos.length, 0)
299
+ })
300
+ })
191
301
  })