@psf/bch-js 4.20.24 → 4.20.28

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/LICENSE.md CHANGED
@@ -1,7 +1,8 @@
1
- Copyright 2021 Chris Troutner
1
+ Copyright 2021 Permissionless Software Foundation contributors
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
4
 
5
5
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software
6
6
 
7
7
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@psf/bch-js",
3
- "version": "4.20.24",
3
+ "version": "4.20.28",
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,8 @@
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.0.36:3000/v5/ && mocha --timeout 30000 test/integration/",
21
+ "test:integration:decatur:bchn": "export RESTURL=http://192.168.0.36:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
22
+ "test:integration:decatur:abc": "export RESTURL=http://192.168.0.38:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
22
23
  "test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v5/ && mocha --timeout 30000 test/integration/",
23
24
  "test:temp": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
24
25
  "test:temp2": "mocha --timeout=30000 -g '#TransactionLib' test/unit/",
@@ -68,20 +69,19 @@
68
69
  "assert": "2.0.0",
69
70
  "chai": "4.1.2",
70
71
  "coveralls": "3.0.2",
71
- "eslint": "^8.1.0",
72
- "eslint-config-prettier": "6.0.0",
73
- "eslint-config-standard": "14.1.0",
74
- "eslint-plugin-node": "9.1.0",
75
- "eslint-plugin-prettier": "3.1.0",
72
+ "eslint": "7.17.0",
73
+ "eslint-config-prettier": "7.1.0",
74
+ "eslint-config-standard": "16.0.3",
75
+ "eslint-plugin-node": "11.1.0",
76
+ "eslint-plugin-prettier": "3.3.1",
76
77
  "eslint-plugin-standard": "4.0.0",
77
78
  "husky": "^4.3.8",
78
79
  "lodash.clonedeep": "4.5.0",
79
80
  "mocha": "9.1.3",
80
81
  "node-mocks-http": "1.7.0",
81
82
  "nyc": "15.1.0",
82
- "prettier": "1.18.2",
83
83
  "semantic-release": "18.0.0",
84
- "sinon": "7.3.2",
84
+ "sinon": "9.2.2",
85
85
  "standard": "^16.0.4"
86
86
  },
87
87
  "apidoc": {
package/src/address.js CHANGED
@@ -223,7 +223,7 @@ class Address {
223
223
  * @api Address.hash160ToCash() hash160ToCash()
224
224
  * @apiName hash160ToCash
225
225
  * @apiGroup Address
226
- * @apiDescription Convert hash160 to cash address.
226
+ * @apiDescription Convert hash160 to cash address. Accepts either hexadecimal or buffer.
227
227
  *
228
228
  * @apiExample Example usage:
229
229
  * bchjs.Address.hash160ToCash("573d93b475be4f1925f3b74ed951201b0147eac1")
@@ -794,13 +794,13 @@ class Address {
794
794
  * @apiDescription Detect an addess from an OutputScript..
795
795
  *
796
796
  * @apiExample Example usage:
797
- * const script = bchjs.Script.encode([
797
+ * const scriptBuffer = bchjs.Script.encode([
798
798
  * Buffer.from("BOX", "ascii"),
799
799
  * bchjs.Script.opcodes.OP_CAT,
800
800
  * Buffer.from("BITBOX", "ascii"),
801
801
  * bchjs.Script.opcodes.OP_EQUAL
802
802
  * ]);
803
- * const p2sh_hash160 = bchjs.Crypto.hash160(script);
803
+ * const p2sh_hash160 = bchjs.Crypto.hash160(scriptBuffer);
804
804
  * const scriptPubKey = bchjs.Script.scriptHash.output.encode(p2sh_hash160);
805
805
  *
806
806
  * // mainnet address from output script
package/src/crypto.js CHANGED
@@ -22,7 +22,7 @@ class Crypto {
22
22
  * // buffer from hex
23
23
  * let buffer = Buffer.from('03123464075c7a5fa6b8680afa2c962a02e7bf071c6b2395b0ac711d462cac9354', 'hex')
24
24
  * bchjs.Crypto.sha256(buffer)
25
- * // <Buffer 26 b0 78 0a 68 3a 1e 09 8e 9c b8 cf a1 b0 92 42 28 25 00 97>
25
+ * // <Buffer 97 8c 09 dd 46 09 1d 19 22 fa 01 e9 f4 a9 75 b9 1a 37 1f 26 ba 83 99 de 27 d5 38 01 15 21 21 de>
26
26
  *
27
27
  * */
28
28
  // Translate address from any address format into a specific format.
@@ -60,7 +60,7 @@ class Crypto {
60
60
  * @api Crypto.hash256() hash256()
61
61
  * @apiName hash256
62
62
  * @apiGroup Crypto
63
- * @apiDescription Utility for creating double sha256 hash digests of data.
63
+ * @apiDescription Utility for creating double sha256 hash digests of buffer encoded data.
64
64
  *
65
65
  * @apiExample Example usage:
66
66
  * // buffer from hex
@@ -86,7 +86,7 @@ class Crypto {
86
86
  * @api Crypto.hash160() hash160()
87
87
  * @apiName hash160
88
88
  * @apiGroup Crypto
89
- * @apiDescription Utility for creating ripemd160(sha256()) hash digests of data.
89
+ * @apiDescription Utility for creating ripemd160(sha256()) hash digests of buffer encoded data.
90
90
  *
91
91
  * @apiExample Example usage:
92
92
  * // buffer from hex
package/src/electrumx.js CHANGED
@@ -142,7 +142,7 @@ class ElectrumX {
142
142
  * (async () => {
143
143
  * try {
144
144
  * let balance = await bchjs.Electrumx.balance('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf');
145
- * console.log(utxo);
145
+ * console.log(balance);
146
146
  * } catch(error) {
147
147
  * console.error(error)
148
148
  * }
@@ -159,7 +159,7 @@ class ElectrumX {
159
159
  * (async () => {
160
160
  * try {
161
161
  * let balance = await bchjs.Electrumx.balance(['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v']);
162
- * console.log(utxo);
162
+ * console.log(balance);
163
163
  * } catch(error) {
164
164
  * console.error(error)
165
165
  * }
package/src/price.js CHANGED
@@ -58,7 +58,7 @@ class Price {
58
58
  * let current = await bchjs.Price.getUsd();
59
59
  * console.log(current);
60
60
  * } catch(err) {
61
- * console.err(err)
61
+ * console.error(err)
62
62
  * }
63
63
  *})()
64
64
  *
@@ -94,7 +94,7 @@ class Price {
94
94
  * let current = await bchjs.Price.rates();
95
95
  * console.log(current);
96
96
  * } catch(err) {
97
- * console.err(err)
97
+ * console.error(err)
98
98
  * }
99
99
  *})()
100
100
  *
@@ -137,7 +137,7 @@ class Price {
137
137
  * let current = await bchjs.Price.getBchaUsd();
138
138
  * console.log(current);
139
139
  * } catch(err) {
140
- * console.err(err)
140
+ * console.error(err)
141
141
  * }
142
142
  *})()
143
143
  *
@@ -175,7 +175,7 @@ class Price {
175
175
  * let current = await bchjs.Price.getXecUsd();
176
176
  * console.log(current);
177
177
  * } catch(err) {
178
- * console.err(err)
178
+ * console.error(err)
179
179
  * }
180
180
  *})()
181
181
  *
@@ -210,7 +210,7 @@ class Price {
210
210
  * let current = await bchjs.Price.getBchUsd();
211
211
  * console.log(current);
212
212
  * } catch(err) {
213
- * console.err(err)
213
+ * console.error(err)
214
214
  * }
215
215
  *})()
216
216
  *
@@ -225,8 +225,8 @@ class Transaction {
225
225
  }
226
226
 
227
227
  /**
228
- * @api Transaction.get2() get2()
229
- * @apiName get2
228
+ * @api Transaction.get3() get3()
229
+ * @apiName get3
230
230
  * @apiGroup Transaction
231
231
  * @apiDescription
232
232
  * Returns an object of transaction data, including addresses for input UTXOs.
@@ -248,7 +248,7 @@ class Transaction {
248
248
  * }
249
249
  * })()
250
250
  */
251
- async get2 (txid) {
251
+ async get3 (txid) {
252
252
  try {
253
253
  if (typeof txid !== 'string') {
254
254
  throw new Error(
@@ -256,6 +256,7 @@ class Transaction {
256
256
  )
257
257
  }
258
258
 
259
+ // Get TX data
259
260
  const txDetails = await this.rawTransaction.getTxData(txid)
260
261
  // console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
261
262
 
@@ -264,35 +265,61 @@ class Transaction {
264
265
  txDetails.blockhash
265
266
  )
266
267
  txDetails.blockheight = blockHeader.height
268
+ // console.log(`blockHeader: ${JSON.stringify(blockHeader, null, 2)}`)
269
+
270
+ // Set default as not an SLP tx
271
+ txDetails.isSlpTx = false
272
+
273
+ // Get Token Data
274
+ const txTokenData = await this.getTokenInfo(txid)
275
+ // console.log(`txTokenData: ${JSON.stringify(txTokenData, null, 2)}`)
276
+
277
+ // If not a token, return the tx data. Processing is complete.
278
+ if (!txTokenData) return txDetails
279
+
280
+ // Mark TX as an SLP tx. This does not mean it's valid, it just means
281
+ // the OP_RETURN passes a basic check.
282
+ txDetails.isSlpTx = true
283
+
284
+ // Get Genesis data
285
+ const genesisData = await this.getTokenInfo(txTokenData.tokenId)
286
+ // console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
287
+
288
+ // Add token information to the tx details object.
289
+ txDetails.tokenTxType = txTokenData.txType
290
+ txDetails.tokenId = txTokenData.tokenId
291
+ txDetails.tokenTicker = genesisData.ticker
292
+ txDetails.tokenName = genesisData.name
293
+ txDetails.tokenDecimals = genesisData.decimals
294
+ txDetails.tokenUri = genesisData.documentUri
295
+ txDetails.tokenDocHash = genesisData.documentHash
296
+ // console.log(`txDetails before processing input and outputs: ${JSON.stringify(txDetails, null, 2)}`)
297
+
298
+ // Process TX Outputs
299
+ // Add the token quantity to each output.
300
+ // 'i' starts at 1, because vout[0] is the OP_RETURN
301
+ for (let i = 0; i < txDetails.vout.length; i++) {
302
+ const thisVout = txDetails.vout[i]
303
+ if (txTokenData.txType === 'SEND') {
304
+ // console.log(
305
+ // `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
306
+ // )
307
+
308
+ // First output is OP_RETURN, so tokenQty is null.
309
+ if (i === 0) {
310
+ thisVout.tokenQty = null
311
+ thisVout.tokenQtyStr = null
312
+ continue
313
+ }
267
314
 
268
- // First get the token information for the output. If that fails, then
269
- // this is not an SLP transaction, and this method can return false.
270
- let outTokenData
271
- try {
272
- outTokenData = await this.slpUtils.decodeOpReturn(txid)
273
- // console.log(`outTokenData: ${JSON.stringify(outTokenData, null, 2)}`)
274
-
275
- // Get Genesis data for this token.
276
- const genesisData = await this.slpUtils.decodeOpReturn(
277
- outTokenData.tokenId
278
- // decodeOpReturnCache
279
- // usrObj // pass user data when making an internal call.
280
- )
281
- // console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
282
-
283
- // Add token information to the tx details object.
284
- txDetails.tokenTxType = outTokenData.txType
285
- txDetails.tokenId = outTokenData.tokenId
286
- txDetails.tokenTicker = genesisData.ticker
287
- txDetails.tokenName = genesisData.name
288
- txDetails.tokenDecimals = genesisData.decimals
289
- txDetails.tokenUri = genesisData.documentUri
290
- txDetails.tokenDocHash = genesisData.documentHash
315
+ // Non SLP outputs.
316
+ if (i > txTokenData.amounts.length) {
317
+ thisVout.tokenQty = null
318
+ thisVout.tokenQtyStr = null
319
+ continue
320
+ }
291
321
 
292
- // Add the token quantity to each output.
293
- for (let i = 0; i < outTokenData.amounts.length; i++) {
294
- const rawQty = outTokenData.amounts[i]
295
- // const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
322
+ const rawQty = txTokenData.amounts[i - 1]
296
323
 
297
324
  // Calculate the real quantity using a BigNumber, then convert it to a
298
325
  // floating point number.
@@ -302,110 +329,179 @@ class Transaction {
302
329
  realQty = realQty.toString()
303
330
  // realQty = parseFloat(realQty)
304
331
 
305
- txDetails.vout[i + 1].tokenQtyStr = realQty
306
- txDetails.vout[i + 1].tokenQty = parseFloat(realQty)
307
- }
308
-
309
- // Add tokenQty = null to any outputs that don't have a value.
310
- for (let i = 0; i < txDetails.vout.length; i++) {
311
- const thisVout = txDetails.vout[i]
312
-
313
- if (!thisVout.tokenQty && thisVout.tokenQty !== 0) {
314
- thisVout.tokenQty = null
332
+ txDetails.vout[i].tokenQtyStr = realQty
333
+ txDetails.vout[i].tokenQty = parseFloat(realQty)
334
+
335
+ // console.log(
336
+ // `thisVout ${i}: ${JSON.stringify(txDetails.vout[i], null, 2)}`
337
+ // )
338
+ } else if (
339
+ txTokenData.txType === 'GENESIS' ||
340
+ txTokenData.txType === 'MINT'
341
+ ) {
342
+ // console.log(
343
+ // `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
344
+ // )
345
+
346
+ let tokenQty = 0 // Default value
347
+
348
+ // Only vout[1] of a Genesis or Mint transaction represents the tokens.
349
+ // Any other outputs in that transaction are normal BCH UTXOs.
350
+ if (i === 1) {
351
+ tokenQty = txTokenData.qty
352
+ // console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
353
+
354
+ // Calculate the real quantity using a BigNumber, then convert it to a
355
+ // floating point number.
356
+ let realQty = new BigNumber(tokenQty).dividedBy(
357
+ 10 ** parseInt(txDetails.tokenDecimals)
358
+ )
359
+ realQty = realQty.toString()
360
+ // realQty = parseFloat(realQty)
361
+
362
+ thisVout.tokenQtyStr = realQty
363
+ thisVout.tokenQty = parseFloat(realQty)
364
+ // console.log(`thisVout[${i}]: ${JSON.stringify(thisVout, null, 2)}`)
365
+ } else if (i === txTokenData.mintBatonVout) {
366
+ // Optional Mint baton
367
+ thisVout.tokenQtyStr = '0'
368
+ thisVout.tokenQty = 0
369
+ thisVout.isMintBaton = true
370
+ } else {
371
+ thisVout.tokenQtyStr = '0'
372
+ thisVout.tokenQty = 0
315
373
  }
374
+ } else {
375
+ throw new Error('Unknown SLP TX type for TX')
316
376
  }
377
+ }
317
378
 
318
- // Loop through each input and retrieve the token data.
319
- for (let i = 0; i < txDetails.vin.length; i++) {
320
- const thisVin = txDetails.vin[i]
321
- // console.log(`thisVin: ${JSON.stringify(thisVin, null, 2)}`)
322
-
323
- try {
324
- // If decodeOpReturn() throws an error, then this input is not
325
- // from an SLP transaction and can be ignored.
326
- const inTokenData = await this.slpUtils.decodeOpReturn(thisVin.txid)
327
- // console.log(
328
- // `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
329
- // )
330
-
331
- let tokenQty = 0
332
-
333
- if (inTokenData.txType === 'SEND') {
334
- // Get the appropriate vout token amount. This may throw an error,
335
- // which means this Vin is not actually a token UTXO, it was just
336
- // associated with a previous token TX.
337
- tokenQty = inTokenData.amounts[thisVin.vout - 1]
338
- // console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
339
-
340
- //
341
- } else if (inTokenData.txType === 'GENESIS') {
342
- // Only vout[1] of a Genesis transaction represents the tokens.
343
- // Any other outputs in that transaction are normal BCH UTXOs.
344
- if (thisVin.vout === 1) {
345
- tokenQty = inTokenData.qty
346
- // console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
347
- }
348
- } else if (inTokenData.txType === 'MINT') {
349
- // vout=1 (second output) recieves the newly minted tokens.
350
- if (thisVin.vout === 1) {
351
- tokenQty = inTokenData.qty
352
- } else {
353
- tokenQty = null
354
- }
379
+ // Process TX inputs
380
+ for (let i = 0; i < txDetails.vin.length; i++) {
381
+ const thisVin = txDetails.vin[i]
382
+ // console.log(`thisVin[${i}]: ${JSON.stringify(thisVin, null, 2)}`)
383
+
384
+ const vinTokenData = await this.getTokenInfo(thisVin.txid)
385
+ // console.log(
386
+ // `vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
387
+ // )
388
+
389
+ // Corner case: Ensure the token ID is the same.
390
+ const vinTokenIdIsTheSame = vinTokenData.tokenId === txDetails.tokenId
391
+
392
+ // If the input is not a token input, or if the tokenID is not the same,
393
+ // then mark the token output as null.
394
+ if (!vinTokenData || !vinTokenIdIsTheSame) {
395
+ thisVin.tokenQty = 0
396
+ thisVin.tokenQtyStr = '0'
397
+ thisVin.tokenId = null
398
+ continue
399
+ }
355
400
 
356
- //
357
- } else {
358
- console.log(
359
- 'Unexpected code path in Transaction.get2(). What is the txType?'
360
- )
361
- console.log(inTokenData)
362
- throw new Error('Unexpected code path')
363
- }
401
+ if (vinTokenData.txType === 'SEND') {
402
+ // console.log(
403
+ // `SEND vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
404
+ // )
364
405
 
365
- if (tokenQty) {
366
- // Calculate the real quantity using a BigNumber, then convert it to a
367
- // floating point number.
368
- let realQty = new BigNumber(tokenQty).dividedBy(
369
- 10 ** parseInt(txDetails.tokenDecimals)
370
- )
371
- realQty = realQty.toString()
372
- // realQty = parseFloat(realQty)
406
+ const tokenQty = vinTokenData.amounts[thisVin.vout - 1]
407
+ // console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
373
408
 
374
- thisVin.tokenQtyStr = realQty
375
- thisVin.tokenQty = parseFloat(realQty)
376
- // txDetails.vin[i].tokenQty = tokenQty
409
+ // Calculate the real quantity using a BigNumber, then convert it to a
410
+ // floating point number.
411
+ let realQty = new BigNumber(tokenQty).dividedBy(
412
+ 10 ** parseInt(txDetails.tokenDecimals)
413
+ )
414
+ realQty = realQty.toString()
415
+ // realQty = parseFloat(realQty)
377
416
 
378
- // Add token ID to input
379
- thisVin.tokenId = inTokenData.tokenId
380
- } else {
381
- thisVin.tokenQty = null
382
- thisVin.tokenQtyStr = null
383
- }
384
- } catch (err) {
385
- // If decodeOpReturn() throws an error, then this input is not
386
- // from an SLP transaction and can be ignored.
387
- // thisVin.tokenQty = null
388
- thisVin.tokenQty = null
389
- thisVin.tokenQtyStr = null
390
- continue
417
+ thisVin.tokenQtyStr = realQty
418
+ thisVin.tokenQty = parseFloat(realQty)
419
+ thisVin.tokenId = vinTokenData.tokenId
420
+ } else if (vinTokenData.txType === 'MINT') {
421
+ // console.log(
422
+ // `MINT vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
423
+ // )
424
+
425
+ let tokenQty = 0 // Default value
426
+
427
+ // Only vout[1] of a Genesis transaction represents the tokens.
428
+ // Any other outputs in that transaction are normal BCH UTXOs.
429
+ if (thisVin.vout === 1) {
430
+ tokenQty = vinTokenData.qty
431
+ // console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
432
+
433
+ // Calculate the real quantity using a BigNumber, then convert it to a
434
+ // floating point number.
435
+ let realQty = new BigNumber(tokenQty).dividedBy(
436
+ 10 ** parseInt(txDetails.tokenDecimals)
437
+ )
438
+ realQty = realQty.toString()
439
+ // realQty = parseFloat(realQty)
440
+
441
+ thisVin.tokenQtyStr = realQty
442
+ thisVin.tokenQty = parseFloat(realQty)
443
+ thisVin.tokenId = vinTokenData.tokenId
444
+ } else if (thisVin.vout === vinTokenData.mintBatonVout) {
445
+ // Optional Mint baton
446
+ thisVin.tokenQtyStr = '0'
447
+ thisVin.tokenQty = 0
448
+ thisVin.tokenId = vinTokenData.tokenId
449
+ thisVin.isMintBaton = true
450
+ } else {
451
+ thisVin.tokenQtyStr = '0'
452
+ thisVin.tokenQty = 0
453
+ thisVin.tokenId = null
391
454
  }
455
+ } else if (vinTokenData.txType === 'GENESIS') {
456
+ // console.log(
457
+ // `GENESIS vinTokenData ${i}: ${JSON.stringify(
458
+ // vinTokenData,
459
+ // null,
460
+ // 2
461
+ // )}`
462
+ // )
463
+
464
+ let tokenQty = 0 // Default value
465
+
466
+ // Only vout[1] of a Genesis transaction represents the tokens.
467
+ // Any other outputs in that transaction are normal BCH UTXOs.
468
+ if (thisVin.vout === 1) {
469
+ tokenQty = vinTokenData.qty
470
+ // console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
471
+
472
+ // Calculate the real quantity using a BigNumber, then convert it to a
473
+ // floating point number.
474
+ let realQty = new BigNumber(tokenQty).dividedBy(
475
+ 10 ** parseInt(txDetails.tokenDecimals)
476
+ )
477
+ realQty = realQty.toString()
478
+ // realQty = parseFloat(realQty)
479
+
480
+ thisVin.tokenQtyStr = realQty
481
+ thisVin.tokenQty = parseFloat(realQty)
482
+ thisVin.tokenId = vinTokenData.tokenId
483
+ } else if (thisVin.vout === vinTokenData.mintBatonVout) {
484
+ // Optional Mint baton
485
+ thisVin.tokenQtyStr = '0'
486
+ thisVin.tokenQty = 0
487
+ thisVin.tokenId = vinTokenData.tokenId
488
+ thisVin.isMintBaton = true
489
+ } else {
490
+ thisVin.tokenQtyStr = '0'
491
+ thisVin.tokenQty = 0
492
+ thisVin.tokenId = null
493
+ }
494
+ } else {
495
+ console.log(
496
+ `Unknown vinTokenData: ${JSON.stringify(vinTokenData, null, 2)}`
497
+ )
498
+ throw new Error('Unknown token type in input')
392
499
  }
393
- } catch (err) {
394
- // console.log('Error: ', err)
395
-
396
- // This case handles rate limit errors.
397
- if (err.response && err.response.data && err.response.data.error) {
398
- throw new Error(err.response.data.error)
399
- }
400
-
401
- // If decoding the op_return fails, then it's not an SLP transaction,
402
- // and the non-hyrated TX details can be returned.
403
- return txDetails
404
500
  }
405
501
 
406
502
  return txDetails
407
503
  } catch (err) {
408
- // console.error('Error in transactions.js/get(): ', err)
504
+ console.error('Error in get3()')
409
505
 
410
506
  // This case handles rate limit errors.
411
507
  if (err.response && err.response.data && err.response.data.error) {
@@ -416,6 +512,17 @@ class Transaction {
416
512
  throw err
417
513
  }
418
514
  }
515
+
516
+ // A wrapper for decodeOpReturn(). Returns false if txid is not an SLP tx.
517
+ // Returns the token data if the txid is an SLP tx.
518
+ async getTokenInfo (txid) {
519
+ try {
520
+ const tokenData = await this.slpUtils.decodeOpReturn(txid)
521
+ return tokenData
522
+ } catch (err) {
523
+ return false
524
+ }
525
+ }
419
526
  }
420
527
 
421
528
  module.exports = Transaction
package/src/utxo.js CHANGED
@@ -183,6 +183,10 @@ class UTXO {
183
183
  */
184
184
  async get (address, useWhitelist = false) {
185
185
  try {
186
+ if (!address) {
187
+ throw new Error('Address must be an array or a string')
188
+ }
189
+
186
190
  // Convert address to an array if it is a string.
187
191
  if (typeof address === 'string') address = [address]
188
192
 
@@ -5,7 +5,7 @@
5
5
 
6
6
  const chai = require('chai')
7
7
  const assert = chai.assert
8
- const BCHJS = require('../../src/bch-js')
8
+ const BCHJS = require('../../../../src/bch-js')
9
9
  const bchjs = new BCHJS()
10
10
 
11
11
  // Inspect utility used for debugging.
@@ -81,7 +81,8 @@ describe('#util', () => {
81
81
 
82
82
  it('should throw error on array size rate limit', async () => {
83
83
  try {
84
- const dataMock = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
84
+ const dataMock =
85
+ 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
85
86
  const data = []
86
87
  for (let i = 0; i < 25; i++) data.push(dataMock)
87
88