@psf/bch-js 6.1.0 → 6.2.0
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 +1 -1
- package/src/slp/utils.js +0 -521
- package/src/transaction.js +1 -498
- package/test/unit/slp-utils.js +0 -1381
- package/test/unit/transaction-unit.js +4 -688
package/src/transaction.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
// Global npm libraries
|
|
6
|
-
const BigNumber = require('bignumber.js')
|
|
6
|
+
// const BigNumber = require('bignumber.js')
|
|
7
7
|
|
|
8
8
|
// Local libraries
|
|
9
9
|
const RawTransaction = require('./raw-transactions')
|
|
@@ -25,503 +25,6 @@ class Transaction {
|
|
|
25
25
|
return await this.psfSlpIndexer.tx(txid)
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/**
|
|
29
|
-
* @api Transaction.get() get()
|
|
30
|
-
* @apiName get
|
|
31
|
-
* @apiGroup Transaction
|
|
32
|
-
* @apiDescription
|
|
33
|
-
* Returns an object of transaction data, including addresses for input UTXOs.
|
|
34
|
-
* If it is a SLP token transaction, the token information for inputs and
|
|
35
|
-
* outputs will also be included.
|
|
36
|
-
*
|
|
37
|
-
* This is an API heavy call. This function will only work with a single txid.
|
|
38
|
-
* It does not yet support an array of TXIDs.
|
|
39
|
-
*
|
|
40
|
-
* @apiExample Example usage:
|
|
41
|
-
* (async () => {
|
|
42
|
-
* try {
|
|
43
|
-
* let txData = await bchjs.Transaction.get("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
|
44
|
-
* console.log(txData);
|
|
45
|
-
* } catch(error) {
|
|
46
|
-
* console.error(error)
|
|
47
|
-
* }
|
|
48
|
-
* })()
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
// CT 10/31/21: TODO: this function should be refactored to use get2(), but
|
|
52
|
-
// add waterfall validation of the TX and its inputs.
|
|
53
|
-
|
|
54
|
-
async getOld (txid) {
|
|
55
|
-
try {
|
|
56
|
-
if (typeof txid !== 'string') {
|
|
57
|
-
throw new Error(
|
|
58
|
-
'Input to Transaction.get() must be a string containing a TXID.'
|
|
59
|
-
)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const txDetails = await this.rawTransaction.getTxData(txid)
|
|
63
|
-
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
|
64
|
-
|
|
65
|
-
// Setup default SLP properties.
|
|
66
|
-
txDetails.isValidSLPTx = false
|
|
67
|
-
|
|
68
|
-
// First get the token information for the output. If that fails, then
|
|
69
|
-
// this is not an SLP transaction, and this method can return false.
|
|
70
|
-
let outTokenData
|
|
71
|
-
try {
|
|
72
|
-
outTokenData = await this.slpUtils.decodeOpReturn(txid)
|
|
73
|
-
// console.log(`outTokenData: ${JSON.stringify(outTokenData, null, 2)}`)
|
|
74
|
-
|
|
75
|
-
// Get Genesis data for this token.
|
|
76
|
-
const genesisData = await this.slpUtils.decodeOpReturn(
|
|
77
|
-
outTokenData.tokenId
|
|
78
|
-
// decodeOpReturnCache
|
|
79
|
-
// usrObj // pass user data when making an internal call.
|
|
80
|
-
)
|
|
81
|
-
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
|
|
82
|
-
|
|
83
|
-
// Add token information to the tx details object.
|
|
84
|
-
txDetails.tokenTxType = outTokenData.txType
|
|
85
|
-
txDetails.tokenId = outTokenData.tokenId
|
|
86
|
-
txDetails.tokenTicker = genesisData.ticker
|
|
87
|
-
txDetails.tokenName = genesisData.name
|
|
88
|
-
txDetails.tokenDecimals = genesisData.decimals
|
|
89
|
-
txDetails.tokenUri = genesisData.documentUri
|
|
90
|
-
txDetails.tokenDocHash = genesisData.documentHash
|
|
91
|
-
|
|
92
|
-
// Add the token quantity to each output.
|
|
93
|
-
for (let i = 0; i < outTokenData.amounts.length; i++) {
|
|
94
|
-
const rawQty = outTokenData.amounts[i]
|
|
95
|
-
// const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
|
|
96
|
-
|
|
97
|
-
// Calculate the real quantity using a BigNumber, then convert it to a
|
|
98
|
-
// floating point number.
|
|
99
|
-
let realQty = new BigNumber(rawQty).dividedBy(
|
|
100
|
-
10 ** parseInt(txDetails.tokenDecimals)
|
|
101
|
-
)
|
|
102
|
-
realQty = realQty.toString()
|
|
103
|
-
// realQty = parseFloat(realQty)
|
|
104
|
-
|
|
105
|
-
txDetails.vout[i + 1].tokenQtyStr = realQty
|
|
106
|
-
txDetails.vout[i + 1].tokenQty = parseFloat(realQty)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Add tokenQty = null to any outputs that don't have a value.
|
|
110
|
-
for (let i = 0; i < txDetails.vout.length; i++) {
|
|
111
|
-
const thisVout = txDetails.vout[i]
|
|
112
|
-
|
|
113
|
-
if (!thisVout.tokenQty && thisVout.tokenQty !== 0) {
|
|
114
|
-
thisVout.tokenQty = null
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Loop through each input and retrieve the token data.
|
|
119
|
-
for (let i = 0; i < txDetails.vin.length; i++) {
|
|
120
|
-
const thisVin = txDetails.vin[i]
|
|
121
|
-
// console.log(`thisVin: ${JSON.stringify(thisVin, null, 2)}`)
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
// If decodeOpReturn() throws an error, then this input is not
|
|
125
|
-
// from an SLP transaction and can be ignored.
|
|
126
|
-
const inTokenData = await this.slpUtils.decodeOpReturn(thisVin.txid)
|
|
127
|
-
// console.log(
|
|
128
|
-
// `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
|
|
129
|
-
// )
|
|
130
|
-
|
|
131
|
-
let tokenQty = 0
|
|
132
|
-
|
|
133
|
-
// Validate the input. Mark qty as null if not valid.
|
|
134
|
-
const vinIsValid = await this.slpUtils.waterfallValidateTxid(
|
|
135
|
-
thisVin.txid
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
if (!vinIsValid) {
|
|
139
|
-
// If the input is not a valid, then set qty as null.
|
|
140
|
-
tokenQty = null
|
|
141
|
-
} else if (inTokenData.txType === 'SEND') {
|
|
142
|
-
// Get the appropriate vout token amount. This may throw an error,
|
|
143
|
-
// which means this Vin is not actually a token UTXO, it was just
|
|
144
|
-
// associated with a previous token TX.
|
|
145
|
-
tokenQty = inTokenData.amounts[thisVin.vout - 1]
|
|
146
|
-
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
|
147
|
-
|
|
148
|
-
//
|
|
149
|
-
} else if (inTokenData.txType === 'GENESIS') {
|
|
150
|
-
// Only vout[1] of a Genesis transaction represents the tokens.
|
|
151
|
-
// Any other outputs in that transaction are normal BCH UTXOs.
|
|
152
|
-
if (thisVin.vout === 1) {
|
|
153
|
-
tokenQty = inTokenData.qty
|
|
154
|
-
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
|
155
|
-
}
|
|
156
|
-
} else if (inTokenData.txType === 'MINT') {
|
|
157
|
-
// vout=1 (second output) recieves the newly minted tokens.
|
|
158
|
-
if (thisVin.vout === 1) {
|
|
159
|
-
tokenQty = inTokenData.qty
|
|
160
|
-
} else {
|
|
161
|
-
tokenQty = null
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
//
|
|
165
|
-
} else {
|
|
166
|
-
console.log(
|
|
167
|
-
'Unexpected code path in Transaction.get(). What is the txType?'
|
|
168
|
-
)
|
|
169
|
-
console.log(inTokenData)
|
|
170
|
-
throw new Error('Unexpected code path')
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (tokenQty) {
|
|
174
|
-
// const realQty =
|
|
175
|
-
// Number(tokenQty) / Math.pow(10, txDetails.tokenDecimals)
|
|
176
|
-
|
|
177
|
-
// Calculate the real quantity using a BigNumber, then convert it to a
|
|
178
|
-
// floating point number.
|
|
179
|
-
let realQty = new BigNumber(tokenQty).dividedBy(
|
|
180
|
-
10 ** parseInt(txDetails.tokenDecimals)
|
|
181
|
-
)
|
|
182
|
-
realQty = realQty.toString()
|
|
183
|
-
// realQty = parseFloat(realQty)
|
|
184
|
-
|
|
185
|
-
thisVin.tokenQtyStr = realQty
|
|
186
|
-
thisVin.tokenQty = parseFloat(realQty)
|
|
187
|
-
// txDetails.vin[i].tokenQty = tokenQty
|
|
188
|
-
|
|
189
|
-
// Add token ID to input
|
|
190
|
-
thisVin.tokenId = inTokenData.tokenId
|
|
191
|
-
} else {
|
|
192
|
-
thisVin.tokenQty = null
|
|
193
|
-
}
|
|
194
|
-
} catch (err) {
|
|
195
|
-
// If decodeOpReturn() throws an error, then this input is not
|
|
196
|
-
// from an SLP transaction and can be ignored.
|
|
197
|
-
// thisVin.tokenQty = null
|
|
198
|
-
thisVin.tokenQty = null
|
|
199
|
-
continue
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Finally, validate the SLP TX.
|
|
204
|
-
// this.slpUtils.waterfallValidateTxid(txid, usrObj)
|
|
205
|
-
txDetails.isValidSLPTx = await this.slpUtils.waterfallValidateTxid(txid)
|
|
206
|
-
|
|
207
|
-
// TODO: Convert the block hash to a block height. Add block height
|
|
208
|
-
// value to the transaction.
|
|
209
|
-
} catch (err) {
|
|
210
|
-
// console.log('Error: ', err)
|
|
211
|
-
|
|
212
|
-
// This case handles rate limit errors.
|
|
213
|
-
if (err.response && err.response.data && err.response.data.error) {
|
|
214
|
-
throw new Error(err.response.data.error)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// If decoding the op_return fails, then it's not an SLP transaction,
|
|
218
|
-
// and the non-hyrated TX details can be returned.
|
|
219
|
-
return txDetails
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return txDetails
|
|
223
|
-
} catch (err) {
|
|
224
|
-
// console.error('Error in transactions.js/get(): ', err)
|
|
225
|
-
|
|
226
|
-
// This case handles rate limit errors.
|
|
227
|
-
if (err.response && err.response.data && err.response.data.error) {
|
|
228
|
-
throw new Error(err.response.data.error)
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (err.error) throw new Error(err.error)
|
|
232
|
-
throw err
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* @api Transaction.get3() get3()
|
|
238
|
-
* @apiName get3
|
|
239
|
-
* @apiGroup Transaction
|
|
240
|
-
* @apiDescription
|
|
241
|
-
* Returns an object of transaction data, including addresses for input UTXOs.
|
|
242
|
-
* If it is a SLP token transaction, the token information for inputs and
|
|
243
|
-
* outputs will also be included.
|
|
244
|
-
*
|
|
245
|
-
* This is an API heavy call. This function will only work with a single txid.
|
|
246
|
-
* It does not yet support an array of TXIDs.
|
|
247
|
-
*
|
|
248
|
-
* This is the same as get(), except it omits DAG validation of the TXID.
|
|
249
|
-
*
|
|
250
|
-
* @apiExample Example usage:
|
|
251
|
-
* (async () => {
|
|
252
|
-
* try {
|
|
253
|
-
* let txData = await bchjs.Transaction.get2("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
|
254
|
-
* console.log(txData);
|
|
255
|
-
* } catch(error) {
|
|
256
|
-
* console.error(error)
|
|
257
|
-
* }
|
|
258
|
-
* })()
|
|
259
|
-
*/
|
|
260
|
-
async get3 (txid) {
|
|
261
|
-
try {
|
|
262
|
-
if (typeof txid !== 'string') {
|
|
263
|
-
throw new Error(
|
|
264
|
-
'Input to Transaction.get() must be a string containing a TXID.'
|
|
265
|
-
)
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Get TX data
|
|
269
|
-
const txDetails = await this.rawTransaction.getTxData(txid)
|
|
270
|
-
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
|
271
|
-
|
|
272
|
-
// Get the block height the transaction was mined in.
|
|
273
|
-
const blockHeader = await this.blockchain.getBlockHeader(
|
|
274
|
-
txDetails.blockhash
|
|
275
|
-
)
|
|
276
|
-
txDetails.blockheight = blockHeader.height
|
|
277
|
-
// console.log(`blockHeader: ${JSON.stringify(blockHeader, null, 2)}`)
|
|
278
|
-
|
|
279
|
-
// Set default as not an SLP tx
|
|
280
|
-
txDetails.isSlpTx = false
|
|
281
|
-
|
|
282
|
-
// Get Token Data
|
|
283
|
-
const txTokenData = await this.getTokenInfo(txid)
|
|
284
|
-
// console.log(`txTokenData: ${JSON.stringify(txTokenData, null, 2)}`)
|
|
285
|
-
|
|
286
|
-
// If not a token, return the tx data. Processing is complete.
|
|
287
|
-
if (!txTokenData) return txDetails
|
|
288
|
-
|
|
289
|
-
// Mark TX as an SLP tx. This does not mean it's valid, it just means
|
|
290
|
-
// the OP_RETURN passes a basic check.
|
|
291
|
-
txDetails.isSlpTx = true
|
|
292
|
-
|
|
293
|
-
// Get Genesis data
|
|
294
|
-
const genesisData = await this.getTokenInfo(txTokenData.tokenId)
|
|
295
|
-
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
|
|
296
|
-
|
|
297
|
-
// Add token information to the tx details object.
|
|
298
|
-
txDetails.tokenTxType = txTokenData.txType
|
|
299
|
-
txDetails.tokenId = txTokenData.tokenId
|
|
300
|
-
txDetails.tokenTicker = genesisData.ticker
|
|
301
|
-
txDetails.tokenName = genesisData.name
|
|
302
|
-
txDetails.tokenDecimals = genesisData.decimals
|
|
303
|
-
txDetails.tokenUri = genesisData.documentUri
|
|
304
|
-
txDetails.tokenDocHash = genesisData.documentHash
|
|
305
|
-
// console.log(`txDetails before processing input and outputs: ${JSON.stringify(txDetails, null, 2)}`)
|
|
306
|
-
|
|
307
|
-
// Process TX Outputs
|
|
308
|
-
// Add the token quantity to each output.
|
|
309
|
-
// 'i' starts at 1, because vout[0] is the OP_RETURN
|
|
310
|
-
for (let i = 0; i < txDetails.vout.length; i++) {
|
|
311
|
-
const thisVout = txDetails.vout[i]
|
|
312
|
-
if (txTokenData.txType === 'SEND') {
|
|
313
|
-
// console.log(
|
|
314
|
-
// `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
|
|
315
|
-
// )
|
|
316
|
-
|
|
317
|
-
// First output is OP_RETURN, so tokenQty is null.
|
|
318
|
-
if (i === 0) {
|
|
319
|
-
thisVout.tokenQty = null
|
|
320
|
-
thisVout.tokenQtyStr = null
|
|
321
|
-
continue
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Non SLP outputs.
|
|
325
|
-
if (i > txTokenData.amounts.length) {
|
|
326
|
-
thisVout.tokenQty = null
|
|
327
|
-
thisVout.tokenQtyStr = null
|
|
328
|
-
continue
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const rawQty = txTokenData.amounts[i - 1]
|
|
332
|
-
|
|
333
|
-
// Calculate the real quantity using a BigNumber, then convert it to a
|
|
334
|
-
// floating point number.
|
|
335
|
-
let realQty = new BigNumber(rawQty).dividedBy(
|
|
336
|
-
10 ** parseInt(txDetails.tokenDecimals)
|
|
337
|
-
)
|
|
338
|
-
realQty = realQty.toString()
|
|
339
|
-
// realQty = parseFloat(realQty)
|
|
340
|
-
|
|
341
|
-
txDetails.vout[i].tokenQtyStr = realQty
|
|
342
|
-
txDetails.vout[i].tokenQty = parseFloat(realQty)
|
|
343
|
-
|
|
344
|
-
// console.log(
|
|
345
|
-
// `thisVout ${i}: ${JSON.stringify(txDetails.vout[i], null, 2)}`
|
|
346
|
-
// )
|
|
347
|
-
} else if (
|
|
348
|
-
txTokenData.txType === 'GENESIS' ||
|
|
349
|
-
txTokenData.txType === 'MINT'
|
|
350
|
-
) {
|
|
351
|
-
// console.log(
|
|
352
|
-
// `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
|
|
353
|
-
// )
|
|
354
|
-
|
|
355
|
-
let tokenQty = 0 // Default value
|
|
356
|
-
|
|
357
|
-
// Only vout[1] of a Genesis or Mint transaction represents the tokens.
|
|
358
|
-
// Any other outputs in that transaction are normal BCH UTXOs.
|
|
359
|
-
if (i === 1) {
|
|
360
|
-
tokenQty = txTokenData.qty
|
|
361
|
-
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
|
362
|
-
|
|
363
|
-
// Calculate the real quantity using a BigNumber, then convert it to a
|
|
364
|
-
// floating point number.
|
|
365
|
-
let realQty = new BigNumber(tokenQty).dividedBy(
|
|
366
|
-
10 ** parseInt(txDetails.tokenDecimals)
|
|
367
|
-
)
|
|
368
|
-
realQty = realQty.toString()
|
|
369
|
-
// realQty = parseFloat(realQty)
|
|
370
|
-
|
|
371
|
-
thisVout.tokenQtyStr = realQty
|
|
372
|
-
thisVout.tokenQty = parseFloat(realQty)
|
|
373
|
-
// console.log(`thisVout[${i}]: ${JSON.stringify(thisVout, null, 2)}`)
|
|
374
|
-
} else if (i === txTokenData.mintBatonVout) {
|
|
375
|
-
// Optional Mint baton
|
|
376
|
-
thisVout.tokenQtyStr = '0'
|
|
377
|
-
thisVout.tokenQty = 0
|
|
378
|
-
thisVout.isMintBaton = true
|
|
379
|
-
} else {
|
|
380
|
-
thisVout.tokenQtyStr = '0'
|
|
381
|
-
thisVout.tokenQty = 0
|
|
382
|
-
}
|
|
383
|
-
} else {
|
|
384
|
-
throw new Error('Unknown SLP TX type for TX')
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// Process TX inputs
|
|
389
|
-
for (let i = 0; i < txDetails.vin.length; i++) {
|
|
390
|
-
const thisVin = txDetails.vin[i]
|
|
391
|
-
// console.log(`thisVin[${i}]: ${JSON.stringify(thisVin, null, 2)}`)
|
|
392
|
-
|
|
393
|
-
const vinTokenData = await this.getTokenInfo(thisVin.txid)
|
|
394
|
-
// console.log(
|
|
395
|
-
// `vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
|
|
396
|
-
// )
|
|
397
|
-
|
|
398
|
-
// Corner case: Ensure the token ID is the same.
|
|
399
|
-
const vinTokenIdIsTheSame = vinTokenData.tokenId === txDetails.tokenId
|
|
400
|
-
|
|
401
|
-
// If the input is not a token input, or if the tokenID is not the same,
|
|
402
|
-
// then mark the token output as null.
|
|
403
|
-
if (!vinTokenData || !vinTokenIdIsTheSame) {
|
|
404
|
-
thisVin.tokenQty = 0
|
|
405
|
-
thisVin.tokenQtyStr = '0'
|
|
406
|
-
thisVin.tokenId = null
|
|
407
|
-
continue
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
if (vinTokenData.txType === 'SEND') {
|
|
411
|
-
// console.log(
|
|
412
|
-
// `SEND vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
|
|
413
|
-
// )
|
|
414
|
-
|
|
415
|
-
const tokenQty = vinTokenData.amounts[thisVin.vout - 1]
|
|
416
|
-
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
|
417
|
-
|
|
418
|
-
// Calculate the real quantity using a BigNumber, then convert it to a
|
|
419
|
-
// floating point number.
|
|
420
|
-
let realQty = new BigNumber(tokenQty).dividedBy(
|
|
421
|
-
10 ** parseInt(txDetails.tokenDecimals)
|
|
422
|
-
)
|
|
423
|
-
realQty = realQty.toString()
|
|
424
|
-
// realQty = parseFloat(realQty)
|
|
425
|
-
|
|
426
|
-
thisVin.tokenQtyStr = realQty
|
|
427
|
-
thisVin.tokenQty = parseFloat(realQty)
|
|
428
|
-
thisVin.tokenId = vinTokenData.tokenId
|
|
429
|
-
} else if (vinTokenData.txType === 'MINT') {
|
|
430
|
-
// console.log(
|
|
431
|
-
// `MINT vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
|
|
432
|
-
// )
|
|
433
|
-
|
|
434
|
-
let tokenQty = 0 // Default value
|
|
435
|
-
|
|
436
|
-
// Only vout[1] of a Genesis transaction represents the tokens.
|
|
437
|
-
// Any other outputs in that transaction are normal BCH UTXOs.
|
|
438
|
-
if (thisVin.vout === 1) {
|
|
439
|
-
tokenQty = vinTokenData.qty
|
|
440
|
-
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
|
441
|
-
|
|
442
|
-
// Calculate the real quantity using a BigNumber, then convert it to a
|
|
443
|
-
// floating point number.
|
|
444
|
-
let realQty = new BigNumber(tokenQty).dividedBy(
|
|
445
|
-
10 ** parseInt(txDetails.tokenDecimals)
|
|
446
|
-
)
|
|
447
|
-
realQty = realQty.toString()
|
|
448
|
-
// realQty = parseFloat(realQty)
|
|
449
|
-
|
|
450
|
-
thisVin.tokenQtyStr = realQty
|
|
451
|
-
thisVin.tokenQty = parseFloat(realQty)
|
|
452
|
-
thisVin.tokenId = vinTokenData.tokenId
|
|
453
|
-
} else if (thisVin.vout === vinTokenData.mintBatonVout) {
|
|
454
|
-
// Optional Mint baton
|
|
455
|
-
thisVin.tokenQtyStr = '0'
|
|
456
|
-
thisVin.tokenQty = 0
|
|
457
|
-
thisVin.tokenId = vinTokenData.tokenId
|
|
458
|
-
thisVin.isMintBaton = true
|
|
459
|
-
} else {
|
|
460
|
-
thisVin.tokenQtyStr = '0'
|
|
461
|
-
thisVin.tokenQty = 0
|
|
462
|
-
thisVin.tokenId = null
|
|
463
|
-
}
|
|
464
|
-
} else if (vinTokenData.txType === 'GENESIS') {
|
|
465
|
-
// console.log(
|
|
466
|
-
// `GENESIS vinTokenData ${i}: ${JSON.stringify(
|
|
467
|
-
// vinTokenData,
|
|
468
|
-
// null,
|
|
469
|
-
// 2
|
|
470
|
-
// )}`
|
|
471
|
-
// )
|
|
472
|
-
|
|
473
|
-
let tokenQty = 0 // Default value
|
|
474
|
-
|
|
475
|
-
// Only vout[1] of a Genesis transaction represents the tokens.
|
|
476
|
-
// Any other outputs in that transaction are normal BCH UTXOs.
|
|
477
|
-
if (thisVin.vout === 1) {
|
|
478
|
-
tokenQty = vinTokenData.qty
|
|
479
|
-
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
|
480
|
-
|
|
481
|
-
// Calculate the real quantity using a BigNumber, then convert it to a
|
|
482
|
-
// floating point number.
|
|
483
|
-
let realQty = new BigNumber(tokenQty).dividedBy(
|
|
484
|
-
10 ** parseInt(txDetails.tokenDecimals)
|
|
485
|
-
)
|
|
486
|
-
realQty = realQty.toString()
|
|
487
|
-
// realQty = parseFloat(realQty)
|
|
488
|
-
|
|
489
|
-
thisVin.tokenQtyStr = realQty
|
|
490
|
-
thisVin.tokenQty = parseFloat(realQty)
|
|
491
|
-
thisVin.tokenId = vinTokenData.tokenId
|
|
492
|
-
} else if (thisVin.vout === vinTokenData.mintBatonVout) {
|
|
493
|
-
// Optional Mint baton
|
|
494
|
-
thisVin.tokenQtyStr = '0'
|
|
495
|
-
thisVin.tokenQty = 0
|
|
496
|
-
thisVin.tokenId = vinTokenData.tokenId
|
|
497
|
-
thisVin.isMintBaton = true
|
|
498
|
-
} else {
|
|
499
|
-
thisVin.tokenQtyStr = '0'
|
|
500
|
-
thisVin.tokenQty = 0
|
|
501
|
-
thisVin.tokenId = null
|
|
502
|
-
}
|
|
503
|
-
} else {
|
|
504
|
-
console.log(
|
|
505
|
-
`Unknown vinTokenData: ${JSON.stringify(vinTokenData, null, 2)}`
|
|
506
|
-
)
|
|
507
|
-
throw new Error('Unknown token type in input')
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
return txDetails
|
|
512
|
-
} catch (err) {
|
|
513
|
-
console.error('Error in get3()')
|
|
514
|
-
|
|
515
|
-
// This case handles rate limit errors.
|
|
516
|
-
if (err.response && err.response.data && err.response.data.error) {
|
|
517
|
-
throw new Error(err.response.data.error)
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
if (err.error) throw new Error(err.error)
|
|
521
|
-
throw err
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
28
|
// A wrapper for decodeOpReturn(). Returns false if txid is not an SLP tx.
|
|
526
29
|
// Returns the token data if the txid is an SLP tx.
|
|
527
30
|
async getTokenInfo (txid) {
|