@psf/bch-js 5.2.2 → 5.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/electrumx.js +15 -2
- package/src/psf-slp-indexer.js +5 -3
- package/src/util.js +50 -0
- package/src/utxo.js +12 -2
- package/test/integration/chains/abc/utxo-integration.js +5 -3
- package/test/integration/chains/bchn/psf-slp-indexer.integration.js +12 -1
- package/test/integration/chains/bchn/transaction-integration.js +28 -0
- package/test/integration/chains/bchn/utxo-integration.js +11 -1
- package/test/unit/utxo-unit.js +7 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@psf/bch-js",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.2",
|
|
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": [
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"test:integration:local:abc": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
|
|
19
19
|
"test:integration:local:bchn": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
|
20
20
|
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v5/ mocha --timeout 30000 test/integration/chains/testnet",
|
|
21
|
-
"test:integration:decatur:bchn": "export RESTURL=http://192.168.2.
|
|
21
|
+
"test:integration:decatur:bchn": "export RESTURL=http://192.168.2.129:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
|
22
22
|
"test:integration:decatur:abc": "export RESTURL=http://192.168.2.141:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
|
|
23
23
|
"test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v5/ && mocha --timeout 30000 test/integration/",
|
|
24
24
|
"test:temp": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
|
package/src/electrumx.js
CHANGED
|
@@ -638,13 +638,24 @@ class ElectrumX {
|
|
|
638
638
|
// Sort confirmed Transactions by the block height
|
|
639
639
|
sortConfTxs (txs, sortingOrder = 'DESCENDING') {
|
|
640
640
|
try {
|
|
641
|
+
// console.log(`sortConfTxs txs: ${JSON.stringify(txs, null, 2)}`)
|
|
642
|
+
|
|
641
643
|
// Filter out unconfirmed transactions, with a height of 0 or less.
|
|
642
644
|
txs = txs.filter(elem => elem.height > 0)
|
|
643
645
|
|
|
644
646
|
if (sortingOrder === 'DESCENDING') {
|
|
645
|
-
|
|
647
|
+
// console.log('Sorting in descending order')
|
|
648
|
+
return txs.sort((a, b) => {
|
|
649
|
+
// console.log(`descending b.height: ${b.height}, a.height: ${a.height}`)
|
|
650
|
+
return b.height - a.height
|
|
651
|
+
})
|
|
646
652
|
}
|
|
647
|
-
|
|
653
|
+
|
|
654
|
+
// console.log('Sorting in ascending order')
|
|
655
|
+
return txs.sort((a, b) => {
|
|
656
|
+
// console.log(`ascending b.height: ${b.height}, a.height: ${a.height}`)
|
|
657
|
+
return a.height - b.height
|
|
658
|
+
})
|
|
648
659
|
} catch (err) {
|
|
649
660
|
console.log('Error in util.js/sortConfTxs()')
|
|
650
661
|
throw err
|
|
@@ -685,6 +696,8 @@ class ElectrumX {
|
|
|
685
696
|
// Substitute zero-conf txs with the current block-height + 1
|
|
686
697
|
async sortAllTxs (txs, sortingOrder = 'DESCENDING') {
|
|
687
698
|
try {
|
|
699
|
+
// console.log(`sortingOrder: ${sortingOrder}`)
|
|
700
|
+
|
|
688
701
|
// Calculate the height of the next block
|
|
689
702
|
const nextBlock = (await this.blockchain.getBlockCount()) + 1
|
|
690
703
|
|
package/src/psf-slp-indexer.js
CHANGED
|
@@ -155,11 +155,13 @@ class PsfSlpIndexer {
|
|
|
155
155
|
* @apiName Token Stats
|
|
156
156
|
* @apiGroup PSF SLP
|
|
157
157
|
* @apiDescription Return list stats for a single slp token.
|
|
158
|
+
* The second input is a Boolean, which determins the the transaction history
|
|
159
|
+
* of the token is included in the returned data. The default is false.
|
|
158
160
|
*
|
|
159
161
|
* @apiExample Example usage:
|
|
160
162
|
* (async () => {
|
|
161
163
|
* try {
|
|
162
|
-
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
|
|
164
|
+
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', true);
|
|
163
165
|
* console.log(tokenStats);
|
|
164
166
|
* } catch(error) {
|
|
165
167
|
* console.error(error)
|
|
@@ -194,13 +196,13 @@ class PsfSlpIndexer {
|
|
|
194
196
|
*
|
|
195
197
|
*/
|
|
196
198
|
|
|
197
|
-
async tokenStats (tokenId) {
|
|
199
|
+
async tokenStats (tokenId, withTxHistory = false) {
|
|
198
200
|
try {
|
|
199
201
|
// Handle single address.
|
|
200
202
|
if (typeof tokenId === 'string') {
|
|
201
203
|
const response = await axios.post(
|
|
202
204
|
`${this.restURL}psf/slp/token`,
|
|
203
|
-
{ tokenId },
|
|
205
|
+
{ tokenId, withTxHistory },
|
|
204
206
|
this.axiosOptions
|
|
205
207
|
)
|
|
206
208
|
return response.data
|
package/src/util.js
CHANGED
|
@@ -150,6 +150,56 @@ class Util {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
/**
|
|
154
|
+
* @api Util.chunk100() chunk100()
|
|
155
|
+
* @apiName chunk100
|
|
156
|
+
* @apiGroup Util
|
|
157
|
+
* @apiDescription chunk up an array into multiple arrays of 100 elements each.
|
|
158
|
+
* Input: arrayToSlice - a one-dimensional array of elements.
|
|
159
|
+
* Returns a two-dimensional array. An array of 100-element arrays.
|
|
160
|
+
*
|
|
161
|
+
* @apiExample Example usage:
|
|
162
|
+
* (async () => {
|
|
163
|
+
* try {
|
|
164
|
+
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,...,148, 149, 150]
|
|
165
|
+
*
|
|
166
|
+
* const chunked = bchjs.Util.chunk20(bigArray)
|
|
167
|
+
* console.log(chunked)
|
|
168
|
+
* } catch(error) {
|
|
169
|
+
* console.error(error)
|
|
170
|
+
* }
|
|
171
|
+
* })()
|
|
172
|
+
*
|
|
173
|
+
* // returns
|
|
174
|
+
* [
|
|
175
|
+
* [0,1,2,3,4,5,6,7,8,9,10,11,...,98,99],
|
|
176
|
+
* [100,101,102,...,148,149,150]
|
|
177
|
+
* ]
|
|
178
|
+
*/
|
|
179
|
+
chunk100 (arrayToSlice) {
|
|
180
|
+
try {
|
|
181
|
+
// Validate inputs
|
|
182
|
+
if (!Array.isArray(arrayToSlice)) {
|
|
183
|
+
throw new Error('input must be an array')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
let offset = 0
|
|
187
|
+
const result = []
|
|
188
|
+
|
|
189
|
+
// Loop over the array and slice off chunks of 100 elements.
|
|
190
|
+
while (offset < arrayToSlice.length) {
|
|
191
|
+
const chunk = arrayToSlice.slice(offset, offset + 100)
|
|
192
|
+
result.push(chunk)
|
|
193
|
+
offset = offset + 100
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return result
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error('Error in chunk100()')
|
|
199
|
+
throw err
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
153
203
|
/**
|
|
154
204
|
* @api Util.sleep() sleep()
|
|
155
205
|
* @apiName sleep
|
package/src/utxo.js
CHANGED
|
@@ -404,8 +404,18 @@ class UTXO {
|
|
|
404
404
|
if (!thisUtxo.isSlp) {
|
|
405
405
|
thisUtxo.txid = thisUtxo.tx_hash
|
|
406
406
|
thisUtxo.vout = thisUtxo.tx_pos
|
|
407
|
-
thisUtxo.isSlp = false
|
|
408
407
|
thisUtxo.address = addr
|
|
408
|
+
|
|
409
|
+
// Check the transaction to see if its a 'null' token, ignored by
|
|
410
|
+
// the indexer.
|
|
411
|
+
const txData = await this.psfSlpIndexer.tx(thisUtxo.tx_hash)
|
|
412
|
+
// console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
|
|
413
|
+
if (txData.txData.isValidSlp === null) {
|
|
414
|
+
thisUtxo.isSlp = null
|
|
415
|
+
} else {
|
|
416
|
+
thisUtxo.isSlp = false
|
|
417
|
+
}
|
|
418
|
+
// console.log(`thisUtxo.isSlp: ${thisUtxo.isSlp}`)
|
|
409
419
|
}
|
|
410
420
|
}
|
|
411
421
|
|
|
@@ -438,7 +448,7 @@ class UTXO {
|
|
|
438
448
|
|
|
439
449
|
return outObj
|
|
440
450
|
} catch (err) {
|
|
441
|
-
|
|
451
|
+
console.error('Error in bchjs.Utxo.get(): ', err)
|
|
442
452
|
|
|
443
453
|
if (err.error) throw new Error(err.error)
|
|
444
454
|
throw err
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
Integration tests for the utxo.js library.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const assert = require('chai').assert
|
|
5
|
+
// const assert = require('chai').assert
|
|
6
6
|
|
|
7
|
-
const BCHJS = require('../../../../src/bch-js')
|
|
8
|
-
const bchjs = new BCHJS()
|
|
7
|
+
// const BCHJS = require('../../../../src/bch-js')
|
|
8
|
+
// const bchjs = new BCHJS()
|
|
9
9
|
|
|
10
10
|
describe('#UTXO', () => {
|
|
11
11
|
beforeEach(async () => {
|
|
@@ -14,6 +14,7 @@ describe('#UTXO', () => {
|
|
|
14
14
|
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
+
/*
|
|
17
18
|
describe('#get', () => {
|
|
18
19
|
it('should get hydrated and filtered UTXOs for an address', async () => {
|
|
19
20
|
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
|
@@ -31,6 +32,7 @@ describe('#UTXO', () => {
|
|
|
31
32
|
assert.isArray(result[0].nullUtxos)
|
|
32
33
|
})
|
|
33
34
|
})
|
|
35
|
+
*/
|
|
34
36
|
})
|
|
35
37
|
|
|
36
38
|
function sleep (ms) {
|
|
@@ -40,13 +40,24 @@ describe('#psf-slp-indexer', () => {
|
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
describe('#tokenStats', () => {
|
|
43
|
-
it('should get stats on a token', async () => {
|
|
43
|
+
it('should get stats on a token, without tx history', async () => {
|
|
44
44
|
const tokenId =
|
|
45
45
|
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
|
46
46
|
|
|
47
47
|
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
|
48
48
|
// console.log('result: ', result)
|
|
49
49
|
|
|
50
|
+
assert.property(result.tokenData, 'documentUri')
|
|
51
|
+
assert.property(result.tokenData, 'totalBurned')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should get stats on a token, with tx history', async () => {
|
|
55
|
+
const tokenId =
|
|
56
|
+
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
|
57
|
+
|
|
58
|
+
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId, true)
|
|
59
|
+
// console.log('result: ', result)
|
|
60
|
+
|
|
50
61
|
assert.property(result.tokenData, 'documentUri')
|
|
51
62
|
assert.property(result.tokenData, 'txs')
|
|
52
63
|
assert.property(result.tokenData, 'totalBurned')
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Integration tests for the transaction.js library.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const assert = require('chai').assert
|
|
6
|
+
const BCHJS = require('../../../../src/bch-js')
|
|
7
|
+
const bchjs = new BCHJS()
|
|
8
|
+
|
|
9
|
+
describe('#Transaction', () => {
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
describe('#get', () => {
|
|
15
|
+
it('should get a tx details for a non-SLP TX with an OP_RETURN', async () => {
|
|
16
|
+
const txid =
|
|
17
|
+
'01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
|
|
18
|
+
|
|
19
|
+
const result = await bchjs.Transaction.get(txid)
|
|
20
|
+
// console.log('result: ', result)
|
|
21
|
+
|
|
22
|
+
assert.property(result.txData, 'txid')
|
|
23
|
+
assert.property(result.txData, 'vin')
|
|
24
|
+
assert.property(result.txData, 'vout')
|
|
25
|
+
assert.equal(result.txData.isValidSlp, false)
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -6,6 +6,7 @@ const assert = require('chai').assert
|
|
|
6
6
|
|
|
7
7
|
const BCHJS = require('../../../../src/bch-js')
|
|
8
8
|
const bchjs = new BCHJS()
|
|
9
|
+
// const bchjs = new BCHJS({ restURL: 'http://192.168.2.129:3000/v5/' })
|
|
9
10
|
|
|
10
11
|
describe('#UTXO', () => {
|
|
11
12
|
beforeEach(async () => {
|
|
@@ -199,7 +200,7 @@ describe('#UTXO', () => {
|
|
|
199
200
|
|
|
200
201
|
// TODO: NFTs are currently not identified as different than normal BCH UTXOs.
|
|
201
202
|
// The psf-slp-indexer needs to be updated to fix this issue.
|
|
202
|
-
it('should handle
|
|
203
|
+
it('should handle minting batons', async () => {
|
|
203
204
|
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
|
204
205
|
|
|
205
206
|
const result = await bchjs.Utxo.get(addr)
|
|
@@ -218,6 +219,15 @@ describe('#UTXO', () => {
|
|
|
218
219
|
assert.isAbove(result.bchUtxos.length, 0)
|
|
219
220
|
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
|
220
221
|
})
|
|
222
|
+
|
|
223
|
+
it('should handle Group NFTs', async () => {
|
|
224
|
+
const addr = 'bitcoincash:qrnghwrfgccf3s5e9wnglzxegcnhje9rkcwv2eka33'
|
|
225
|
+
|
|
226
|
+
const result = await bchjs.Utxo.get(addr)
|
|
227
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
228
|
+
|
|
229
|
+
assert.isAbove(result.nullUtxos.length, 0)
|
|
230
|
+
})
|
|
221
231
|
})
|
|
222
232
|
})
|
|
223
233
|
|
package/test/unit/utxo-unit.js
CHANGED
|
@@ -249,6 +249,10 @@ describe('#utxo', () => {
|
|
|
249
249
|
sandbox
|
|
250
250
|
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
|
251
251
|
.resolves(mockData.psfSlpIndexerUtxos01)
|
|
252
|
+
sandbox
|
|
253
|
+
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
|
|
254
|
+
.resolves({ txData: { isValidSlp: false } })
|
|
255
|
+
|
|
252
256
|
// Mock function to return the same input. Good enough for this test.
|
|
253
257
|
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x)
|
|
254
258
|
|
|
@@ -278,6 +282,9 @@ describe('#utxo', () => {
|
|
|
278
282
|
sandbox
|
|
279
283
|
.stub(bchjs.Utxo.electrumx, 'utxo')
|
|
280
284
|
.resolves(mockData.fulcrumUtxos02)
|
|
285
|
+
sandbox
|
|
286
|
+
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
|
|
287
|
+
.resolves({ txData: { isValidSlp: false } })
|
|
281
288
|
|
|
282
289
|
// Force psf-slp-indexer to return no UTXOs
|
|
283
290
|
sandbox
|