From 83d523260d59d3ba48827555099a5cd4101dd323 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 30 Jan 2021 14:40:46 -0800 Subject: [PATCH] fix(getTxDataSlp): Adding prototype function --- package.json | 2 +- src/raw-transactions.js | 83 ++++++++++++++++++++++++++++++ test/integration/rawtransaction.js | 28 +++++++++- 3 files changed, 110 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 4fa743b..8406a97 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "test:integration:local": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration", "test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/", "test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet", - "test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#rawtransaction' test/integration/", + "test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#getTxDataSlp' test/integration/", "test:temp2": "mocha --timeout=30000 -g '#getTxData' test/unit/", "coverage": "nyc report --reporter=text-lcov | coveralls", "coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/", diff --git a/src/raw-transactions.js b/src/raw-transactions.js index 5fcc19b..caf5f0c 100644 --- a/src/raw-transactions.js +++ b/src/raw-transactions.js @@ -1,5 +1,7 @@ const axios = require('axios') +const SlpUtils = require('./slp/utils') + let _this class RawTransactions { @@ -27,6 +29,9 @@ class RawTransactions { // Encapsulate dependencies this.axios = axios + // Dependencies + this.slpUtils = new SlpUtils(config) + _this = this } @@ -385,6 +390,84 @@ class RawTransactions { } } + // Wraps getTxData(), but also appends SLP token information to each input + // and output of the transaction. This is a very API-heavy call. + // Returns false if the txid is not an SLP transaction. + // + // Warning! This is a prototype function and the output can change at any time + // without reflecting a change in the semantic version. DO NOT USE IN PRODUCTION. + async getTxDataSlp (txid) { + try { + if (typeof txid !== 'string') { + throw new Error('Input must be a string or array of strings.') + } + + const txDetails = await this.getTxData(txid) + + // First get the token information for the output. If that fails, then + // this is not an SLP transaction, and this method can return false. + let outTokenData + try { + outTokenData = await this.slpUtils.decodeOpReturn(txid) + // console.log(`outTokenData: ${JSON.stringify(outTokenData, null, 2)}`) + + // Add token information to the tx details object. + txDetails.tokenTxType = outTokenData.txType + txDetails.tokenId = outTokenData.tokenId + + // Add the token quantity to each output. + for (let i = 0; i < outTokenData.amounts.length; i++) { + txDetails.vout[i + 1].tokenQty = outTokenData.amounts[i] + } + + // Loop through each input and retrieve the token data. + for (let i = 0; i < txDetails.vin.length; i++) { + const thisVin = txDetails.vin[i] + + try { + // If decodeOpReturn() throws an error, then this input is not + // from an SLP transaction and can be ignored. + const inTokenData = await this.slpUtils.decodeOpReturn(thisVin.txid) + // console.log( + // `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}` + // ) + + // Get the appropriate vout token amount. This may throw an error, + // which means this Vin is not actually a token UTXO, it was just + // associated with a previous token TX. + const tokenQty = inTokenData.amounts[thisVin.vout - 1] + // console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`) + + if (tokenQty) { + thisVin.tokenQty = tokenQty + // txDetails.vin[i].tokenQty = tokenQty + } else { + thisVin.tokenQty = null + } + } catch (err) { + // console.log('catch 2: ', err) + // If decodeOpReturn() throws an error, then this input is not + // from an SLP transaction and can be ignored. + // thisVin.tokenQty = null + thisVin.tokenQty = null + continue + } + // console.log( + // `2: txDetails.vin[i]: ${JSON.stringify(txDetails.vin[i], null, 2)}` + // ) + } + } catch (err) { + // console.log('catch 1: ', err) + return false + } + + return txDetails + } catch (error) { + if (error.response && error.response.data) throw error.response.data + else throw error + } + } + /** * @api RawTransactions.sendRawTransaction() sendRawTransaction() * @apiName sendRawTransaction diff --git a/test/integration/rawtransaction.js b/test/integration/rawtransaction.js index 5d06fd1..86c18f5 100644 --- a/test/integration/rawtransaction.js +++ b/test/integration/rawtransaction.js @@ -238,9 +238,9 @@ describe('#rawtransaction', () => { const data = [] for (let i = 0; i < 25; i++) data.push(dataMock) - const result = await bchjs.RawTransactions.sendRawTransaction(data) + await bchjs.RawTransactions.sendRawTransaction(data) - console.log(`result: ${util.inspect(result)}`) + // console.log(`result: ${util.inspect(result)}`) assert.equal(true, false, 'Unexpected result!') } catch (err) { assert.hasAnyKeys(err, ['error']) @@ -276,6 +276,30 @@ describe('#rawtransaction', () => { assert.property(result.vin[0], 'address') }) }) + + describe('#getTxDataSlp', () => { + it('should return tx data with SLP information', async () => { + const txid = 'b438855cfcab64516b44097d7212df9cdb99226c8d7c7ab504d35fcfd834cb5b' + + const result = await bchjs.RawTransactions.getTxDataSlp(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.property(result.vin[0], 'address') + assert.property(result.vin[0], 'tokenQty') + + assert.equal(result.vin[0].tokenQty, null) + assert.equal(result.vin[1].tokenQty, '100000000000') + }) + + it('should handle non-slp tx', async () => { + const txid = '04a6aca328af2445327015895b8da9766093b1989b52e477559759eb8072fc0a' + + const result = await bchjs.RawTransactions.getTxDataSlp(txid) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + assert.equal(result, false) + }) + }) }) function sleep (ms) {