@psf/bch-js 5.2.0 → 5.2.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
CHANGED
package/src/utxo.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
const Electrumx = require('./electrumx')
|
|
10
10
|
const Slp = require('./slp/slp')
|
|
11
11
|
const PsfSlpIndexer = require('./psf-slp-indexer')
|
|
12
|
+
const BigNumber = require('bignumber.js')
|
|
12
13
|
|
|
13
14
|
class UTXO {
|
|
14
15
|
constructor (config = {}) {
|
|
@@ -16,6 +17,7 @@ class UTXO {
|
|
|
16
17
|
this.electrumx = new Electrumx(config)
|
|
17
18
|
this.slp = new Slp(config)
|
|
18
19
|
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
|
20
|
+
this.BigNumber = BigNumber
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -399,10 +401,15 @@ class UTXO {
|
|
|
399
401
|
}
|
|
400
402
|
}
|
|
401
403
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
+
// Get token UTXOs
|
|
405
|
+
let type1TokenUtxos = utxos.filter(
|
|
404
406
|
x => x.isSlp === true && x.type === 'token'
|
|
405
407
|
)
|
|
408
|
+
|
|
409
|
+
// Hydrate the UTXOs with additional token data.
|
|
410
|
+
type1TokenUtxos = await this.hydrateTokenData(type1TokenUtxos)
|
|
411
|
+
|
|
412
|
+
const bchUtxos = utxos.filter(x => x.isSlp === false)
|
|
406
413
|
const type1BatonUtxos = utxos.filter(
|
|
407
414
|
x => x.isSlp === true && x.type === 'baton'
|
|
408
415
|
)
|
|
@@ -430,6 +437,60 @@ class UTXO {
|
|
|
430
437
|
}
|
|
431
438
|
}
|
|
432
439
|
|
|
440
|
+
// Hydrate an array of token UTXOs with token information.
|
|
441
|
+
// Returns an array of token UTXOs with additional data.
|
|
442
|
+
async hydrateTokenData (utxoAry) {
|
|
443
|
+
try {
|
|
444
|
+
// console.log('utxoAry: ', utxoAry)
|
|
445
|
+
|
|
446
|
+
// Create a list of token IDs without duplicates.
|
|
447
|
+
let tokenIds = utxoAry.map(x => x.tokenId)
|
|
448
|
+
|
|
449
|
+
// Remove duplicates. https://stackoverflow.com/questions/9229645/remove-duplicate-values-from-js-array
|
|
450
|
+
tokenIds = [...new Set(tokenIds)]
|
|
451
|
+
// console.log('tokenIds: ', tokenIds)
|
|
452
|
+
|
|
453
|
+
// Get Genesis data for each tokenId
|
|
454
|
+
const genesisData = []
|
|
455
|
+
for (let i = 0; i < tokenIds.length; i++) {
|
|
456
|
+
const thisTokenId = tokenIds[i]
|
|
457
|
+
const thisTokenData = await this.psfSlpIndexer.tokenStats(thisTokenId)
|
|
458
|
+
// console.log('thisTokenData: ', thisTokenData)
|
|
459
|
+
|
|
460
|
+
genesisData.push(thisTokenData)
|
|
461
|
+
}
|
|
462
|
+
// console.log('genesisData: ', genesisData)
|
|
463
|
+
|
|
464
|
+
// Hydrate each token UTXO with data from the genesis transaction.
|
|
465
|
+
for (let i = 0; i < utxoAry.length; i++) {
|
|
466
|
+
const thisUtxo = utxoAry[i]
|
|
467
|
+
|
|
468
|
+
// Get the genesis data for this token.
|
|
469
|
+
const genData = genesisData.filter(
|
|
470
|
+
x => x.tokenData.tokenId === thisUtxo.tokenId
|
|
471
|
+
)
|
|
472
|
+
// console.log('genData: ', genData)
|
|
473
|
+
|
|
474
|
+
thisUtxo.ticker = genData[0].tokenData.ticker
|
|
475
|
+
thisUtxo.name = genData[0].tokenData.name
|
|
476
|
+
thisUtxo.documentUri = genData[0].tokenData.documentUri
|
|
477
|
+
thisUtxo.documentHash = genData[0].tokenData.documentHash
|
|
478
|
+
thisUtxo.decimals = genData[0].tokenData.decimals
|
|
479
|
+
|
|
480
|
+
// Calculate the real token quantity
|
|
481
|
+
const qty = new BigNumber(thisUtxo.qty).dividedBy(
|
|
482
|
+
10 ** parseInt(thisUtxo.decimals)
|
|
483
|
+
)
|
|
484
|
+
thisUtxo.qtyStr = qty.toString()
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return utxoAry
|
|
488
|
+
} catch (err) {
|
|
489
|
+
console.log('Error in hydrateTokenData()')
|
|
490
|
+
throw err
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
433
494
|
/**
|
|
434
495
|
* @api Utxo.findBiggestUtxo() findBiggestUtxo()
|
|
435
496
|
* @apiName findBiggestUtxo
|
|
@@ -91,6 +91,57 @@ describe('#UTXO', () => {
|
|
|
91
91
|
})
|
|
92
92
|
})
|
|
93
93
|
*/
|
|
94
|
+
|
|
95
|
+
describe('#hydrateTokenData', () => {
|
|
96
|
+
it('should hydrate token UTXOs', async () => {
|
|
97
|
+
const utxos = [
|
|
98
|
+
{
|
|
99
|
+
txid:
|
|
100
|
+
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
|
101
|
+
vout: 1,
|
|
102
|
+
type: 'token',
|
|
103
|
+
qty: '10000000',
|
|
104
|
+
tokenId:
|
|
105
|
+
'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
|
106
|
+
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
txid:
|
|
110
|
+
'4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
|
111
|
+
vout: 1,
|
|
112
|
+
type: 'token',
|
|
113
|
+
qty: '100000000',
|
|
114
|
+
tokenId:
|
|
115
|
+
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
|
116
|
+
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
txid:
|
|
120
|
+
'42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
|
121
|
+
vout: 5,
|
|
122
|
+
type: 'token',
|
|
123
|
+
qty: '4764',
|
|
124
|
+
tokenId:
|
|
125
|
+
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
|
126
|
+
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
txid:
|
|
130
|
+
'06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
|
131
|
+
vout: 5,
|
|
132
|
+
type: 'token',
|
|
133
|
+
qty: '238',
|
|
134
|
+
tokenId:
|
|
135
|
+
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
|
136
|
+
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
|
|
140
|
+
const result = await bchjs.Utxo.hydrateTokenData(utxos)
|
|
141
|
+
console.log('result: ', result)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
94
145
|
describe('#get', () => {
|
|
95
146
|
it('should hydrate address with BCH and SLP UTXOs', async () => {
|
|
96
147
|
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
|
@@ -117,12 +168,13 @@ describe('#UTXO', () => {
|
|
|
117
168
|
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
|
118
169
|
|
|
119
170
|
const result = await bchjs.Utxo.get(addr)
|
|
120
|
-
|
|
171
|
+
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
|
121
172
|
|
|
122
173
|
// Assert that minting batons are correctly identified.
|
|
123
174
|
assert.isAbove(result.slpUtxos.type1.mintBatons.length, 0)
|
|
124
175
|
})
|
|
125
176
|
})
|
|
177
|
+
|
|
126
178
|
/*
|
|
127
179
|
describe('#findBiggestUtxo', () => {
|
|
128
180
|
it('should sort UTXOs from Electrumx', async () => {
|
|
@@ -368,6 +368,95 @@ const psfSlpIndexerUtxos01 = {
|
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
370
|
|
|
371
|
+
const tokenUtxos01 = [
|
|
372
|
+
{
|
|
373
|
+
txid: '384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
|
374
|
+
vout: 1,
|
|
375
|
+
type: 'token',
|
|
376
|
+
qty: '10000000',
|
|
377
|
+
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
|
378
|
+
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
txid: '4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
|
382
|
+
vout: 1,
|
|
383
|
+
type: 'token',
|
|
384
|
+
qty: '100000000',
|
|
385
|
+
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
|
386
|
+
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
txid: '42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
|
390
|
+
vout: 5,
|
|
391
|
+
type: 'token',
|
|
392
|
+
qty: '4764',
|
|
393
|
+
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
|
394
|
+
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
txid: '06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
|
398
|
+
vout: 5,
|
|
399
|
+
type: 'token',
|
|
400
|
+
qty: '238',
|
|
401
|
+
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
|
402
|
+
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
|
403
|
+
}
|
|
404
|
+
]
|
|
405
|
+
|
|
406
|
+
const genesisData01 = {
|
|
407
|
+
tokenData: {
|
|
408
|
+
type: 1,
|
|
409
|
+
ticker: 'sleven',
|
|
410
|
+
name: 'sleven',
|
|
411
|
+
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
|
412
|
+
documentUri: 'sleven',
|
|
413
|
+
documentHash: '',
|
|
414
|
+
decimals: 7,
|
|
415
|
+
mintBatonIsActive: false,
|
|
416
|
+
tokensInCirculationBN: '770059999999',
|
|
417
|
+
tokensInCirculationStr: '770059999999',
|
|
418
|
+
blockCreated: 555483,
|
|
419
|
+
totalBurned: '7711234568',
|
|
420
|
+
totalMinted: '777771234567'
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const genesisData02 = {
|
|
425
|
+
tokenData: {
|
|
426
|
+
type: 1,
|
|
427
|
+
ticker: 'NAKAMOTO',
|
|
428
|
+
name: 'NAKAMOTO',
|
|
429
|
+
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
|
430
|
+
documentUri: '',
|
|
431
|
+
documentHash: '',
|
|
432
|
+
decimals: 8,
|
|
433
|
+
mintBatonIsActive: false,
|
|
434
|
+
tokensInCirculationBN: '2099260968799900',
|
|
435
|
+
tokensInCirculationStr: '2099260968799900',
|
|
436
|
+
blockCreated: 555671,
|
|
437
|
+
totalBurned: '739031200100',
|
|
438
|
+
totalMinted: '2100000000000000'
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const genesisData03 = {
|
|
443
|
+
tokenData: {
|
|
444
|
+
type: 1,
|
|
445
|
+
ticker: 'AUDC',
|
|
446
|
+
name: 'AUD Coin',
|
|
447
|
+
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
|
448
|
+
documentUri: 'audcoino@gmail.com',
|
|
449
|
+
documentHash: '',
|
|
450
|
+
decimals: 6,
|
|
451
|
+
mintBatonIsActive: false,
|
|
452
|
+
tokensInCirculationBN: '974791786216512742',
|
|
453
|
+
tokensInCirculationStr: '974791786216512742',
|
|
454
|
+
blockCreated: 603311,
|
|
455
|
+
totalBurned: '1025208213783487258',
|
|
456
|
+
totalMinted: '2000000000000000000'
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
371
460
|
module.exports = {
|
|
372
461
|
mockUtxoData,
|
|
373
462
|
mockHydratedUtxos,
|
|
@@ -375,5 +464,9 @@ module.exports = {
|
|
|
375
464
|
mockEveryUtxoType,
|
|
376
465
|
electrumxUtxos,
|
|
377
466
|
fulcrumUtxos01,
|
|
378
|
-
psfSlpIndexerUtxos01
|
|
467
|
+
psfSlpIndexerUtxos01,
|
|
468
|
+
tokenUtxos01,
|
|
469
|
+
genesisData01,
|
|
470
|
+
genesisData02,
|
|
471
|
+
genesisData03
|
|
379
472
|
}
|
package/test/unit/utxo-unit.js
CHANGED
|
@@ -189,6 +189,45 @@ describe('#utxo', () => {
|
|
|
189
189
|
})
|
|
190
190
|
})
|
|
191
191
|
|
|
192
|
+
describe('#hydrateTokenData', () => {
|
|
193
|
+
it('should hydrate token UTXOs', async () => {
|
|
194
|
+
// Mock dependencies
|
|
195
|
+
sandbox
|
|
196
|
+
.stub(bchjs.Utxo.psfSlpIndexer, 'tokenStats')
|
|
197
|
+
.onCall(0)
|
|
198
|
+
.resolves(mockData.genesisData01)
|
|
199
|
+
.onCall(1)
|
|
200
|
+
.resolves(mockData.genesisData02)
|
|
201
|
+
.onCall(2)
|
|
202
|
+
.resolves(mockData.genesisData03)
|
|
203
|
+
|
|
204
|
+
const result = await bchjs.Utxo.hydrateTokenData(mockData.tokenUtxos01)
|
|
205
|
+
// console.log('result: ', result)
|
|
206
|
+
|
|
207
|
+
assert.equal(result.length, 4)
|
|
208
|
+
assert.property(result[0], 'qtyStr')
|
|
209
|
+
assert.property(result[0], 'ticker')
|
|
210
|
+
assert.property(result[0], 'name')
|
|
211
|
+
assert.property(result[0], 'documentUri')
|
|
212
|
+
assert.property(result[0], 'documentHash')
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
it('should should catch and throw errors', async () => {
|
|
216
|
+
try {
|
|
217
|
+
// Force error
|
|
218
|
+
sandbox
|
|
219
|
+
.stub(bchjs.Utxo.psfSlpIndexer, 'tokenStats')
|
|
220
|
+
.rejects(new Error('test error'))
|
|
221
|
+
|
|
222
|
+
await bchjs.Utxo.hydrateTokenData(mockData.tokenUtxos01)
|
|
223
|
+
|
|
224
|
+
assert.fail('Unexpected code path')
|
|
225
|
+
} catch (err) {
|
|
226
|
+
assert.equal(err.message, 'test error')
|
|
227
|
+
}
|
|
228
|
+
})
|
|
229
|
+
})
|
|
230
|
+
|
|
192
231
|
describe('#get', () => {
|
|
193
232
|
it('should throw an error if input is not a string', async () => {
|
|
194
233
|
try {
|
|
@@ -210,6 +249,8 @@ describe('#utxo', () => {
|
|
|
210
249
|
sandbox
|
|
211
250
|
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
|
212
251
|
.resolves(mockData.psfSlpIndexerUtxos01)
|
|
252
|
+
// Mock function to return the same input. Good enough for this test.
|
|
253
|
+
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x)
|
|
213
254
|
|
|
214
255
|
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
|
215
256
|
|