@psf/bch-js 6.1.0 → 6.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 +1 -1
- package/src/slp/utils.js +0 -521
- package/src/transaction.js +9 -467
- package/src/utxo.js +3 -0
- 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')
|
|
@@ -20,11 +20,6 @@ class Transaction {
|
|
|
20
20
|
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
// Proxy the call to the psf-slp-indexer.
|
|
24
|
-
async get (txid) {
|
|
25
|
-
return await this.psfSlpIndexer.tx(txid)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
23
|
/**
|
|
29
24
|
* @api Transaction.get() get()
|
|
30
25
|
* @apiName get
|
|
@@ -34,8 +29,6 @@ class Transaction {
|
|
|
34
29
|
* If it is a SLP token transaction, the token information for inputs and
|
|
35
30
|
* outputs will also be included.
|
|
36
31
|
*
|
|
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
32
|
*
|
|
40
33
|
* @apiExample Example usage:
|
|
41
34
|
* (async () => {
|
|
@@ -47,481 +40,30 @@ class Transaction {
|
|
|
47
40
|
* }
|
|
48
41
|
* })()
|
|
49
42
|
*/
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
}
|
|
43
|
+
async get (txid) {
|
|
44
|
+
return await this.psfSlpIndexer.tx(txid)
|
|
234
45
|
}
|
|
235
46
|
|
|
236
47
|
/**
|
|
237
|
-
* @api Transaction.
|
|
238
|
-
* @apiName
|
|
48
|
+
* @api Transaction.getTokenInfo() getTokenInfo()
|
|
49
|
+
* @apiName getTokenInfo
|
|
239
50
|
* @apiGroup Transaction
|
|
240
51
|
* @apiDescription
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
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.
|
|
52
|
+
* Given the TXID of a token transaction, it will return data about that
|
|
53
|
+
* token by retrieving the data from the Genesis transaction and docoding
|
|
54
|
+
* the OP_RETURN.
|
|
247
55
|
*
|
|
248
|
-
* This is the same as get(), except it omits DAG validation of the TXID.
|
|
249
56
|
*
|
|
250
57
|
* @apiExample Example usage:
|
|
251
58
|
* (async () => {
|
|
252
59
|
* try {
|
|
253
|
-
* let txData = await bchjs.Transaction.
|
|
60
|
+
* let txData = await bchjs.Transaction.getTokenInfo("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
|
254
61
|
* console.log(txData);
|
|
255
62
|
* } catch(error) {
|
|
256
63
|
* console.error(error)
|
|
257
64
|
* }
|
|
258
65
|
* })()
|
|
259
66
|
*/
|
|
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
67
|
// A wrapper for decodeOpReturn(). Returns false if txid is not an SLP tx.
|
|
526
68
|
// Returns the token data if the txid is an SLP tx.
|
|
527
69
|
async getTokenInfo (txid) {
|
package/src/utxo.js
CHANGED