@psf/bch-js 4.22.1 → 5.2.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/bch-js.js +0 -4
- package/src/psf-slp-indexer.js +82 -3
- package/src/slp/utils.js +1 -1
- package/src/transaction.js +11 -2
- package/src/utxo.js +219 -6
- package/test/integration/chains/bchn/psf-slp-indexer.integration.js +49 -2
- package/test/integration/chains/bchn/utxo-integration.js +179 -78
- package/test/integration/transaction-integration.js +58 -55
- package/test/unit/fixtures/psf-slp-indexer-mock.js +37 -14
- package/test/unit/fixtures/utxo-mocks.js +205 -1
- package/test/unit/psf-slp-indexer.js +93 -12
- package/test/unit/transaction-unit.js +19 -8
- package/test/unit/utxo-unit.js +117 -7
- package/src/ninsight.js +0 -319
- package/test/unit/fixtures/ninsight-mock.js +0 -170
- package/test/unit/ninsight.js +0 -255
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@psf/bch-js",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.2.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.139: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/bch-js.js
CHANGED
|
@@ -36,7 +36,6 @@ const DSProof = require('./dsproof')
|
|
|
36
36
|
const Ecash = require('./ecash')
|
|
37
37
|
|
|
38
38
|
// Indexers
|
|
39
|
-
const Ninsight = require('./ninsight')
|
|
40
39
|
const Electrumx = require('./electrumx')
|
|
41
40
|
const PsfSlpIndexer = require('./psf-slp-indexer')
|
|
42
41
|
|
|
@@ -83,9 +82,6 @@ class BCHJS {
|
|
|
83
82
|
|
|
84
83
|
// console.log(`apiToken: ${this.apiToken}`)
|
|
85
84
|
|
|
86
|
-
// Bitcoin.com Ninsight indexer
|
|
87
|
-
this.Ninsight = new Ninsight(config)
|
|
88
|
-
|
|
89
85
|
// ElectrumX indexer
|
|
90
86
|
this.Electrumx = new Electrumx(libConfig)
|
|
91
87
|
|
package/src/psf-slp-indexer.js
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
/*
|
|
2
2
|
This library interacts with the PSF slp indexer REST API endpoints operated
|
|
3
3
|
by FullStack.cash
|
|
4
|
+
|
|
5
|
+
TODO:
|
|
6
|
+
- detect TXs from tokens in the blacklist.
|
|
4
7
|
*/
|
|
8
|
+
|
|
5
9
|
// Public npm libraries
|
|
6
10
|
const axios = require('axios')
|
|
7
11
|
|
|
12
|
+
// Local libraries
|
|
13
|
+
const RawTransaction = require('./raw-transactions')
|
|
14
|
+
const SlpUtils = require('./slp/utils')
|
|
15
|
+
|
|
8
16
|
// let _this
|
|
9
17
|
|
|
10
18
|
class PsfSlpIndexer {
|
|
11
|
-
constructor (config) {
|
|
19
|
+
constructor (config = {}) {
|
|
12
20
|
this.restURL = config.restURL
|
|
13
21
|
this.apiToken = config.apiToken
|
|
14
22
|
this.authToken = config.authToken
|
|
@@ -29,6 +37,10 @@ class PsfSlpIndexer {
|
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
// Encapsulate dependencies
|
|
41
|
+
this.rawTransaction = new RawTransaction(config)
|
|
42
|
+
this.slpUtils = new SlpUtils(config)
|
|
43
|
+
|
|
32
44
|
// _this = this
|
|
33
45
|
}
|
|
34
46
|
|
|
@@ -120,6 +132,8 @@ class PsfSlpIndexer {
|
|
|
120
132
|
*/
|
|
121
133
|
async balance (address) {
|
|
122
134
|
try {
|
|
135
|
+
// console.log('balance() address: ', address)
|
|
136
|
+
|
|
123
137
|
// Handle single address.
|
|
124
138
|
if (typeof address === 'string') {
|
|
125
139
|
const response = await axios.post(
|
|
@@ -283,8 +297,73 @@ class PsfSlpIndexer {
|
|
|
283
297
|
}
|
|
284
298
|
throw new Error('Input txid must be a string.')
|
|
285
299
|
} catch (error) {
|
|
286
|
-
|
|
287
|
-
|
|
300
|
+
// console.log('error: ', error)
|
|
301
|
+
|
|
302
|
+
// Case: txid is not stored in the psf-slp-indexer tx database.
|
|
303
|
+
// Response: If it's not in the database, then it can be assumed the TX
|
|
304
|
+
// is not a token TX?
|
|
305
|
+
if (
|
|
306
|
+
error.response &&
|
|
307
|
+
error.response.data &&
|
|
308
|
+
error.response.data.error &&
|
|
309
|
+
error.response.data.error.includes('Key not found in database')
|
|
310
|
+
) {
|
|
311
|
+
// console.log(
|
|
312
|
+
// 'TX not found in psf-slp-indexer. Retrieving from full node.'
|
|
313
|
+
// )
|
|
314
|
+
|
|
315
|
+
// Check if this txid belongs to a blacklisted token.
|
|
316
|
+
const isInBlacklist = await this.checkBlacklist(txid)
|
|
317
|
+
|
|
318
|
+
// Get the TX Details from the full node.
|
|
319
|
+
const txDetails = await this.rawTransaction.getTxData(txid)
|
|
320
|
+
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
|
321
|
+
|
|
322
|
+
if (isInBlacklist) {
|
|
323
|
+
txDetails.isValidSlp = null
|
|
324
|
+
} else {
|
|
325
|
+
txDetails.isValidSlp = false
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const outObj = {
|
|
329
|
+
txData: txDetails
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return outObj
|
|
333
|
+
} else throw error
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Check if the txid has an OP_RETURN containing a tokenID that is in the
|
|
338
|
+
// blacklist. In that case, the isValidSlp property should be marked as
|
|
339
|
+
// null, and not false.
|
|
340
|
+
async checkBlacklist (txid) {
|
|
341
|
+
try {
|
|
342
|
+
// TODO: Add endpoint to psf-slp-indexer to retrieve current blacklist.
|
|
343
|
+
// This should be done once at startup, and not each time this function
|
|
344
|
+
// is called.
|
|
345
|
+
const blacklist = [
|
|
346
|
+
'dd21be4532d93661e8ffe16db6535af0fb8ee1344d1fef81a193e2b4cfa9fbc9'
|
|
347
|
+
]
|
|
348
|
+
|
|
349
|
+
const outTokenData = await this.slpUtils.decodeOpReturn(txid)
|
|
350
|
+
// console.log('outTokenData: ', outTokenData)
|
|
351
|
+
|
|
352
|
+
// Loop through each token in the blacklist.
|
|
353
|
+
for (let i = 0; i < blacklist.length; i++) {
|
|
354
|
+
// If a match is found, return true.
|
|
355
|
+
if (outTokenData.tokenId === blacklist[i]) {
|
|
356
|
+
return true
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// By default, return false.
|
|
361
|
+
return false
|
|
362
|
+
} catch (err) {
|
|
363
|
+
// console.log(err)
|
|
364
|
+
|
|
365
|
+
// Exit quietly.
|
|
366
|
+
return false
|
|
288
367
|
}
|
|
289
368
|
}
|
|
290
369
|
}
|
package/src/slp/utils.js
CHANGED
package/src/transaction.js
CHANGED
|
@@ -2,18 +2,27 @@
|
|
|
2
2
|
High-level functions for working with Transactions
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// Global npm libraries
|
|
5
6
|
const BigNumber = require('bignumber.js')
|
|
6
7
|
|
|
8
|
+
// Local libraries
|
|
7
9
|
const RawTransaction = require('./raw-transactions')
|
|
8
10
|
const SlpUtils = require('./slp/utils')
|
|
9
11
|
const Blockchain = require('./blockchain')
|
|
12
|
+
const PsfSlpIndexer = require('./psf-slp-indexer')
|
|
10
13
|
|
|
11
14
|
class Transaction {
|
|
12
|
-
constructor (config) {
|
|
15
|
+
constructor (config = {}) {
|
|
13
16
|
// Encapsulate dependencies
|
|
14
17
|
this.slpUtils = new SlpUtils(config)
|
|
15
18
|
this.rawTransaction = new RawTransaction(config)
|
|
16
19
|
this.blockchain = new Blockchain(config)
|
|
20
|
+
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Proxy the call to the psf-slp-indexer.
|
|
24
|
+
async get (txid) {
|
|
25
|
+
return await this.psfSlpIndexer.tx(txid)
|
|
17
26
|
}
|
|
18
27
|
|
|
19
28
|
/**
|
|
@@ -42,7 +51,7 @@ class Transaction {
|
|
|
42
51
|
// CT 10/31/21: TODO: this function should be refactored to use get2(), but
|
|
43
52
|
// add waterfall validation of the TX and its inputs.
|
|
44
53
|
|
|
45
|
-
async
|
|
54
|
+
async getOld (txid) {
|
|
46
55
|
try {
|
|
47
56
|
if (typeof txid !== 'string') {
|
|
48
57
|
throw new Error(
|
package/src/utxo.js
CHANGED
|
@@ -8,19 +8,23 @@
|
|
|
8
8
|
// Local libraries
|
|
9
9
|
const Electrumx = require('./electrumx')
|
|
10
10
|
const Slp = require('./slp/slp')
|
|
11
|
+
const PsfSlpIndexer = require('./psf-slp-indexer')
|
|
12
|
+
const BigNumber = require('bignumber.js')
|
|
11
13
|
|
|
12
14
|
class UTXO {
|
|
13
|
-
constructor (config) {
|
|
15
|
+
constructor (config = {}) {
|
|
14
16
|
// Encapsulate dependencies for easier mocking.
|
|
15
17
|
this.electrumx = new Electrumx(config)
|
|
16
18
|
this.slp = new Slp(config)
|
|
19
|
+
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
|
20
|
+
this.BigNumber = BigNumber
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
/**
|
|
20
|
-
* @api Utxo.
|
|
21
|
-
* @apiName
|
|
24
|
+
* @api Utxo.getOld() getOld()
|
|
25
|
+
* @apiName getOld
|
|
22
26
|
* @apiGroup UTXO
|
|
23
|
-
* @apiDescription Get UTXOs for an address
|
|
27
|
+
* @apiDescription Get UTXOs for an address (from SLPDB)
|
|
24
28
|
*
|
|
25
29
|
* Given an address, this function will return an object with thre following
|
|
26
30
|
* properties:
|
|
@@ -45,7 +49,7 @@ class UTXO {
|
|
|
45
49
|
* @apiExample Example usage:
|
|
46
50
|
* (async () => {
|
|
47
51
|
* try {
|
|
48
|
-
* let utxos = await bchjs.Utxo.
|
|
52
|
+
* let utxos = await bchjs.Utxo.getOld('simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj');
|
|
49
53
|
* console.log(utxos);
|
|
50
54
|
* } catch(error) {
|
|
51
55
|
* console.error(error)
|
|
@@ -181,7 +185,7 @@ class UTXO {
|
|
|
181
185
|
*
|
|
182
186
|
*
|
|
183
187
|
*/
|
|
184
|
-
async
|
|
188
|
+
async getOld (address, useWhitelist = false) {
|
|
185
189
|
try {
|
|
186
190
|
if (!address) {
|
|
187
191
|
throw new Error('Address must be an array or a string')
|
|
@@ -286,6 +290,215 @@ class UTXO {
|
|
|
286
290
|
}
|
|
287
291
|
}
|
|
288
292
|
|
|
293
|
+
/**
|
|
294
|
+
* @api Utxo.get() get()
|
|
295
|
+
* @apiName get
|
|
296
|
+
* @apiGroup UTXO
|
|
297
|
+
* @apiDescription Get UTXOs for an address (from psf-slp-indexer)
|
|
298
|
+
*
|
|
299
|
+
* Given an address, this function will return an object with thre following
|
|
300
|
+
* properties:
|
|
301
|
+
* - address: "" - the address these UTXOs are associated with
|
|
302
|
+
* - bchUtxos: [] - UTXOs confirmed to be spendable as normal BCH
|
|
303
|
+
* - nullUtxo: [] - UTXOs that did not pass SLP validation. Should be ignored and
|
|
304
|
+
* not spent, to be safe.
|
|
305
|
+
* - slpUtxos: {} - UTXOs confirmed to be colored as valid SLP tokens
|
|
306
|
+
* - type1: {}
|
|
307
|
+
* - tokens: [] - SLP token Type 1 tokens.
|
|
308
|
+
* - mintBatons: [] - SLP token Type 1 mint batons.
|
|
309
|
+
* - nft: {}
|
|
310
|
+
* - tokens: [] - NFT tokens
|
|
311
|
+
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
|
|
312
|
+
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
|
|
313
|
+
*
|
|
314
|
+
*
|
|
315
|
+
* @apiExample Example usage:
|
|
316
|
+
* (async () => {
|
|
317
|
+
* try {
|
|
318
|
+
* let utxos = await bchjs.Utxo.get('simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj');
|
|
319
|
+
* console.log(utxos);
|
|
320
|
+
* } catch(error) {
|
|
321
|
+
* console.error(error)
|
|
322
|
+
* }
|
|
323
|
+
* })()
|
|
324
|
+
*
|
|
325
|
+
* // returns
|
|
326
|
+
* [
|
|
327
|
+
* {
|
|
328
|
+
* "address": "bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv",
|
|
329
|
+
* "bchUtxos": [
|
|
330
|
+
* {
|
|
331
|
+
* "height": 674513,
|
|
332
|
+
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
|
333
|
+
* "tx_pos": 3,
|
|
334
|
+
* "value": 38134,
|
|
335
|
+
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
|
336
|
+
* "vout": 3,
|
|
337
|
+
* "isValid": false
|
|
338
|
+
* }
|
|
339
|
+
* ],
|
|
340
|
+
*/
|
|
341
|
+
// This version of get() uses the psf-slp-indexer. It will replace the older
|
|
342
|
+
// get() function that uses SLPDB.
|
|
343
|
+
// TODO: NFT UTXOs are identified as non-token UTXOs, which will cause a wallet
|
|
344
|
+
// to burn them. The psf-slp-indexer needs to be updated to mark these UTXOs.
|
|
345
|
+
async get (address) {
|
|
346
|
+
try {
|
|
347
|
+
// Convert address to an array if it is a string.
|
|
348
|
+
if (typeof address !== 'string') {
|
|
349
|
+
throw new Error('address input must be a string')
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Ensure the address is a BCH address.
|
|
353
|
+
const addr = this.slp.Address.toCashAddress(address)
|
|
354
|
+
|
|
355
|
+
// Get the UTXOs associated with the address.
|
|
356
|
+
const utxoData = await this.electrumx.utxo(addr)
|
|
357
|
+
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
|
|
358
|
+
const utxos = utxoData.utxos
|
|
359
|
+
|
|
360
|
+
let slpUtxos = []
|
|
361
|
+
|
|
362
|
+
// Get SLP UTXOs from the psf-slp-indexer
|
|
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
|
+
}
|
|
376
|
+
|
|
377
|
+
// Loop through the Fulcrum UTXOs.
|
|
378
|
+
for (let i = 0; i < utxos.length; i++) {
|
|
379
|
+
const thisUtxo = utxos[i]
|
|
380
|
+
|
|
381
|
+
// Loop through the UTXOs from psf-slp-indexer.
|
|
382
|
+
for (let j = 0; j < slpUtxos.length; j++) {
|
|
383
|
+
const thisSlpUtxo = slpUtxos[j]
|
|
384
|
+
|
|
385
|
+
// If the non-hydrated UTXO matches the SLP UTXO, then combine the data
|
|
386
|
+
// and mark the UTXO as an SLP token.
|
|
387
|
+
if (
|
|
388
|
+
thisUtxo.tx_hash === thisSlpUtxo.txid &&
|
|
389
|
+
thisUtxo.tx_pos === thisSlpUtxo.vout
|
|
390
|
+
) {
|
|
391
|
+
thisUtxo.txid = thisUtxo.tx_hash
|
|
392
|
+
thisUtxo.vout = thisUtxo.tx_pos
|
|
393
|
+
thisUtxo.isSlp = true
|
|
394
|
+
thisUtxo.type = thisSlpUtxo.type
|
|
395
|
+
thisUtxo.qty = thisSlpUtxo.qty
|
|
396
|
+
thisUtxo.tokenId = thisSlpUtxo.tokenId
|
|
397
|
+
thisUtxo.address = thisSlpUtxo.address
|
|
398
|
+
|
|
399
|
+
break
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// If there was no match, then this is a normal BCH UTXO. Mark it as such.
|
|
404
|
+
if (!thisUtxo.isSlp) {
|
|
405
|
+
thisUtxo.txid = thisUtxo.tx_hash
|
|
406
|
+
thisUtxo.vout = thisUtxo.tx_pos
|
|
407
|
+
thisUtxo.isSlp = false
|
|
408
|
+
thisUtxo.address = addr
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Get token UTXOs
|
|
413
|
+
let type1TokenUtxos = utxos.filter(
|
|
414
|
+
x => x.isSlp === true && x.type === 'token'
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
// Hydrate the UTXOs with additional token data.
|
|
418
|
+
type1TokenUtxos = await this.hydrateTokenData(type1TokenUtxos)
|
|
419
|
+
|
|
420
|
+
const bchUtxos = utxos.filter(x => x.isSlp === false)
|
|
421
|
+
const type1BatonUtxos = utxos.filter(
|
|
422
|
+
x => x.isSlp === true && x.type === 'baton'
|
|
423
|
+
)
|
|
424
|
+
const nullUtxos = utxos.filter(x => x.isSlp === null)
|
|
425
|
+
|
|
426
|
+
const outObj = {
|
|
427
|
+
address: addr,
|
|
428
|
+
bchUtxos,
|
|
429
|
+
slpUtxos: {
|
|
430
|
+
type1: {
|
|
431
|
+
tokens: type1TokenUtxos,
|
|
432
|
+
mintBatons: type1BatonUtxos
|
|
433
|
+
},
|
|
434
|
+
nft: {} // Allocated for future support of NFT spec.
|
|
435
|
+
},
|
|
436
|
+
nullUtxos
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return outObj
|
|
440
|
+
} catch (err) {
|
|
441
|
+
// console.error('Error in bchjs.utxo.get2(): ', err)
|
|
442
|
+
|
|
443
|
+
if (err.error) throw new Error(err.error)
|
|
444
|
+
throw err
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Hydrate an array of token UTXOs with token information.
|
|
449
|
+
// Returns an array of token UTXOs with additional data.
|
|
450
|
+
async hydrateTokenData (utxoAry) {
|
|
451
|
+
try {
|
|
452
|
+
// console.log('utxoAry: ', utxoAry)
|
|
453
|
+
|
|
454
|
+
// Create a list of token IDs without duplicates.
|
|
455
|
+
let tokenIds = utxoAry.map(x => x.tokenId)
|
|
456
|
+
|
|
457
|
+
// Remove duplicates. https://stackoverflow.com/questions/9229645/remove-duplicate-values-from-js-array
|
|
458
|
+
tokenIds = [...new Set(tokenIds)]
|
|
459
|
+
// console.log('tokenIds: ', tokenIds)
|
|
460
|
+
|
|
461
|
+
// Get Genesis data for each tokenId
|
|
462
|
+
const genesisData = []
|
|
463
|
+
for (let i = 0; i < tokenIds.length; i++) {
|
|
464
|
+
const thisTokenId = tokenIds[i]
|
|
465
|
+
const thisTokenData = await this.psfSlpIndexer.tokenStats(thisTokenId)
|
|
466
|
+
// console.log('thisTokenData: ', thisTokenData)
|
|
467
|
+
|
|
468
|
+
genesisData.push(thisTokenData)
|
|
469
|
+
}
|
|
470
|
+
// console.log('genesisData: ', genesisData)
|
|
471
|
+
|
|
472
|
+
// Hydrate each token UTXO with data from the genesis transaction.
|
|
473
|
+
for (let i = 0; i < utxoAry.length; i++) {
|
|
474
|
+
const thisUtxo = utxoAry[i]
|
|
475
|
+
|
|
476
|
+
// Get the genesis data for this token.
|
|
477
|
+
const genData = genesisData.filter(
|
|
478
|
+
x => x.tokenData.tokenId === thisUtxo.tokenId
|
|
479
|
+
)
|
|
480
|
+
// console.log('genData: ', genData)
|
|
481
|
+
|
|
482
|
+
thisUtxo.ticker = genData[0].tokenData.ticker
|
|
483
|
+
thisUtxo.name = genData[0].tokenData.name
|
|
484
|
+
thisUtxo.documentUri = genData[0].tokenData.documentUri
|
|
485
|
+
thisUtxo.documentHash = genData[0].tokenData.documentHash
|
|
486
|
+
thisUtxo.decimals = genData[0].tokenData.decimals
|
|
487
|
+
|
|
488
|
+
// Calculate the real token quantity
|
|
489
|
+
const qty = new BigNumber(thisUtxo.qty).dividedBy(
|
|
490
|
+
10 ** parseInt(thisUtxo.decimals)
|
|
491
|
+
)
|
|
492
|
+
thisUtxo.qtyStr = qty.toString()
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return utxoAry
|
|
496
|
+
} catch (err) {
|
|
497
|
+
console.log('Error in hydrateTokenData()')
|
|
498
|
+
throw err
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
289
502
|
/**
|
|
290
503
|
* @api Utxo.findBiggestUtxo() findBiggestUtxo()
|
|
291
504
|
* @apiName findBiggestUtxo
|
|
@@ -54,9 +54,9 @@ describe('#psf-slp-indexer', () => {
|
|
|
54
54
|
})
|
|
55
55
|
|
|
56
56
|
describe('#tx', () => {
|
|
57
|
-
it('should get hydrated tx data', async () => {
|
|
57
|
+
it('should get hydrated tx data for an SLP transaction', async () => {
|
|
58
58
|
const txid =
|
|
59
|
-
'
|
|
59
|
+
'947ccb2a0d62ca287bc4b0993874ab0f9f6afd454193e631e2bf84dca66731fc'
|
|
60
60
|
|
|
61
61
|
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
|
62
62
|
// console.log('result: ', result)
|
|
@@ -64,6 +64,53 @@ describe('#psf-slp-indexer', () => {
|
|
|
64
64
|
assert.property(result.txData, 'vin')
|
|
65
65
|
assert.property(result.txData, 'vout')
|
|
66
66
|
assert.property(result.txData, 'isValidSlp')
|
|
67
|
+
assert.equal(result.txData.isValidSlp, true)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should mark non-SLP transaction as false', async () => {
|
|
71
|
+
const txid =
|
|
72
|
+
'03d6e6b63647ce7b02ecc73dc6d41b485be14a3e20eed4474b8a840358ddf14e'
|
|
73
|
+
|
|
74
|
+
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
|
75
|
+
// console.log('result: ', result)
|
|
76
|
+
|
|
77
|
+
assert.property(result.txData, 'vin')
|
|
78
|
+
assert.property(result.txData, 'vout')
|
|
79
|
+
assert.property(result.txData, 'isValidSlp')
|
|
80
|
+
assert.equal(result.txData.isValidSlp, false)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// FlexUSD transactions
|
|
84
|
+
// Currently FlexUSD UTXOs are reported as invalid SLP UTXOs, which means
|
|
85
|
+
// the wallet will burn them. There is a TODO in the code. This test will
|
|
86
|
+
// need to be changed when it is done.
|
|
87
|
+
it('should mark blacklisted token as null', async () => {
|
|
88
|
+
const txid =
|
|
89
|
+
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
|
90
|
+
|
|
91
|
+
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
|
92
|
+
// console.log('result: ', result)
|
|
93
|
+
|
|
94
|
+
assert.property(result.txData, 'vin')
|
|
95
|
+
assert.property(result.txData, 'vout')
|
|
96
|
+
assert.property(result.txData, 'isValidSlp')
|
|
97
|
+
assert.equal(result.txData.isValidSlp, null)
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should throw error for non-existent txid', async () => {
|
|
101
|
+
try {
|
|
102
|
+
const txid =
|
|
103
|
+
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85e'
|
|
104
|
+
|
|
105
|
+
await bchjs.PsfSlpIndexer.tx(txid)
|
|
106
|
+
// console.log('result: ', result)
|
|
107
|
+
|
|
108
|
+
assert.fail('Unexpected code path')
|
|
109
|
+
} catch (err) {
|
|
110
|
+
// console.log(err)
|
|
111
|
+
|
|
112
|
+
assert.include(err.message, 'No such mempool or blockchain transaction')
|
|
113
|
+
}
|
|
67
114
|
})
|
|
68
115
|
})
|
|
69
116
|
})
|