@psf/bch-js 5.3.2 → 6.1.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/src/slp/utils.js CHANGED
@@ -41,863 +41,6 @@ class Utils {
41
41
  this.util = new Util(config)
42
42
  }
43
43
 
44
- /**
45
- * @api SLP.Utils.list() list()
46
- * @apiName list
47
- * @apiGroup SLP Utils
48
- * @apiDescription List all tokens or list single token by id.
49
- *
50
- * @apiExample Example usage:
51
- *
52
- * // List all tokens
53
- *
54
- * (async () => {
55
- * try {
56
- * let list = await bchjs.SLP.Utils.list();
57
- * console.log(list);
58
- * } catch (error) {
59
- * console.error(error);
60
- * }
61
- * })();
62
- *
63
- * // returns
64
- * [ { decimals: 5,
65
- * timestamp: '2019-04-20 05:03',
66
- * versionType: 1,
67
- * documentUri: 'developer.bitcoin.com',
68
- * symbol: 'MYSTERY',
69
- * name: 'Mystery',
70
- * containsBaton: true,
71
- * id:
72
- * '10528f22fc20422f7c1075a87ed7270c0a17bc17ea79c6e2f426c6cc14bb25f2',
73
- * documentHash:
74
- * '1010101010101010101010101010101010101010101010101010101010101010',
75
- * initialTokenQty: 500,
76
- * blockCreated: 579041,
77
- * blockLastActiveSend: null,
78
- * blockLastActiveMint: null,
79
- * txnsSinceGenesis: 1,
80
- * validAddresses: 1,
81
- * totalMinted: 500,
82
- * totalBurned: 0,
83
- * circulatingSupply: 500,
84
- * mintingBatonStatus: 'ALIVE' },
85
- * { decimals: 8,
86
- * timestamp: '2019-04-20 04:54',
87
- * versionType: 1,
88
- * documentUri: 'developer.bitcoin.com',
89
- * symbol: 'ENIGMA',
90
- * name: 'Enigma',
91
- * containsBaton: true,
92
- * id:
93
- * '113c55921fe29919ff84e53a6d5af39ed9d983a1c3b3000f27125688489935fa',
94
- * documentHash:
95
- * '1010101010101010101010101010101010101010101010101010101010101010',
96
- * initialTokenQty: 1234,
97
- * blockCreated: 579040,
98
- * blockLastActiveSend: null,
99
- * blockLastActiveMint: 579040,
100
- * txnsSinceGenesis: 2,
101
- * validAddresses: 2,
102
- * totalMinted: 1334,
103
- * totalBurned: 0,
104
- * circulatingSupply: 1334,
105
- * mintingBatonStatus: 'ALIVE' }
106
- * ]
107
- *
108
- * // List single token
109
- *
110
- * (async () => {
111
- * try {
112
- * let list = await bchjs.SLP.Utils.list(
113
- * "b3f4f132dc3b9c8c96316346993a8d54d729715147b7b11aa6c8cd909e955313"
114
- * );
115
- * console.log(list);
116
- * } catch (error) {
117
- * console.error(error);
118
- * }
119
- * })();
120
- *
121
- * // returns
122
- * { decimals: 8,
123
- * timestamp: '2019-04-20 04:54',
124
- * versionType: 1,
125
- * documentUri: 'developer.bitcoin.com',
126
- * symbol: 'ENIGMA',
127
- * name: 'Enigma',
128
- * containsBaton: true,
129
- * id:
130
- * '113c55921fe29919ff84e53a6d5af39ed9d983a1c3b3000f27125688489935fa',
131
- * documentHash:
132
- * '1010101010101010101010101010101010101010101010101010101010101010',
133
- * initialTokenQty: 1234,
134
- * blockCreated: 579040,
135
- * blockLastActiveSend: null,
136
- * blockLastActiveMint: 579040,
137
- * txnsSinceGenesis: 2,
138
- * validAddresses: 2,
139
- * totalMinted: 1334,
140
- * totalBurned: 0,
141
- * circulatingSupply: 1334,
142
- * mintingBatonStatus: 'ALIVE' }
143
- *
144
- * // List multiple tokens by tokenIds
145
- *
146
- * (async () => {
147
- * try {
148
- * let list = await bchjs.SLP.Utils.list([
149
- * "fa6c74c52450fc164e17402a46645ce494a8a8e93b1383fa27460086931ef59f",
150
- * "38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0"
151
- * ]);
152
- * console.log(list);
153
- * } catch (error) {
154
- * console.error(error);
155
- * }
156
- * })();
157
- *
158
- * // returns
159
- * [ { decimals: 0,
160
- * timestamp: '2019-02-18 14:47',
161
- * versionType: 1,
162
- * documentUri: 'https://simpleledger.cash',
163
- * symbol: 'SLP',
164
- * name: 'Official SLP Token',
165
- * containsBaton: true,
166
- * id:
167
- * 'fa6c74c52450fc164e17402a46645ce494a8a8e93b1383fa27460086931ef59f',
168
- * documentHash: null,
169
- * initialTokenQty: 18446744073709552000,
170
- * blockCreated: 570305,
171
- * blockLastActiveSend: 580275,
172
- * blockLastActiveMint: 575914,
173
- * txnsSinceGenesis: 4537,
174
- * validAddresses: 164,
175
- * totalMinted: 19414628793626410000,
176
- * totalBurned: 18446568350267302000,
177
- * circulatingSupply: 968060443359109600,
178
- * mintingBatonStatus: 'ALIVE' },
179
- * { decimals: 8,
180
- * timestamp: '2019-02-14 03:11',
181
- * versionType: 1,
182
- * documentUri: 'psfoundation.cash',
183
- * symbol: 'PSF',
184
- * name: 'Permissionless Software Foundation',
185
- * containsBaton: true,
186
- * id:
187
- * '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
188
- * documentHash: null,
189
- * initialTokenQty: 19882.09163133,
190
- * blockCreated: 569658,
191
- * blockLastActiveSend: 580153,
192
- * blockLastActiveMint: null,
193
- * txnsSinceGenesis: 51,
194
- * validAddresses: 9,
195
- * totalMinted: 19882.09163133,
196
- * totalBurned: 0.0534241,
197
- * circulatingSupply: 19882.03820723,
198
- * mintingBatonStatus: 'ALIVE' } ]
199
- */
200
- async list (id) {
201
- let path
202
- let method
203
-
204
- if (!id) {
205
- method = 'get'
206
- path = `${this.restURL}slp/list`
207
- } else if (typeof id === 'string') {
208
- method = 'get'
209
- path = `${this.restURL}slp/list/${id}`
210
- } else if (typeof id === 'object') {
211
- method = 'post'
212
- path = `${this.restURL}slp/list`
213
- }
214
-
215
- // console.log(`path: ${path}`)
216
-
217
- try {
218
- let response
219
- if (method === 'get') {
220
- response = await _this.axios.get(path, this.axiosOptions)
221
- } else {
222
- response = await _this.axios.post(
223
- path,
224
- {
225
- tokenIds: id
226
- },
227
- this.axiosOptions
228
- )
229
- }
230
- return response.data
231
- } catch (error) {
232
- if (error.response && error.response.data) throw error.response.data
233
- throw error
234
- }
235
- }
236
-
237
- /**
238
- * @api SLP.Utils.balancesForAddress() balancesForAddress()
239
- * @apiName balancesForAddress
240
- * @apiGroup SLP Utils
241
- * @apiDescription Return all balances for an address or array of addresses.
242
- *
243
- * @apiExample Example usage:
244
- *
245
- * (async () => {
246
- * try {
247
- * let balances = await bchjs.SLP.Utils.balancesForAddress('simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk');
248
- * console.log(balances);
249
- * } catch (error) {
250
- * console.error(error);
251
- * }
252
- * })();
253
- *
254
- * // returns
255
- * // [ { tokenId:
256
- * // '968ff0cc4c93864001e03e9524e351250b94ec56150fa4897f65b0b6477d44d4',
257
- * // balance: '9980',
258
- * // slpAddress: 'simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk',
259
- * // decimalCount: 9 },
260
- * // { tokenId:
261
- * // 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
262
- * // balance: '617',
263
- * // slpAddress: 'simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk',
264
- * // decimalCount: 8 },
265
- * // { tokenId:
266
- * // 'b96304d12f1bbc2196df582516410e55a9b34e13c7b4585bf5c1770af30d034f',
267
- * // balance: '1',
268
- * // slpAddress: 'simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk',
269
- * // decimalCount: 0 },
270
- * // { tokenId:
271
- * // 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
272
- * // balance: '776',
273
- * // slpAddress: 'simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk',
274
- * // decimalCount: 7 } ]
275
- *
276
- * // balances for Cash Address
277
- * (async () => {
278
- * try {
279
- * let balances = await bchjs.SLP.Utils.balancesForAddress('bitcoincash:qr4zg7xth86yzq94gl8jvnf5z4wuupzt3g4hl47n9y');
280
- * console.log(balances);
281
- * } catch (error) {
282
- * console.error(error);
283
- * }
284
- * })();
285
- *
286
- * // returns
287
- * // [ { tokenId:
288
- * // '467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5',
289
- * // balance: '507',
290
- * // decimalCount: 2 } ]
291
- *
292
- * // balances for Legacy Address
293
- * (async () => {
294
- * try {
295
- * let balances = await bchjs.SLP.Utils.balancesForAddress('1NM2ozrXVSnMRm66ua6aGeXgMsU7yqwqLS');
296
- * console.log(balances);
297
- * } catch (error) {
298
- * console.error(error);
299
- * }
300
- * })();
301
- *
302
- * // returns
303
- * // [ { tokenId:
304
- * // '467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5',
305
- * // balance: '507',
306
- * // decimalCount: 2 } ]
307
- *
308
- * Note: Balances for multiple addresses can be retrieves by passing in an
309
- * array of addresses.
310
- */
311
- // Retrieve token balances for a given address.
312
- async balancesForAddress (address) {
313
- try {
314
- // Single address.
315
- if (typeof address === 'string') {
316
- const path = `${this.restURL}slp/balancesForAddress/${address}`
317
-
318
- const response = await _this.axios.get(path, this.axiosOptions)
319
- return response.data
320
-
321
- // Array of addresses.
322
- } else if (Array.isArray(address)) {
323
- const path = `${this.restURL}slp/balancesForAddress`
324
-
325
- // Dev note: must use axios.post for unit test stubbing.
326
- const response = await _this.axios.post(
327
- path,
328
- {
329
- addresses: address
330
- },
331
- this.axiosOptions
332
- )
333
-
334
- return response.data
335
- }
336
-
337
- throw new Error('Input address must be a string or array of strings.')
338
- } catch (error) {
339
- if (error.response && error.response.data) throw error.response.data
340
- throw error
341
- }
342
- }
343
-
344
- /**
345
- * @api SLP.Utils.balancesForToken() balancesForToken()
346
- * @apiName balancesForToken
347
- * @apiGroup SLP Utils
348
- * @apiDescription List all balances for tokenId.
349
- *
350
- * @apiExample Example usage:
351
- *
352
- * (async () => {
353
- * try {
354
- * let balances = await bchjs.SLP.Utils.balancesForToken(
355
- * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
356
- * )
357
- * console.log(balances)
358
- * } catch (error) {
359
- * console.error(error)
360
- * }
361
- * })()
362
- *
363
- * // returns
364
- * [
365
- * {
366
- * tokenId: "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb",
367
- * tokenBalance: 20,
368
- * slpAddress: 'simpleledger:qp4g0q97tq53pasnxk2rs570c6573qvylunsf5gy9e'
369
- * },
370
- * {
371
- * tokenId: "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb",
372
- * tokenBalance: 335.55,
373
- * slpAddress: 'simpleledger:qqcraw7q0ys3kg4z6f2zd267fhg2093c5c0spfk03f'
374
- * }
375
- * ]
376
- *
377
- */
378
- // Retrieve token balances for a given tokenId.
379
- async balancesForToken (tokenId) {
380
- try {
381
- const path = `${this.restURL}slp/balancesForToken/${tokenId}`
382
-
383
- const response = await _this.axios.get(path, this.axiosOptions)
384
- return response.data
385
- } catch (error) {
386
- if (error.response && error.response.data) throw error.response.data
387
- throw error
388
- }
389
- }
390
-
391
- /**
392
- * @api SLP.Utils.validateTxid() validateTxid()
393
- * @apiName validateTxid
394
- * @apiGroup SLP Utils
395
- * @apiDescription Validate that txid is an SLP transaction.
396
- *
397
- * @apiExample Example usage:
398
- *
399
- * // validate single SLP txid
400
- * (async () => {
401
- * try {
402
- * let validated = await bchjs.SLP.Utils.validateTxid(
403
- * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
404
- * );
405
- * console.log(validated);
406
- * } catch (error) {
407
- * console.error(error);
408
- * }
409
- * })();
410
- *
411
- * // returns
412
- * [ { txid:
413
- * 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
414
- * valid: true } ]
415
- *
416
- * // validate multiple SLP txids
417
- * (async () => {
418
- * try {
419
- * let validated = await bchjs.SLP.Utils.validateTxid([
420
- * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb",
421
- * "00ea27261196a411776f81029c0ebe34362936b4a9847deb1f7a40a02b3a1476"
422
- * ]);
423
- * console.log(validated);
424
- * } catch (error) {
425
- * console.error(error);
426
- * }
427
- * })();
428
- *
429
- * // returns
430
- * [ { txid:
431
- * 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
432
- * valid: true },
433
- * { txid:
434
- * '00ea27261196a411776f81029c0ebe34362936b4a9847deb1f7a40a02b3a1476',
435
- * valid: true } ]
436
- */
437
- // This function has two responses. If SLPDB is working correctly, the output
438
- // will be like the examples above. If SLPDB has fallen behind real-time
439
- // processing, it will return this output:
440
- // [ null ]
441
- async validateTxid (txid, usrObj = null) {
442
- const path = `${this.restURL}slp/validateTxid`
443
-
444
- // console.log(`txid: ${JSON.stringify(txid, null, 2)}`)
445
- // console.log(`validateTxid usrObj: ${JSON.stringify(usrObj, null, 2)}`)
446
-
447
- // Handle a single TXID or an array of TXIDs.
448
- let txids
449
- if (typeof txid === 'string') txids = [txid]
450
- else txids = txid
451
-
452
- try {
453
- // console.log('validateTxid() this.axiosOptions: ', this.axiosOptions)
454
- const response = await _this.axios.post(
455
- path,
456
- {
457
- txids: txids,
458
- usrObj // pass user data when making an internal call.
459
- },
460
- this.axiosOptions
461
- )
462
- // console.log(
463
- // `validateTxid response.data: ${JSON.stringify(response.data, null, 2)}`
464
- // )
465
-
466
- const validatedTxids = response.data
467
-
468
- return validatedTxids
469
- } catch (error) {
470
- if (error.response && error.response.data) throw error.response.data
471
- throw error
472
- }
473
- }
474
-
475
- /**
476
- * @api SLP.Utils.validateTxid2() validateTxid2()
477
- * @apiName validateTxid2
478
- * @apiGroup SLP Utils
479
- * @apiDescription Validate that txid is an SLP transaction.
480
- *
481
- * This second validatoin version uses the slp-validate slp library. It is
482
- * much slower and less efficient than SLPDB and is prone to time-outs for
483
- * tokens with large DAGs. However, it operates independently of SLPDB and
484
- * is a great second validation option, particularly when SLPDB returns 'null'
485
- * values.
486
- *
487
- * Due to the inefficiency of this call, only a single TXID can be input at a
488
- * time. This call will throw an error if the input is an array.
489
- *
490
- * @apiExample Example usage:
491
- *
492
- * // validate single SLP txid
493
- * (async () => {
494
- * try {
495
- * let validated = await bchjs.SLP.Utils.validateTxid2(
496
- * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
497
- * );
498
- * console.log(validated);
499
- * } catch (error) {
500
- * console.error(error);
501
- * }
502
- * })();
503
- *
504
- * // returns
505
- * [ { txid:
506
- * 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
507
- * valid: true } ]
508
- */
509
- async validateTxid2 (txid) {
510
- try {
511
- // console.log(`txid: ${JSON.stringify(txid, null, 2)}`)
512
-
513
- if (
514
- !txid ||
515
- txid === '' ||
516
- typeof txid !== 'string' ||
517
- txid.length !== 64
518
- ) {
519
- throw new Error('txid must be 64 character string.')
520
- }
521
-
522
- const path = `${this.restURL}slp/validateTxid2/${txid}`
523
-
524
- // console.log('validateTxid2() this.axiosOptions: ', this.axiosOptions)
525
- const response = await _this.axios.get(path, this.axiosOptions)
526
- return response.data
527
- } catch (error) {
528
- if (error.response && error.response.data) throw error.response.data
529
-
530
- if (error.error && error.error.indexOf('Network error') > -1) {
531
- throw new Error('slp-validate timed out')
532
- }
533
-
534
- throw error
535
- }
536
- }
537
-
538
- /**
539
- * @api SLP.Utils.whitelist() whitelist()
540
- * @apiName whitelist
541
- * @apiGroup SLP Utils
542
- * @apiDescription Get SLP tokens in whitelist
543
- * Retrieves a list of the SLP tokens that in the whitelist. Tokens in the
544
- * whitelist can be validated with the validateTxid3() function. validateTxid3()
545
- * will still work when the SLP network is under stress.
546
- *
547
- * @apiExample Example usage:
548
- *
549
- * // validate single SLP txid
550
- * (async () => {
551
- * try {
552
- * let list = await bchjs.SLP.Utils.whitelit();
553
- * console.log(list);
554
- * } catch (error) {
555
- * console.error(error);
556
- * }
557
- * })();
558
- *
559
- * // returns
560
- * [
561
- * {
562
- * name: 'USDH',
563
- * tokenId:
564
- * 'c4b0d62156b3fa5c8f3436079b5394f7edc1bef5dc1cd2f9d0c4d46f82cca479'
565
- * },
566
- * {
567
- * name: 'SPICE',
568
- * tokenId:
569
- * '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf'
570
- * },
571
- * {
572
- * name: 'PSF',
573
- * tokenId:
574
- * '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
575
- * },
576
- * {
577
- * name: 'TROUT',
578
- * tokenId:
579
- * 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
580
- * },
581
- * {
582
- * name: 'PSFTEST',
583
- * tokenId:
584
- * 'd0ef4de95b78222bfee2326ab11382f4439aa0855936e2fe6ac129a8d778baa0'
585
- * }
586
- * ]
587
- */
588
- async getWhitelist () {
589
- try {
590
- const path = `${this.restURL}slp/whitelist`
591
-
592
- // Retrieve the whitelist from the REST API if we haven't gotten it yet.
593
- if (this.whitelist.length === 0) {
594
- const response = await _this.axios.get(path, this.axiosOptions)
595
- // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
596
-
597
- this.whitelist = response.data
598
- }
599
-
600
- return this.whitelist
601
- } catch (error) {
602
- if (error.response && error.response.data) throw error.response.data
603
- throw error
604
- }
605
- }
606
-
607
- /**
608
- * @api SLP.Utils.validateTxid3() validateTxid3()
609
- * @apiName validateTxid3
610
- * @apiGroup SLP Utils
611
- * @apiDescription
612
- * Validate that txid is an SLP transaction using the SLPDB whitelist server.
613
- * Same exact functionality as the validateTxid() function, but this function
614
- * calls the whitelist SLPDB. It will only validate SLP tokens that are in the
615
- * whitelist. You can retrieve the whitelist with the SLP.Utils.whitelist()
616
- * function.
617
- *
618
- * @apiExample Example usage:
619
- *
620
- * // validate single SLP txid
621
- * (async () => {
622
- * try {
623
- * let validated = await bchjs.SLP.Utils.validateTxid3(
624
- * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
625
- * );
626
- * console.log(validated);
627
- * } catch (error) {
628
- * console.error(error);
629
- * }
630
- * })();
631
- *
632
- * // returns
633
- * [ { txid:
634
- * 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
635
- * valid: true } ]
636
- *
637
- * // validate multiple SLP txids
638
- * (async () => {
639
- * try {
640
- * let validated = await bchjs.SLP.Utils.validateTxid3([
641
- * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb",
642
- * "00ea27261196a411776f81029c0ebe34362936b4a9847deb1f7a40a02b3a1476"
643
- * ]);
644
- * console.log(validated);
645
- * } catch (error) {
646
- * console.error(error);
647
- * }
648
- * })();
649
- *
650
- * // returns
651
- * [ { txid:
652
- * 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
653
- * valid: true },
654
- * { txid:
655
- * '00ea27261196a411776f81029c0ebe34362936b4a9847deb1f7a40a02b3a1476',
656
- * valid: true } ]
657
- */
658
- async validateTxid3 (txid, usrObj = null) {
659
- const path = `${this.restURL}slp/validateTxid3`
660
-
661
- // console.log(`txid: ${JSON.stringify(txid, null, 2)}`)
662
- // console.log(`path: ${JSON.stringify(path, null, 2)}`)
663
- // console.log('validateTxid3 usrObj: ', usrObj)
664
-
665
- // Handle a single TXID or an array of TXIDs.
666
- let txids
667
- if (typeof txid === 'string') txids = [txid]
668
- else txids = txid
669
-
670
- try {
671
- // console.log('validateTxid3() this.axiosOptions: ', this.axiosOptions)
672
- const response = await _this.axios.post(
673
- path,
674
- {
675
- txids: txids,
676
- usrObj // pass user data when making an internal call.
677
- },
678
- this.axiosOptions
679
- )
680
- // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
681
-
682
- const validatedTxids = response.data
683
-
684
- return validatedTxids
685
- } catch (error) {
686
- // console.log('validateTxid3 error: ', error)
687
-
688
- // This case handles rate limit errors.
689
- if (error.response && error.response.data && error.response.data.error) {
690
- throw new Error(error.response.data.error)
691
- }
692
-
693
- // Not sure if this can be safely deprecated?
694
- if (error.response && error.response.data) throw error.response.data
695
- throw error
696
- }
697
- }
698
-
699
- /**
700
- * @api SLP.Utils.tokenStats() tokenStats()
701
- * @apiName tokenStats
702
- * @apiGroup SLP Utils
703
- * @apiDescription Stats for token by tokenId.
704
- *
705
- * @apiExample Example usage:
706
- *
707
- * (async () => {
708
- * try {
709
- * let stats = await bchjs.SLP.Utils.tokenStats(
710
- * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
711
- * )
712
- * console.log(stats)
713
- * } catch (error) {
714
- * console.error(error)
715
- * }
716
- * })()
717
- *
718
- * // returns
719
- * { tokenId:
720
- * 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
721
- * documentUri: '',
722
- * symbol: 'NAKAMOTO',
723
- * name: 'NAKAMOTO',
724
- * decimals: 8,
725
- * txnsSinceGenesis: 367,
726
- * validUtxos: 248,
727
- * validAddresses: 195,
728
- * circulatingSupply: 20995990,
729
- * totalBurned: 4010,
730
- * totalMinted: 21000000,
731
- * satoshisLockedUp: 135408
732
- * }
733
- */
734
- async tokenStats (tokenId) {
735
- try {
736
- const path = `${this.restURL}slp/tokenStats/${tokenId}`
737
-
738
- const response = await _this.axios.get(path, this.axiosOptions)
739
-
740
- return response.data
741
- } catch (error) {
742
- if (error.response && error.response.data) throw error.response.data
743
- throw error
744
- }
745
- }
746
-
747
- /**
748
- * @api SLP.Utils.transactions() transactions()
749
- * @apiName transactions
750
- * @apiGroup SLP Utils
751
- * @apiDescription SLP Transactions by tokenId and address.
752
- *
753
- * @apiExample Example usage:
754
- *
755
- * (async () => {
756
- * try {
757
- * let transactions = await bchjs.SLP.Utils.transactions(
758
- * "495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a",
759
- * "qrhvcy5xlegs858fjqf8ssl6a4f7wpstaqlsy4gusz"
760
- * )
761
- * console.log(transactions)
762
- * } catch (error) {
763
- * console.error(error)
764
- * }
765
- * })()
766
- *
767
- * // returns
768
- * [
769
- * {
770
- * "txid": "27e27170b546f05b2af69d6eddff8834038facf5d81302e9e562df09a5c4445f",
771
- * "tokenDetails": {
772
- * "valid": true,
773
- * "detail": {
774
- * "decimals": null,
775
- * "tokenIdHex": "495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a",
776
- * "timestamp": null,
777
- * "transactionType": "SEND",
778
- * "versionType": 1,
779
- * "documentUri": null,
780
- * "documentSha256Hex": null,
781
- * "symbol": null,
782
- * "name": null,
783
- * "batonVout": null,
784
- * "containsBaton": null,
785
- * "genesisOrMintQuantity": null,
786
- * "sendOutputs": [
787
- * {
788
- * "$numberDecimal": "0"
789
- * },
790
- * {
791
- * "$numberDecimal": "25"
792
- * },
793
- * {
794
- * "$numberDecimal": "77"
795
- * }
796
- * ]
797
- * },
798
- * "invalidReason": null,
799
- * "schema_version": 30
800
- * }
801
- * }
802
- * ]
803
- */
804
- // Retrieve token transactions for a given tokenId and address.
805
- async transactions (tokenId, address) {
806
- try {
807
- const path = `${this.restURL}slp/transactions/${tokenId}/${address}`
808
-
809
- const response = await _this.axios.get(path, this.axiosOptions)
810
-
811
- return response.data
812
- } catch (error) {
813
- if (error.response && error.response.data) throw error.response.data
814
- throw error
815
- }
816
- }
817
-
818
- /**
819
- * @api SLP.Utils.burnTotal() burnTotal()
820
- * @apiName burnTotal
821
- * @apiGroup SLP Utils
822
- * @apiDescription List input, output and burn total for slp transaction.
823
- *
824
- * @apiExample Example usage:
825
- *
826
- * (async () => {
827
- * try {
828
- * const burnTotal = await bchjs.SLP.Utils.burnTotal(
829
- * "c7078a6c7400518a513a0bde1f4158cf740d08d3b5bfb19aa7b6657e2f4160de"
830
- * )
831
- * console.log(burnTotal)
832
- * } catch (error) {
833
- * console.error(error)
834
- * }
835
- * })()
836
- *
837
- * // returns
838
- * {
839
- * transactionId: 'c7078a6c7400518a513a0bde1f4158cf740d08d3b5bfb19aa7b6657e2f4160de',
840
- * inputTotal: 100000100,
841
- * outputTotal: 100000000,
842
- * burnTotal: 100
843
- * }
844
- */
845
- async burnTotal (transactionId) {
846
- try {
847
- const path = `${this.restURL}slp/burnTotal/${transactionId}`
848
-
849
- const response = await _this.axios.get(path, this.axiosOptions)
850
-
851
- return response.data
852
- } catch (error) {
853
- if (error.response && error.response.data) throw error.response.data
854
- throw error
855
- }
856
- }
857
-
858
- /**
859
- * @api SLP.Utils.txDetails() txDetails()
860
- * @apiName txDetails
861
- * @apiGroup SLP Utils
862
- * @apiDescription Transaction details on a token transfer.
863
- * There is no bulk method for this endpoint. Can only get one tx at a time.
864
- *
865
- * @apiExample Example usage:
866
- *
867
- * (async () => {
868
- * try {
869
- * const details = await bchjs.SLP.Utils.txDetails(
870
- * "c7078a6c7400518a513a0bde1f4158cf740d08d3b5bfb19aa7b6657e2f4160de"
871
- * )
872
- * console.log(details)
873
- * } catch (error) {
874
- * console.error(error)
875
- * }
876
- * })()
877
- *
878
- */
879
- async txDetails (txid) {
880
- try {
881
- if (
882
- !txid ||
883
- txid === '' ||
884
- typeof txid !== 'string' ||
885
- txid.length !== 64
886
- ) {
887
- throw new Error('txid string must be included.')
888
- }
889
-
890
- // console.log(`this.restURL: ${this.restURL}`)
891
- const path = `${this.restURL}slp/txDetails/${txid}`
892
-
893
- const response = await _this.axios.get(path, this.axiosOptions)
894
- return response.data
895
- } catch (error) {
896
- if (error.response && error.response.data) throw error.response.data
897
- throw error
898
- }
899
- }
900
-
901
44
  /**
902
45
  * @api SLP.Utils.decodeOpReturn() decodeOpReturn()
903
46
  * @apiName decodeOpReturn
@@ -1176,107 +319,6 @@ class Utils {
1176
319
  }
1177
320
  }
1178
321
 
1179
- /**
1180
- * @api SLP.Utils.tokenUtxoDetailsWL() tokenUtxoDetailsWL()
1181
- * @apiName tokenUtxoDetailsWL
1182
- * @apiGroup SLP Utils
1183
- * @apiDescription
1184
- *
1185
- * Same as tokenUtxoDetails(), but it only uses the whitelist SLPDB to
1186
- * validate UTXOs. This will result in a lot of `isValid: null` values,
1187
- * but much more performant handling of SLP tokens. Some wallet apps prefer
1188
- * the scaling performance over the breadth of supported tokens.
1189
- *
1190
- * An optional second input object, `usrObj`, allows the user to inject an
1191
- * artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
1192
- * a number, the call will delay by that number of milliseconds between
1193
- * processing UTXOs.
1194
- *
1195
- * This is an API-heavy call. If you get a lot of `null` values, then slow down
1196
- * the calls by using the usrObj.utxoDelay property, or request info on fewer
1197
- * UTXOs at a
1198
- * time. `null` indicates that the UTXO can *not* be safely spent, because
1199
- * a judgement as to weather it is a token UTXO has not been made. Spending it
1200
- * could burn tokens. It's safest to ignore UTXOs with a value of `null`.
1201
- *
1202
- */
1203
- async tokenUtxoDetailsWL (utxos, usrObj = null) {
1204
- try {
1205
- // Throw error if input is not an array.
1206
- if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
1207
-
1208
- // Loop through each element in the array and validate the input before
1209
- // further processing.
1210
- for (let i = 0; i < utxos.length; i++) {
1211
- const utxo = utxos[i]
1212
-
1213
- // Ensure the UTXO has a txid or tx_hash property.
1214
- if (!utxo.txid) {
1215
- // If Electrumx, convert the tx_hash property to txid.
1216
- if (utxo.tx_hash) {
1217
- utxo.txid = utxo.tx_hash
1218
- } else {
1219
- // If there is neither a txid or tx_hash property, throw an error.
1220
- throw new Error(
1221
- `utxo ${i} does not have a txid or tx_hash property.`
1222
- )
1223
- }
1224
- }
1225
-
1226
- // Ensure the UTXO has a vout or tx_pos property.
1227
- if (!Number.isInteger(utxo.vout)) {
1228
- if (Number.isInteger(utxo.tx_pos)) {
1229
- utxo.vout = utxo.tx_pos
1230
- } else {
1231
- throw new Error(
1232
- `utxo ${i} does not have a vout or tx_pos property.`
1233
- )
1234
- }
1235
- }
1236
- }
1237
-
1238
- // Hydrate each UTXO with data from SLP OP_REUTRNs.
1239
- const outAry = await this._hydrateUtxo(utxos, usrObj)
1240
- // console.log(`outAry: ${JSON.stringify(outAry, null, 2)}`)
1241
-
1242
- // *After* each UTXO has been hydrated with SLP data,
1243
- // validate the TXID with SLPDB.
1244
- for (let i = 0; i < outAry.length; i++) {
1245
- const utxo = outAry[i]
1246
-
1247
- // *After* the UTXO has been hydrated with SLP data,
1248
- // validate the TXID with SLPDB.
1249
- if (utxo.tokenType) {
1250
- // Only execute this block if the current UTXO has a 'tokenType'
1251
- // property. i.e. it has been successfully hydrated with SLP
1252
- // information.
1253
-
1254
- // Validate against the whitelist SLPDB.
1255
- const whitelistResult = await this.validateTxid3(utxo.txid, usrObj)
1256
- // console.log(
1257
- // `whitelist-SLPDB for ${txid}: ${JSON.stringify(
1258
- // whitelistResult,
1259
- // null,
1260
- // 2
1261
- // )}`
1262
- // )
1263
-
1264
- let isValid = null
1265
-
1266
- // Safely retrieve the returned value.
1267
- if (whitelistResult[0] !== null) isValid = whitelistResult[0].valid
1268
-
1269
- utxo.isValid = isValid
1270
- }
1271
- }
1272
-
1273
- return outAry
1274
- } catch (error) {
1275
- if (error.response && error.response.data) throw error.response.data
1276
- throw error
1277
- }
1278
- }
1279
-
1280
322
  // This is a private function that is called by tokenUtxoDetails().
1281
323
  // It loops through an array of UTXOs and tries to hydrate them with SLP
1282
324
  // token information from the OP_RETURN data.
@@ -1661,289 +703,6 @@ class Utils {
1661
703
  throw error
1662
704
  }
1663
705
  }
1664
-
1665
- /**
1666
- * @api SLP.Utils.hydrateUtxos() hydrateUtxos()
1667
- * @apiName hydrateUtxos
1668
- * @apiGroup SLP Utils
1669
- * @apiDescription Hydrate a UTXO with SLP token metadata.
1670
- *
1671
- * The same as tokenUtxoDetails(), but uses bch-api to do the heavy lifting,
1672
- * which greatly increases the speed, since fewer API calls need to be made.
1673
- * However, internal API calls are still counted against your rate limits.
1674
- *
1675
- * This function expects an array of UTXO objects as input. It returns an
1676
- * array of equal size. The UTXO data hydrated with token information.
1677
- * - If the UTXO does not belong to a SLP transaction, it will return an
1678
- * `isValid` property set to `false`.
1679
- * - If the UTXO is part of an SLP transaction, it will return the UTXO object
1680
- * with additional SLP information attached. An `isValid` property will be
1681
- * included.
1682
- * - If `isValid` is `true`, the UTXO is a valid SLP UTXO.
1683
- * - If `isValid` is `null`, then SLPDB has not yet processed that txid
1684
- * and validity has not been confirmed,
1685
- * or a 429 rate-limit error was enountered during the processing of the
1686
- * request.
1687
- *
1688
- * An optional second input object, `usrObj`, allows the user to inject an
1689
- * artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
1690
- * a number, the call will delay by that number of milliseconds between
1691
- * processing UTXOs.
1692
- *
1693
- * This is an API-heavy call. If you get a lot of `null` values, then slow down
1694
- * the calls by using the usrObj.utxoDelay property, or request info on fewer
1695
- * UTXOs at a
1696
- * time. `null` indicates that the UTXO can *not* be safely spent, because
1697
- * a judgement as to weather it is a token UTXO has not been made. Spending it
1698
- * could burn tokens. It's safest to ignore UTXOs with a value of `null`.
1699
- *
1700
- * @apiExample Example usage:
1701
- *
1702
- * (async () => {
1703
- * try {
1704
- * const utxos = await bchjs.Electrumx.utxo([
1705
- * "bitcoincash:qq6mvsm7l92d77zpymmltvaw09p5uzghyuyx7spygg",
1706
- * "bitcoincash:qpjdrs8qruzh8xvusdfmutjx62awcepnhyperm3g89",
1707
- * "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk"
1708
- * ])
1709
- *
1710
- * // Wait 100mS between processing UTXOs, to prevent rate limit errors.
1711
- * const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos, { utxoDelay: 100 })
1712
- *
1713
- * console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
1714
- * } catch (error) {
1715
- * console.error(error)
1716
- * }
1717
- * })()
1718
- *
1719
- * // returns
1720
- * {
1721
- * "slpUtxos": [
1722
- * {
1723
- * "utxos": [
1724
- * {
1725
- * "height": 654522,
1726
- * "tx_hash": "516e763932061f9e868652d727045b714db1ecac459e84cd52b5b4cb39572ecc",
1727
- * "tx_pos": 0,
1728
- * "value": 6000,
1729
- * "satoshis": 6000,
1730
- * "txid": "516e763932061f9e868652d727045b714db1ecac459e84cd52b5b4cb39572ecc",
1731
- * "vout": 0,
1732
- * "isValid": false
1733
- * }
1734
- * ],
1735
- * "address": "bitcoincash:qq6mvsm7l92d77zpymmltvaw09p5uzghyuyx7spygg"
1736
- * },
1737
- * {
1738
- * "utxos": [
1739
- * {
1740
- * "height": 654522,
1741
- * "tx_hash": "8ec01d851d9df9fb4b4331275e2ff680257c224100d0081cec6fbeedf982f738",
1742
- * "tx_pos": 1,
1743
- * "value": 546,
1744
- * "satoshis": 546,
1745
- * "txid": "8ec01d851d9df9fb4b4331275e2ff680257c224100d0081cec6fbeedf982f738",
1746
- * "vout": 1,
1747
- * "utxoType": "token",
1748
- * "transactionType": "send",
1749
- * "tokenId": "a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2",
1750
- * "tokenTicker": "TROUT",
1751
- * "tokenName": "Trout's test token",
1752
- * "tokenDocumentUrl": "troutsblog.com",
1753
- * "tokenDocumentHash": "",
1754
- * "decimals": 2,
1755
- * "tokenType": 1,
1756
- * "tokenQty": 10,
1757
- * "isValid": true
1758
- * }
1759
- * ],
1760
- * "address": "bitcoincash:qpjdrs8qruzh8xvusdfmutjx62awcepnhyperm3g89"
1761
- * },
1762
- * {
1763
- * "utxos": [
1764
- * {
1765
- * "height": 654522,
1766
- * "tx_hash": "072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
1767
- * "tx_pos": 0,
1768
- * "value": 6999,
1769
- * "satoshis": 6999,
1770
- * "txid": "072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
1771
- * "vout": 0,
1772
- * "isValid": false
1773
- * },
1774
- * {
1775
- * "height": 654522,
1776
- * "tx_hash": "a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
1777
- * "tx_pos": 1,
1778
- * "value": 546,
1779
- * "satoshis": 546,
1780
- * "txid": "a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
1781
- * "vout": 1,
1782
- * "utxoType": "token",
1783
- * "transactionType": "send",
1784
- * "tokenId": "6201f3efe486c577433622817b99645e1d473cd3882378f9a0efc128ab839a82",
1785
- * "tokenTicker": "VALENTINE",
1786
- * "tokenName": "Valentine day token",
1787
- * "tokenDocumentUrl": "fullstack.cash",
1788
- * "tokenDocumentHash": "",
1789
- * "decimals": 2,
1790
- * "tokenType": 1,
1791
- * "tokenQty": 5,
1792
- * "isValid": true
1793
- * }
1794
- * ],
1795
- * "address": "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk"
1796
- * }
1797
- * ]
1798
- * }
1799
- *
1800
- * (async () => {
1801
- * try {
1802
- * const utxos = [
1803
- * {
1804
- * utxos: [
1805
- * {
1806
- * txid: "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
1807
- * vout: 3,
1808
- * value: "6816",
1809
- * height: 606848,
1810
- * confirmations: 13,
1811
- * satoshis: 6816
1812
- * }
1813
- * ]
1814
- * }
1815
- * ]
1816
- *
1817
- * const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos)
1818
- *
1819
- * console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
1820
- * } catch (error) {
1821
- * console.error(error)
1822
- * }
1823
- * })()
1824
- *
1825
- * // returns
1826
- * {
1827
- * "slpUtxos": [
1828
- * {
1829
- * "utxos": [
1830
- * {
1831
- * "txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
1832
- * "vout": 3,
1833
- * "value": "6816",
1834
- * "height": 606848,
1835
- * "confirmations": 13,
1836
- * "satoshis": 6816,
1837
- * "isValid": false
1838
- * }
1839
- * ]
1840
- * }
1841
- * ]
1842
- */
1843
- // Same as tokenUtxoDetails(), but reduces API calls by having bch-api server
1844
- // do the heavy lifting.
1845
- async hydrateUtxos (utxos, usrObj) {
1846
- try {
1847
- // Throw error if input is not an array.
1848
- if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
1849
-
1850
- const response = await _this.axios.post(
1851
- `${this.restURL}slp/hydrateUtxos`,
1852
- {
1853
- utxos: utxos,
1854
- usrObj
1855
- },
1856
- this.axiosOptions
1857
- )
1858
-
1859
- return response.data
1860
- } catch (error) {
1861
- console.log('Error in hydrateUtxos(): ', error)
1862
-
1863
- if (error.response && error.response.data) {
1864
- throw new Error(JSON.stringify(error.response.data, null, 2))
1865
- }
1866
- throw error
1867
- }
1868
- }
1869
-
1870
- /**
1871
- * @api SLP.Utils.hydrateUtxosWL() hydrateUtxosWL()
1872
- * @apiName hydrateUtxosWL
1873
- * @apiGroup SLP Utils
1874
- * @apiDescription
1875
- * This call is exactly the same as `hydrateUtxos()`. This version hydrate a
1876
- * UTXO with SLP token metadata, but only uses the whitelist SLPDB for
1877
- * validation.
1878
- *
1879
- * Whitelist SLPDBs will return `isValid: null` for any token not in the
1880
- * 'whitelist' filter. Filtered SLPDBs are much smaller and more reliable
1881
- * to operate.
1882
- *
1883
- */
1884
- // Same as tokenUtxoDetailsWL(), but reduces API calls by having bch-api server
1885
- // do the heavy lifting.
1886
- async hydrateUtxosWL (utxos) {
1887
- try {
1888
- // Throw error if input is not an array.
1889
- if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
1890
-
1891
- const response = await _this.axios.post(
1892
- `${this.restURL}slp/hydrateUtxosWL`,
1893
- {
1894
- utxos: utxos
1895
- },
1896
- this.axiosOptions
1897
- )
1898
-
1899
- return response.data
1900
- } catch (error) {
1901
- if (error.response && error.response.data) throw error.response.data
1902
- else throw error
1903
- }
1904
- }
1905
-
1906
- /**
1907
- * @api SLP.Utils.getStatus() getStatus()
1908
- * @apiName getStatus
1909
- * @apiGroup SLP Utils
1910
- * @apiDescription Get the status and health of the SLPDB connected to bch-api.
1911
- *
1912
- * @apiExample Example usage:
1913
- *
1914
- * // Get the current blockheight of the SLPDB indexer.
1915
- * (async () => {
1916
- * try {
1917
- * let validated = await bchjs.SLP.Utils.validateTxid(
1918
- * "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
1919
- * );
1920
- * console.log(validated);
1921
- * } catch (error) {
1922
- * console.error(error);
1923
- * }
1924
- * })();
1925
- *
1926
- * // returns
1927
- * [ { txid:
1928
- * 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
1929
- * valid: true } ]
1930
- *
1931
- */
1932
- async getStatus (txid) {
1933
- const path = `${this.restURL}slp/status`
1934
-
1935
- try {
1936
- const response = await _this.axios.get(path, this.axiosOptions)
1937
- // console.log(
1938
- // `getStatus response.data: ${JSON.stringify(response.data, null, 2)}`
1939
- // )
1940
-
1941
- return response.data
1942
- } catch (error) {
1943
- if (error.response && error.response.data) throw error.response.data
1944
- throw error
1945
- }
1946
- }
1947
706
  }
1948
707
 
1949
708
  module.exports = Utils