@psf/bch-js 4.21.0 → 4.22.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@psf/bch-js",
3
- "version": "4.21.0",
3
+ "version": "4.22.0",
4
4
  "description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
5
5
  "author": "Chris Troutner <chris.troutner@gmail.com>",
6
6
  "contributors": [
package/src/bch-js.js CHANGED
@@ -38,6 +38,7 @@ const Ecash = require('./ecash')
38
38
  // Indexers
39
39
  const Ninsight = require('./ninsight')
40
40
  const Electrumx = require('./electrumx')
41
+ const PsfSlpIndexer = require('./psf-slp-indexer')
41
42
 
42
43
  class BCHJS {
43
44
  constructor (config) {
@@ -120,6 +121,8 @@ class BCHJS {
120
121
 
121
122
  this.DSProof = new DSProof(libConfig)
122
123
  this.eCash = new Ecash()
124
+
125
+ this.PsfSlpIndexer = new PsfSlpIndexer(libConfig)
123
126
  }
124
127
  }
125
128
 
@@ -0,0 +1,292 @@
1
+ /*
2
+ This library interacts with the PSF slp indexer REST API endpoints operated
3
+ by FullStack.cash
4
+ */
5
+ // Public npm libraries
6
+ const axios = require('axios')
7
+
8
+ // let _this
9
+
10
+ class PsfSlpIndexer {
11
+ constructor (config) {
12
+ this.restURL = config.restURL
13
+ this.apiToken = config.apiToken
14
+ this.authToken = config.authToken
15
+
16
+ if (this.authToken) {
17
+ // Add Basic Authentication token to the authorization header.
18
+ this.axiosOptions = {
19
+ headers: {
20
+ authorization: this.authToken
21
+ }
22
+ }
23
+ } else {
24
+ // Add JWT token to the authorization header.
25
+ this.axiosOptions = {
26
+ headers: {
27
+ authorization: `Token ${this.apiToken}`
28
+ }
29
+ }
30
+ }
31
+
32
+ // _this = this
33
+ }
34
+
35
+ /**
36
+ * @api PsfSlpIndexer.status() status()
37
+ * @apiName Status
38
+ * @apiGroup PSF SLP
39
+ * @apiDescription Return status from psf slp indexer.
40
+ *
41
+ * @apiExample Example usage:
42
+ * (async () => {
43
+ * try {
44
+ * let status = await bchjs.PsfSlpIndexer.status();
45
+ * console.log(status);
46
+ * } catch(error) {
47
+ * console.error(error)
48
+ * }
49
+ * })()
50
+ *
51
+ * {
52
+ * "status": {
53
+ * "startBlockHeight": 543376,
54
+ * "syncedBlockHeight": 723249,
55
+ * "chainBlockHeight": 722679
56
+ * }
57
+ * }
58
+ *
59
+ */
60
+ async status () {
61
+ try {
62
+ const response = await axios.get(
63
+ `${this.restURL}psf/slp/status`,
64
+ this.axiosOptions
65
+ )
66
+ return response.data
67
+ } catch (error) {
68
+ if (error.response && error.response.data) throw error.response.data
69
+ else throw error
70
+ }
71
+ }
72
+
73
+ /**
74
+ * @api PsfSlpIndexer.balance() balance()
75
+ * @apiName SLP Balance
76
+ * @apiGroup PSF SLP
77
+ * @apiDescription Return slp balance for a single address.
78
+ *
79
+ * @apiExample Example usage:
80
+ * (async () => {
81
+ * try {
82
+ * let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n');
83
+ * console.log(balance);
84
+ * } catch(error) {
85
+ * console.error(error)
86
+ * }
87
+ * })()
88
+ *
89
+ * {
90
+ * balance: {
91
+ * utxos: [
92
+ * {
93
+ * txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
94
+ * vout: 1,
95
+ * type: 'token',
96
+ * qty: '1800',
97
+ * tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
98
+ * address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
99
+ * }
100
+ * ],
101
+ * txs: [
102
+ * {
103
+ * txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
104
+ * height: 717796
105
+ * },
106
+ * {
107
+ * txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
108
+ * height: 717832
109
+ * }
110
+ * ],
111
+ * balances: [
112
+ * {
113
+ * tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
114
+ * qty: '1800'
115
+ * }
116
+ * ]
117
+ * }
118
+ * }
119
+ *
120
+ */
121
+ async balance (address) {
122
+ try {
123
+ // Handle single address.
124
+ if (typeof address === 'string') {
125
+ const response = await axios.post(
126
+ `${this.restURL}psf/slp/address`,
127
+ { address },
128
+ this.axiosOptions
129
+ )
130
+ return response.data
131
+ }
132
+ throw new Error('Input address must be a string.')
133
+ } catch (error) {
134
+ if (error.response && error.response.data) throw error.response.data
135
+ else throw error
136
+ }
137
+ }
138
+
139
+ /**
140
+ * @api PsfSlpIndexer.tokenStats() tokenStats()
141
+ * @apiName Token Stats
142
+ * @apiGroup PSF SLP
143
+ * @apiDescription Return list stats for a single slp token.
144
+ *
145
+ * @apiExample Example usage:
146
+ * (async () => {
147
+ * try {
148
+ * let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
149
+ * console.log(tokenStats);
150
+ * } catch(error) {
151
+ * console.error(error)
152
+ * }
153
+ * })()
154
+ *
155
+ * {
156
+ * tokenData: {
157
+ * type: 1,
158
+ * ticker: 'TROUT',
159
+ * name: "Trout's test token",
160
+ * tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
161
+ * documentUri: 'troutsblog.com',
162
+ * documentHash: '',
163
+ * decimals: 2,
164
+ * mintBatonIsActive: true,
165
+ * tokensInCirculationBN: '100098953386',
166
+ * tokensInCirculationStr: '100098953386',
167
+ * blockCreated: 622414,
168
+ * totalBurned: '1046614',
169
+ * totalMinted: '100100000000'
170
+ * txs: [
171
+ * {
172
+ * txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
173
+ * height: 622414,
174
+ * type: 'GENESIS',
175
+ * qty: '100000000000'
176
+ * }
177
+ * ]
178
+ * }
179
+ * }
180
+ *
181
+ */
182
+
183
+ async tokenStats (tokenId) {
184
+ try {
185
+ // Handle single address.
186
+ if (typeof tokenId === 'string') {
187
+ const response = await axios.post(
188
+ `${this.restURL}psf/slp/token`,
189
+ { tokenId },
190
+ this.axiosOptions
191
+ )
192
+ return response.data
193
+ }
194
+ throw new Error('Input tokenId must be a string.')
195
+ } catch (error) {
196
+ if (error.response && error.response.data) throw error.response.data
197
+ else throw error
198
+ }
199
+ }
200
+
201
+ /**
202
+ * @api PsfSlpIndexer.tx() tx()
203
+ * @apiName SLP Transaction Data
204
+ * @apiGroup PSF SLP
205
+ * @apiDescription Return slp transaction data.
206
+ *
207
+ * @apiExample Example usage:
208
+ * (async () => {
209
+ * try {
210
+ * let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
211
+ * console.log(txData);
212
+ * } catch(error) {
213
+ * console.error(error)
214
+ * }
215
+ * })()
216
+ *
217
+ * {
218
+ * txData: {
219
+ * txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
220
+ * hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
221
+ * version: 2,
222
+ * size: 339,
223
+ * locktime: 0,
224
+ * vin: [
225
+ * {
226
+ * txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
227
+ * vout: 0,
228
+ * scriptSig: {
229
+ * asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
230
+ * hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
231
+ * },
232
+ * sequence: 4294967295,
233
+ * address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
234
+ * value: 0.00051303,
235
+ * tokenQty: 0,
236
+ * tokenQtyStr: '0',
237
+ * tokenId: null
238
+ * }
239
+ * ],
240
+ * vout: [
241
+ * {
242
+ * value: 0,
243
+ * n: 0,
244
+ * scriptPubKey: {
245
+ * asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
246
+ * hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
247
+ * type: 'nulldata'
248
+ * },
249
+ * tokenQtyStr: '0',
250
+ * tokenQty: 0
251
+ * }
252
+ * ],
253
+ * hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
254
+ * blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
255
+ * confirmations: 97398,
256
+ * time: 1581773131,
257
+ * blocktime: 1581773131,
258
+ * blockheight: 622414,
259
+ * isSlpTx: true,
260
+ * tokenTxType: 'GENESIS',
261
+ * tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
262
+ * tokenType: 1,
263
+ * tokenTicker: 'TROUT',
264
+ * tokenName: "Trout's test token",
265
+ * tokenDecimals: 2,
266
+ * tokenUri: 'troutsblog.com',
267
+ * tokenDocHash: '',
268
+ * isValidSlp: true
269
+ * }
270
+ * }
271
+ *
272
+ */
273
+ async tx (txid) {
274
+ try {
275
+ // Handle single address.
276
+ if (typeof txid === 'string') {
277
+ const response = await axios.post(
278
+ `${this.restURL}psf/slp/txid`,
279
+ { txid },
280
+ this.axiosOptions
281
+ )
282
+ return response.data
283
+ }
284
+ throw new Error('Input txid must be a string.')
285
+ } catch (error) {
286
+ if (error.response && error.response.data) throw error.response.data
287
+ else throw error
288
+ }
289
+ }
290
+ }
291
+
292
+ module.exports = PsfSlpIndexer
@@ -0,0 +1,74 @@
1
+ /*
2
+ Integration tests for the psf-slp-indexer.js library
3
+ */
4
+
5
+ const assert = require('chai').assert
6
+
7
+ const BCHJS = require('../../../../src/bch-js')
8
+ let bchjs
9
+
10
+ describe('#psf-slp-indexer', () => {
11
+ beforeEach(async () => {
12
+ // Introduce a delay so that the BVT doesn't trip the rate limits.
13
+ if (process.env.IS_USING_FREE_TIER) await sleep(3000)
14
+
15
+ bchjs = new BCHJS()
16
+ })
17
+
18
+ describe('#status', () => {
19
+ it('should return the status of the indexer.', async () => {
20
+ const result = await bchjs.PsfSlpIndexer.status()
21
+ // console.log('result: ', result)
22
+
23
+ assert.property(result.status, 'startBlockHeight')
24
+ assert.property(result.status, 'syncedBlockHeight')
25
+ assert.property(result.status, 'chainBlockHeight')
26
+ })
27
+ })
28
+
29
+ describe('#balance', () => {
30
+ it('should get balance data for an address.', async () => {
31
+ const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
32
+
33
+ const result = await bchjs.PsfSlpIndexer.balance(addr)
34
+ // console.log('result: ', result)
35
+
36
+ assert.property(result.balance, 'utxos')
37
+ assert.property(result.balance, 'txs')
38
+ assert.property(result.balance, 'balances')
39
+ })
40
+ })
41
+
42
+ describe('#tokenStats', () => {
43
+ it('should get stats on a token', async () => {
44
+ const tokenId =
45
+ '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
46
+
47
+ const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
48
+ // console.log('result: ', result)
49
+
50
+ assert.property(result.tokenData, 'documentUri')
51
+ assert.property(result.tokenData, 'txs')
52
+ assert.property(result.tokenData, 'totalBurned')
53
+ })
54
+ })
55
+
56
+ describe('#tx', () => {
57
+ it('should get hydrated tx data', async () => {
58
+ const txid =
59
+ '83361c34cac2ea7f9ca287fca57a96cc0763719f0cdf4850f9696c1e68eb635c'
60
+
61
+ const result = await bchjs.PsfSlpIndexer.tx(txid)
62
+ // console.log('result: ', result)
63
+
64
+ assert.property(result.txData, 'vin')
65
+ assert.property(result.txData, 'vout')
66
+ assert.property(result.txData, 'isValidSlp')
67
+ })
68
+ })
69
+ })
70
+
71
+ // Promise-based sleep function
72
+ function sleep (ms) {
73
+ return new Promise(resolve => setTimeout(resolve, ms))
74
+ }
@@ -0,0 +1,130 @@
1
+ /*
2
+ Mocking data for unit tests for the PSF SLP INDEXER library.
3
+ */
4
+
5
+ 'use strict'
6
+
7
+ const tokenStats = {
8
+ tokenData: {
9
+ type: 1,
10
+ ticker: 'TP03',
11
+ name: 'Test Plugin 03',
12
+ tokenId: '13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
13
+ documentUri: 'fullstack.cash',
14
+ documentHash: '',
15
+ decimals: 0,
16
+ mintBatonIsActive: false,
17
+ tokensInCirculationBN: '1',
18
+ tokensInCirculationStr: '1',
19
+ blockCreated: 722420,
20
+ totalBurned: '0',
21
+ totalMinted: '1',
22
+ txs: [
23
+ {
24
+ txid: '13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
25
+ height: 722420,
26
+ type: 'GENESIS',
27
+ qty: '1'
28
+ }
29
+ ]
30
+ }
31
+ }
32
+
33
+ const txData = {
34
+ txData: {
35
+ txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
36
+ hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
37
+ version: 2,
38
+ size: 339,
39
+ locktime: 0,
40
+ vin: [
41
+ {
42
+ txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
43
+ vout: 0,
44
+ scriptSig: {
45
+ asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
46
+ hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
47
+ },
48
+ sequence: 4294967295,
49
+ address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
50
+ value: 0.00051303,
51
+ tokenQty: 0,
52
+ tokenQtyStr: '0',
53
+ tokenId: null
54
+ }
55
+ ],
56
+ vout: [
57
+ {
58
+ value: 0,
59
+ n: 0,
60
+ scriptPubKey: {
61
+ asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
62
+ hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
63
+ type: 'nulldata'
64
+ },
65
+ tokenQtyStr: '0',
66
+ tokenQty: 0
67
+ }
68
+ ],
69
+ hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
70
+ blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
71
+ confirmations: 97398,
72
+ time: 1581773131,
73
+ blocktime: 1581773131,
74
+ blockheight: 622414,
75
+ isSlpTx: true,
76
+ tokenTxType: 'GENESIS',
77
+ tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
78
+ tokenType: 1,
79
+ tokenTicker: 'TROUT',
80
+ tokenName: "Trout's test token",
81
+ tokenDecimals: 2,
82
+ tokenUri: 'troutsblog.com',
83
+ tokenDocHash: '',
84
+ isValidSlp: true
85
+ }
86
+ }
87
+ const balance = {
88
+ balance: {
89
+ utxos: [
90
+ {
91
+ txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
92
+ vout: 1,
93
+ type: 'token',
94
+ qty: '1800',
95
+ tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
96
+ address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
97
+ }
98
+ ],
99
+ txs: [
100
+ {
101
+ txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
102
+ height: 717796
103
+ },
104
+ {
105
+ txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
106
+ height: 717832
107
+ }
108
+ ],
109
+ balances: [
110
+ {
111
+ tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
112
+ qty: '1800'
113
+ }
114
+ ]
115
+ }
116
+ }
117
+
118
+ const status = {
119
+ status: {
120
+ startBlockHeight: 543376,
121
+ syncedBlockHeight: 722860,
122
+ chainBlockHeight: 722679
123
+ }
124
+ }
125
+ module.exports = {
126
+ tokenStats,
127
+ txData,
128
+ balance,
129
+ status
130
+ }
@@ -0,0 +1,258 @@
1
+ const chai = require('chai')
2
+ const assert = chai.assert
3
+ const axios = require('axios')
4
+ const sinon = require('sinon')
5
+
6
+ const BCHJS = require('../../src/bch-js')
7
+ const bchjs = new BCHJS()
8
+
9
+ const mockData = require('./fixtures/psf-slp-indexer-mock')
10
+
11
+ describe('#PsfSlpIndexer', () => {
12
+ let sandbox
13
+ beforeEach(() => (sandbox = sinon.createSandbox()))
14
+ afterEach(() => sandbox.restore())
15
+
16
+ describe('#status', () => {
17
+ it('should GET status', async () => {
18
+ // Stub the network call.
19
+ sandbox.stub(axios, 'get').resolves({ data: mockData.status })
20
+
21
+ const result = await bchjs.PsfSlpIndexer.status()
22
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
23
+ assert.property(result, 'status')
24
+ assert.property(result.status, 'startBlockHeight')
25
+ assert.property(result.status, 'syncedBlockHeight')
26
+ assert.property(result.status, 'chainBlockHeight')
27
+ })
28
+
29
+ it('should handle axios error', async () => {
30
+ try {
31
+ // Stub the network call.
32
+ sandbox.stub(axios, 'get').throws(new Error('test error'))
33
+
34
+ await bchjs.PsfSlpIndexer.status()
35
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
36
+ assert.equal(true, false, 'Unexpected result!')
37
+ } catch (err) {
38
+ assert.include(err.message, 'test error')
39
+ }
40
+ })
41
+ it('should handle request error', async () => {
42
+ try {
43
+ // Stub the network call.
44
+ const testErr = new Error()
45
+ testErr.response = { data: { status: 422 } }
46
+ sandbox.stub(axios, 'get').throws(testErr)
47
+
48
+ await bchjs.PsfSlpIndexer.status()
49
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
50
+ assert.equal(true, false, 'Unexpected result!')
51
+ } catch (err) {
52
+ assert.equal(err.status, 422)
53
+ }
54
+ })
55
+ })
56
+
57
+ describe('#balance', () => {
58
+ it('should GET balance', async () => {
59
+ // Stub the network call.
60
+ sandbox.stub(axios, 'post').resolves({ data: mockData.balance })
61
+ const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
62
+ const result = await bchjs.PsfSlpIndexer.balance(addr)
63
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
64
+ assert.property(result, 'balance')
65
+
66
+ assert.property(result.balance, 'utxos')
67
+ assert.property(result.balance, 'txs')
68
+ assert.property(result.balance, 'balances')
69
+ assert.isArray(result.balance.utxos)
70
+ assert.isArray(result.balance.txs)
71
+ assert.isArray(result.balance.balances)
72
+ })
73
+ it('should throw an error for improper input', async () => {
74
+ try {
75
+ const addr = 12345
76
+
77
+ await bchjs.PsfSlpIndexer.balance(addr)
78
+ assert.equal(true, false, 'Unexpected result!')
79
+ } catch (err) {
80
+ // console.log(`err: `, err)
81
+ assert.include(err.message, 'Input address must be a string.')
82
+ }
83
+ })
84
+ it('should handle axios error', async () => {
85
+ try {
86
+ // Stub the network call.
87
+ sandbox.stub(axios, 'post').throws(new Error('test error'))
88
+
89
+ const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
90
+
91
+ await bchjs.PsfSlpIndexer.balance(addr)
92
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
93
+ assert.equal(true, false, 'Unexpected result!')
94
+ } catch (err) {
95
+ assert.include(err.message, 'test error')
96
+ }
97
+ })
98
+ it('should handle request error', async () => {
99
+ try {
100
+ // Stub the network call.
101
+ const testErr = new Error()
102
+ testErr.response = { data: { status: 422 } }
103
+ sandbox.stub(axios, 'post').throws(testErr)
104
+
105
+ const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
106
+
107
+ await bchjs.PsfSlpIndexer.balance(addr)
108
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
109
+ assert.equal(true, false, 'Unexpected result!')
110
+ } catch (err) {
111
+ assert.equal(err.status, 422)
112
+ }
113
+ })
114
+ })
115
+
116
+ describe('#tokenStats', () => {
117
+ it('should GET token stats', async () => {
118
+ // Stub the network call.
119
+ sandbox.stub(axios, 'post').resolves({ data: mockData.tokenStats })
120
+
121
+ const tokenId =
122
+ 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
123
+ const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
124
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
125
+ assert.property(result, 'tokenData')
126
+ assert.property(result.tokenData, 'type')
127
+ assert.property(result.tokenData, 'ticker')
128
+ assert.property(result.tokenData, 'name')
129
+ assert.property(result.tokenData, 'tokenId')
130
+ assert.property(result.tokenData, 'documentUri')
131
+ assert.property(result.tokenData, 'documentHash')
132
+ assert.property(result.tokenData, 'decimals')
133
+ assert.property(result.tokenData, 'mintBatonIsActive')
134
+ assert.property(result.tokenData, 'tokensInCirculationBN')
135
+ assert.property(result.tokenData, 'tokensInCirculationStr')
136
+ assert.property(result.tokenData, 'blockCreated')
137
+ assert.property(result.tokenData, 'totalBurned')
138
+ assert.property(result.tokenData, 'totalMinted')
139
+ assert.property(result.tokenData, 'txs')
140
+ })
141
+ it('should throw an error for improper input', async () => {
142
+ try {
143
+ const tokenId = 12345
144
+
145
+ await bchjs.PsfSlpIndexer.tokenStats(tokenId)
146
+ assert.equal(true, false, 'Unexpected result!')
147
+ } catch (err) {
148
+ // console.log(`err: `, err)
149
+ assert.include(err.message, 'Input tokenId must be a string.')
150
+ }
151
+ })
152
+ it('should handle axios error', async () => {
153
+ try {
154
+ // Stub the network call.
155
+ sandbox.stub(axios, 'post').throws(new Error('test error'))
156
+
157
+ const tokenId =
158
+ 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
159
+ await bchjs.PsfSlpIndexer.tokenStats(tokenId)
160
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
161
+ assert.equal(true, false, 'Unexpected result!')
162
+ } catch (err) {
163
+ assert.include(err.message, 'test error')
164
+ }
165
+ })
166
+ it('should handle request error', async () => {
167
+ try {
168
+ // Stub the network call.
169
+ const testErr = new Error()
170
+ testErr.response = { data: { status: 422 } }
171
+ sandbox.stub(axios, 'post').throws(testErr)
172
+
173
+ const tokenId =
174
+ 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
175
+ await bchjs.PsfSlpIndexer.tokenStats(tokenId)
176
+ assert.equal(true, false, 'Unexpected result!')
177
+ } catch (err) {
178
+ assert.equal(err.status, 422)
179
+ }
180
+ })
181
+ })
182
+
183
+ describe('#tx', () => {
184
+ it('should GET transaction data', async () => {
185
+ // Stub the network call.
186
+ sandbox.stub(axios, 'post').resolves({ data: mockData.txData })
187
+
188
+ const txid =
189
+ 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
190
+ const result = await bchjs.PsfSlpIndexer.tx(txid)
191
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
192
+ assert.property(result, 'txData')
193
+ assert.property(result.txData, 'txid')
194
+ assert.property(result.txData, 'hash')
195
+ assert.property(result.txData, 'version')
196
+ assert.property(result.txData, 'size')
197
+ assert.property(result.txData, 'locktime')
198
+ assert.property(result.txData, 'vin')
199
+ assert.property(result.txData, 'vout')
200
+ assert.property(result.txData, 'hex')
201
+ assert.property(result.txData, 'blockhash')
202
+ assert.property(result.txData, 'confirmations')
203
+ assert.property(result.txData, 'time')
204
+ assert.property(result.txData, 'blocktime')
205
+ assert.property(result.txData, 'blockheight')
206
+ assert.property(result.txData, 'isSlpTx')
207
+ assert.property(result.txData, 'tokenTxType')
208
+ assert.property(result.txData, 'tokenId')
209
+ assert.property(result.txData, 'tokenType')
210
+ assert.property(result.txData, 'tokenTicker')
211
+ assert.property(result.txData, 'tokenName')
212
+ assert.property(result.txData, 'tokenDecimals')
213
+ assert.property(result.txData, 'tokenUri')
214
+ assert.property(result.txData, 'tokenDocHash')
215
+ assert.property(result.txData, 'isValidSlp')
216
+ })
217
+ it('should throw an error for improper input', async () => {
218
+ try {
219
+ const txid = 12345
220
+
221
+ await bchjs.PsfSlpIndexer.tx(txid)
222
+ assert.equal(true, false, 'Unexpected result!')
223
+ } catch (err) {
224
+ // console.log(`err: `, err)
225
+ assert.include(err.message, 'Input txid must be a string.')
226
+ }
227
+ })
228
+ it('should handle axios error', async () => {
229
+ try {
230
+ // Stub the network call.
231
+ sandbox.stub(axios, 'post').throws(new Error('test error'))
232
+
233
+ const txid =
234
+ 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
235
+ await bchjs.PsfSlpIndexer.tx(txid)
236
+ // console.log(`result: ${JSON.stringify(result, null, 2)}`)
237
+ assert.equal(true, false, 'Unexpected result!')
238
+ } catch (err) {
239
+ assert.include(err.message, 'test error')
240
+ }
241
+ })
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
+
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)
255
+ }
256
+ })
257
+ })
258
+ })