@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
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
|
+
})
|