@psf/bch-js 5.2.1 → 5.3.1
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 +2 -1
- package/src/util.js +50 -0
- package/src/utxo.js +27 -9
- package/test/integration/chains/abc/utxo-integration.js +5 -3
- package/test/integration/chains/bchn/transaction-integration.js +28 -0
- package/test/integration/chains/bchn/utxo-integration.js +116 -91
- package/test/unit/fixtures/utxo-mocks.js +22 -1
- package/test/unit/utxo-unit.js +32 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@psf/bch-js",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.1",
|
|
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
|
@@ -132,7 +132,8 @@ class PsfSlpIndexer {
|
|
|
132
132
|
*/
|
|
133
133
|
async balance (address) {
|
|
134
134
|
try {
|
|
135
|
-
console.log('balance() address: ', address)
|
|
135
|
+
// console.log('balance() address: ', address)
|
|
136
|
+
|
|
136
137
|
// Handle single address.
|
|
137
138
|
if (typeof address === 'string') {
|
|
138
139
|
const response = await axios.post(
|
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
|
@@ -311,10 +311,6 @@ class UTXO {
|
|
|
311
311
|
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
|
|
312
312
|
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
|
|
313
313
|
*
|
|
314
|
-
* Note: You can pass in an optional second Boolean argument. The default
|
|
315
|
-
* `false` will use the normal waterfall validation method. Set to `true`,
|
|
316
|
-
* SLP UTXOs will be validated with the whitelist filtered SLPDB. This will
|
|
317
|
-
* result is many more UTXOs in the `nullUtxos` array.
|
|
318
314
|
*
|
|
319
315
|
* @apiExample Example usage:
|
|
320
316
|
* (async () => {
|
|
@@ -361,10 +357,22 @@ class UTXO {
|
|
|
361
357
|
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
|
|
362
358
|
const utxos = utxoData.utxos
|
|
363
359
|
|
|
360
|
+
let slpUtxos = []
|
|
361
|
+
|
|
364
362
|
// Get SLP UTXOs from the psf-slp-indexer
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
363
|
+
try {
|
|
364
|
+
const slpUtxoData = await this.psfSlpIndexer.balance(addr)
|
|
365
|
+
// console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
|
|
366
|
+
|
|
367
|
+
slpUtxos = slpUtxoData.balance.utxos
|
|
368
|
+
} catch (err) {
|
|
369
|
+
// console.log('err: ', err)
|
|
370
|
+
|
|
371
|
+
// Exit quietly if address has no SLP UTXOs. Otherwise, throw the error.
|
|
372
|
+
if (err.error && !err.error.includes('Key not found in database')) {
|
|
373
|
+
throw err
|
|
374
|
+
}
|
|
375
|
+
}
|
|
368
376
|
|
|
369
377
|
// Loop through the Fulcrum UTXOs.
|
|
370
378
|
for (let i = 0; i < utxos.length; i++) {
|
|
@@ -396,8 +404,18 @@ class UTXO {
|
|
|
396
404
|
if (!thisUtxo.isSlp) {
|
|
397
405
|
thisUtxo.txid = thisUtxo.tx_hash
|
|
398
406
|
thisUtxo.vout = thisUtxo.tx_pos
|
|
399
|
-
thisUtxo.isSlp = false
|
|
400
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}`)
|
|
401
419
|
}
|
|
402
420
|
}
|
|
403
421
|
|
|
@@ -430,7 +448,7 @@ class UTXO {
|
|
|
430
448
|
|
|
431
449
|
return outObj
|
|
432
450
|
} catch (err) {
|
|
433
|
-
|
|
451
|
+
console.error('Error in bchjs.Utxo.get(): ', err)
|
|
434
452
|
|
|
435
453
|
if (err.error) throw new Error(err.error)
|
|
436
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) {
|
|
@@ -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
|
+
})
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
const assert = require('chai').assert
|
|
6
6
|
|
|
7
7
|
const BCHJS = require('../../../../src/bch-js')
|
|
8
|
-
const bchjs = new BCHJS()
|
|
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 () => {
|
|
@@ -13,84 +14,113 @@ describe('#UTXO', () => {
|
|
|
13
14
|
|
|
14
15
|
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
|
15
16
|
})
|
|
16
|
-
/*
|
|
17
|
-
describe('#get', () => {
|
|
18
|
-
it('should get hydrated and filtered UTXOs for an address', async () => {
|
|
19
|
-
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
|
20
|
-
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
if (process.env.TESTSLP) {
|
|
19
|
+
describe('#getOld', () => {
|
|
20
|
+
it('should get hydrated and filtered UTXOs for an address', async () => {
|
|
21
|
+
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
|
22
|
+
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
|
23
|
+
|
|
24
|
+
const result = await bchjs.Utxo.getOld(addr)
|
|
25
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
26
|
+
|
|
27
|
+
assert.isArray(result)
|
|
28
|
+
assert.property(result[0], 'address')
|
|
29
|
+
assert.property(result[0], 'bchUtxos')
|
|
30
|
+
assert.property(result[0], 'nullUtxos')
|
|
31
|
+
assert.property(result[0], 'slpUtxos')
|
|
32
|
+
assert.isArray(result[0].bchUtxos)
|
|
33
|
+
assert.isArray(result[0].nullUtxos)
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('should handle an array of addresses', async () => {
|
|
37
|
+
const addr = [
|
|
38
|
+
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
|
39
|
+
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
const result = await bchjs.Utxo.getOld(addr)
|
|
43
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
44
|
+
|
|
45
|
+
assert.isArray(result)
|
|
46
|
+
assert.property(result[0], 'address')
|
|
47
|
+
assert.property(result[0], 'bchUtxos')
|
|
48
|
+
assert.property(result[0], 'nullUtxos')
|
|
49
|
+
assert.property(result[0], 'slpUtxos')
|
|
50
|
+
assert.isArray(result[0].bchUtxos)
|
|
51
|
+
assert.isArray(result[0].nullUtxos)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should handle NFTs and minting batons', async () => {
|
|
55
|
+
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
|
56
|
+
|
|
57
|
+
const result = await bchjs.Utxo.getOld(addr)
|
|
58
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
59
|
+
|
|
60
|
+
assert.isArray(result)
|
|
61
|
+
assert.property(result[0], 'address')
|
|
62
|
+
assert.property(result[0], 'bchUtxos')
|
|
63
|
+
assert.property(result[0], 'nullUtxos')
|
|
64
|
+
assert.property(result[0], 'slpUtxos')
|
|
65
|
+
assert.isArray(result[0].bchUtxos)
|
|
66
|
+
assert.isArray(result[0].nullUtxos)
|
|
67
|
+
|
|
68
|
+
assert.isArray(result[0].slpUtxos.type1.mintBatons)
|
|
69
|
+
assert.isArray(result[0].slpUtxos.type1.tokens)
|
|
70
|
+
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
|
|
71
|
+
assert.isArray(result[0].slpUtxos.nft.groupTokens)
|
|
72
|
+
assert.isArray(result[0].slpUtxos.nft.tokens)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should use the whitelist when flag is set', async () => {
|
|
76
|
+
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
|
77
|
+
const useWhitelist = true
|
|
78
|
+
|
|
79
|
+
const result = await bchjs.Utxo.getOld(addr, useWhitelist)
|
|
80
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
81
|
+
|
|
82
|
+
assert.isArray(result)
|
|
83
|
+
assert.property(result[0], 'address')
|
|
84
|
+
assert.property(result[0], 'bchUtxos')
|
|
85
|
+
assert.property(result[0], 'nullUtxos')
|
|
86
|
+
assert.property(result[0], 'slpUtxos')
|
|
87
|
+
assert.isArray(result[0].bchUtxos)
|
|
88
|
+
assert.isArray(result[0].nullUtxos)
|
|
89
|
+
|
|
90
|
+
// Most token UTXOs should end up in the nullUtxos array.
|
|
91
|
+
assert.isAbove(result[0].bchUtxos.length, 0)
|
|
92
|
+
assert.isAbove(result[0].nullUtxos.length, 1)
|
|
93
|
+
})
|
|
32
94
|
})
|
|
33
95
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
'
|
|
37
|
-
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
|
38
|
-
]
|
|
96
|
+
describe('#findBiggestUtxo', () => {
|
|
97
|
+
it('should sort UTXOs from Electrumx', async () => {
|
|
98
|
+
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
|
39
99
|
|
|
40
|
-
|
|
41
|
-
|
|
100
|
+
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
|
|
101
|
+
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
|
|
42
102
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
assert.property(result[0], 'bchUtxos')
|
|
46
|
-
assert.property(result[0], 'nullUtxos')
|
|
47
|
-
assert.property(result[0], 'slpUtxos')
|
|
48
|
-
assert.isArray(result[0].bchUtxos)
|
|
49
|
-
assert.isArray(result[0].nullUtxos)
|
|
50
|
-
})
|
|
103
|
+
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
|
|
104
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
51
105
|
|
|
52
|
-
|
|
53
|
-
|
|
106
|
+
assert.property(result, 'satoshis')
|
|
107
|
+
assert.equal(result.satoshis, 800)
|
|
108
|
+
})
|
|
54
109
|
|
|
55
|
-
|
|
56
|
-
|
|
110
|
+
it('should sort UTXOs from Utxos.get()', async () => {
|
|
111
|
+
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
|
57
112
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
assert.property(result[0], 'bchUtxos')
|
|
61
|
-
assert.property(result[0], 'nullUtxos')
|
|
62
|
-
assert.property(result[0], 'slpUtxos')
|
|
63
|
-
assert.isArray(result[0].bchUtxos)
|
|
64
|
-
assert.isArray(result[0].nullUtxos)
|
|
65
|
-
|
|
66
|
-
assert.isArray(result[0].slpUtxos.type1.mintBatons)
|
|
67
|
-
assert.isArray(result[0].slpUtxos.type1.tokens)
|
|
68
|
-
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
|
|
69
|
-
assert.isArray(result[0].slpUtxos.nft.groupTokens)
|
|
70
|
-
assert.isArray(result[0].slpUtxos.nft.tokens)
|
|
71
|
-
})
|
|
113
|
+
const utxos = await bchjs.Utxo.getOld(addr)
|
|
114
|
+
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
|
72
115
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const useWhitelist = true
|
|
116
|
+
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
|
|
117
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
76
118
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
assert.isArray(result)
|
|
81
|
-
assert.property(result[0], 'address')
|
|
82
|
-
assert.property(result[0], 'bchUtxos')
|
|
83
|
-
assert.property(result[0], 'nullUtxos')
|
|
84
|
-
assert.property(result[0], 'slpUtxos')
|
|
85
|
-
assert.isArray(result[0].bchUtxos)
|
|
86
|
-
assert.isArray(result[0].nullUtxos)
|
|
87
|
-
|
|
88
|
-
// Most token UTXOs should end up in the nullUtxos array.
|
|
89
|
-
assert.isAbove(result[0].bchUtxos.length, 0)
|
|
90
|
-
assert.isAbove(result[0].nullUtxos.length, 1)
|
|
119
|
+
assert.property(result, 'satoshis')
|
|
120
|
+
assert.equal(result.satoshis, 800)
|
|
121
|
+
})
|
|
91
122
|
})
|
|
92
|
-
}
|
|
93
|
-
*/
|
|
123
|
+
}
|
|
94
124
|
|
|
95
125
|
describe('#hydrateTokenData', () => {
|
|
96
126
|
it('should hydrate token UTXOs', async () => {
|
|
@@ -138,7 +168,13 @@ describe('#UTXO', () => {
|
|
|
138
168
|
]
|
|
139
169
|
|
|
140
170
|
const result = await bchjs.Utxo.hydrateTokenData(utxos)
|
|
141
|
-
console.log('result: ', result)
|
|
171
|
+
// console.log('result: ', result)
|
|
172
|
+
|
|
173
|
+
assert.property(result[0], 'ticker')
|
|
174
|
+
assert.property(result[0], 'name')
|
|
175
|
+
assert.property(result[0], 'qtyStr')
|
|
176
|
+
assert.property(result[0], 'documentUri')
|
|
177
|
+
assert.property(result[0], 'documentHash')
|
|
142
178
|
})
|
|
143
179
|
})
|
|
144
180
|
|
|
@@ -164,46 +200,35 @@ describe('#UTXO', () => {
|
|
|
164
200
|
|
|
165
201
|
// TODO: NFTs are currently not identified as different than normal BCH UTXOs.
|
|
166
202
|
// The psf-slp-indexer needs to be updated to fix this issue.
|
|
167
|
-
it('should handle
|
|
203
|
+
it('should handle minting batons', async () => {
|
|
168
204
|
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
|
169
205
|
|
|
170
206
|
const result = await bchjs.Utxo.get(addr)
|
|
171
|
-
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
207
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
172
208
|
|
|
173
209
|
// Assert that minting batons are correctly identified.
|
|
174
210
|
assert.isAbove(result.slpUtxos.type1.mintBatons.length, 0)
|
|
175
211
|
})
|
|
176
|
-
})
|
|
177
212
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
it('should sort UTXOs from Electrumx', async () => {
|
|
181
|
-
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
|
213
|
+
it('should return UTXOs for address with no SLP tokens', async () => {
|
|
214
|
+
const addr = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
|
|
182
215
|
|
|
183
|
-
const
|
|
184
|
-
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
|
|
185
|
-
|
|
186
|
-
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
|
|
216
|
+
const result = await bchjs.Utxo.get(addr)
|
|
187
217
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
188
218
|
|
|
189
|
-
assert.
|
|
190
|
-
assert.equal(result.
|
|
219
|
+
assert.isAbove(result.bchUtxos.length, 0)
|
|
220
|
+
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
|
191
221
|
})
|
|
192
222
|
|
|
193
|
-
it('should
|
|
194
|
-
const addr = 'bitcoincash:
|
|
223
|
+
it('should handle Group NFTs', async () => {
|
|
224
|
+
const addr = 'bitcoincash:qrnghwrfgccf3s5e9wnglzxegcnhje9rkcwv2eka33'
|
|
195
225
|
|
|
196
|
-
const
|
|
197
|
-
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
|
198
|
-
|
|
199
|
-
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
|
|
226
|
+
const result = await bchjs.Utxo.get(addr)
|
|
200
227
|
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
201
228
|
|
|
202
|
-
assert.
|
|
203
|
-
assert.equal(result.satoshis, 800)
|
|
229
|
+
assert.isAbove(result.nullUtxos.length, 0)
|
|
204
230
|
})
|
|
205
231
|
})
|
|
206
|
-
*/
|
|
207
232
|
})
|
|
208
233
|
|
|
209
234
|
function sleep (ms) {
|
|
@@ -328,6 +328,19 @@ const fulcrumUtxos01 = {
|
|
|
328
328
|
]
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
const fulcrumUtxos02 = {
|
|
332
|
+
success: true,
|
|
333
|
+
utxos: [
|
|
334
|
+
{
|
|
335
|
+
height: 674513,
|
|
336
|
+
tx_hash:
|
|
337
|
+
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
|
338
|
+
tx_pos: 3,
|
|
339
|
+
value: 38134
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
}
|
|
343
|
+
|
|
331
344
|
const psfSlpIndexerUtxos01 = {
|
|
332
345
|
balance: {
|
|
333
346
|
utxos: [
|
|
@@ -457,6 +470,12 @@ const genesisData03 = {
|
|
|
457
470
|
}
|
|
458
471
|
}
|
|
459
472
|
|
|
473
|
+
const noUtxoErr = {
|
|
474
|
+
success: false,
|
|
475
|
+
error:
|
|
476
|
+
'Key not found in database [bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7]'
|
|
477
|
+
}
|
|
478
|
+
|
|
460
479
|
module.exports = {
|
|
461
480
|
mockUtxoData,
|
|
462
481
|
mockHydratedUtxos,
|
|
@@ -464,9 +483,11 @@ module.exports = {
|
|
|
464
483
|
mockEveryUtxoType,
|
|
465
484
|
electrumxUtxos,
|
|
466
485
|
fulcrumUtxos01,
|
|
486
|
+
fulcrumUtxos02,
|
|
467
487
|
psfSlpIndexerUtxos01,
|
|
468
488
|
tokenUtxos01,
|
|
469
489
|
genesisData01,
|
|
470
490
|
genesisData02,
|
|
471
|
-
genesisData03
|
|
491
|
+
genesisData03,
|
|
492
|
+
noUtxoErr
|
|
472
493
|
}
|
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
|
|
|
@@ -272,5 +276,33 @@ describe('#utxo', () => {
|
|
|
272
276
|
assert.equal(result.slpUtxos.type1.mintBatons.length, 1)
|
|
273
277
|
assert.equal(result.nullUtxos.length, 0)
|
|
274
278
|
})
|
|
279
|
+
|
|
280
|
+
it('should handle an address with no SLP UTXOs', async () => {
|
|
281
|
+
// mock dependencies
|
|
282
|
+
sandbox
|
|
283
|
+
.stub(bchjs.Utxo.electrumx, 'utxo')
|
|
284
|
+
.resolves(mockData.fulcrumUtxos02)
|
|
285
|
+
sandbox
|
|
286
|
+
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
|
|
287
|
+
.resolves({ txData: { isValidSlp: false } })
|
|
288
|
+
|
|
289
|
+
// Force psf-slp-indexer to return no UTXOs
|
|
290
|
+
sandbox
|
|
291
|
+
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
|
292
|
+
.rejects(mockData.noUtxoErr)
|
|
293
|
+
|
|
294
|
+
// Mock function to return the same input. Good enough for this test.
|
|
295
|
+
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(() => [])
|
|
296
|
+
|
|
297
|
+
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
|
298
|
+
|
|
299
|
+
const result = await bchjs.Utxo.get(addr)
|
|
300
|
+
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
301
|
+
|
|
302
|
+
assert.equal(result.bchUtxos.length, 1)
|
|
303
|
+
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
|
304
|
+
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
|
|
305
|
+
assert.equal(result.nullUtxos.length, 0)
|
|
306
|
+
})
|
|
275
307
|
})
|
|
276
308
|
})
|