diff --git a/package-lock.json b/package-lock.json index 9b2c98f..766a8f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -147,106 +147,6 @@ "to-fast-properties": "^2.0.0" } }, - "@chris.troutner/bch-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@chris.troutner/bch-js/-/bch-js-3.0.1.tgz", - "integrity": "sha512-XTOCFeMCBThXlGxdWsmW6OmfZBqa3XtsvFGE3hIapWfvoCUd6fSyegn9G1mXod236D3qKTW/Vk/NBwGlc7Y1YQ==", - "requires": { - "@uppy/core": "^1.10.4", - "@uppy/tus": "^1.5.12", - "apidoc": "^0.17.7", - "assert": "^2.0.0", - "axios": "^0.19.0", - "bc-bip68": "^1.0.5", - "bch-wallet-bridge.js": "github:web3bch/bch-wallet-bridge.js#master", - "bchaddrjs-slp": "^0.2.5", - "bigi": "^1.4.2", - "bignumber.js": "^9.0.0", - "bip-schnorr": "^0.3.0", - "bip21": "github:Bitcoin-com/bip21", - "bip32-utils": "github:Bitcoin-com/bip32-utils#0.13.1", - "bip38": "^2.0.2", - "bip39": "^3.0.2", - "bip66": "^1.1.5", - "bitcoincash-ops": "github:christroutner/bitcoincash-ops", - "bitcoincashjs-lib": "github:christroutner/bitcoincashjs-lib", - "bitcoinjs-message": "^2.0.0", - "bs58": "^4.0.1", - "buffer": "^5.1.0", - "cashaddrjs": "^0.3.3", - "chalk": "^2.3.0", - "clear": "0.1.0", - "coininfo": "github:Bitcoin-com/coininfo", - "commander": "^3.0.0", - "cp-file": "^7.0.0", - "ecurve": "^1.0.6", - "figlet": "^1.2.0", - "git-clone": "^0.1.0", - "ini": "^1.3.5", - "mkdirp": "^0.5.1", - "node-cmd": "^3.0.0", - "node-emoji": "^1.8.1", - "qrcode": "^1.4.1", - "randombytes": "^2.0.6", - "repl.history": "^0.1.4", - "safe-buffer": "^5.1.2", - "satoshi-bitcoin": "^1.0.4", - "slp-mdm": "0.0.6", - "slp-parser": "0.0.4", - "socket.io": "^2.1.1", - "socket.io-client": "^2.1.1", - "touch": "^3.1.0", - "wif": "^2.0.6" - }, - "dependencies": { - "apidoc": { - "version": "0.17.7", - "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.17.7.tgz", - "integrity": "sha512-9Wf4bRPwCuWOIOxR42dDnsXnFw+rhJg5VrMQK+KmNxJwyIh30UqX6gvjjXSG6YO74MqE87F18bbQXUENK9dPGg==", - "requires": { - "apidoc-core": "~0.8.2", - "commander": "^2.19.0", - "fs-extra": "^7.0.0", - "lodash": "^4.17.10", - "markdown-it": "^8.3.1", - "winston": "^3.0.0" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - } - } - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, "@istanbuljs/load-nyc-config": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", @@ -465,6 +365,127 @@ "@types/node": ">= 8" } }, + "@psf/bch-js": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-3.5.5.tgz", + "integrity": "sha512-bZs1p6VNDBYJPJuKuYD4h2Wsp+gt38181XlVX+2Yq8JGFOvf5ZRSrkqcd0LYvfiP8f9IA46aaAtTaLQT1KQsJw==", + "requires": { + "@uppy/core": "^1.10.4", + "@uppy/tus": "^1.5.12", + "apidoc": "^0.17.7", + "assert": "^2.0.0", + "axios": "^0.19.0", + "bc-bip68": "^1.0.5", + "bch-wallet-bridge.js": "github:web3bch/bch-wallet-bridge.js#master", + "bchaddrjs-slp": "^0.2.5", + "bigi": "^1.4.2", + "bignumber.js": "^9.0.0", + "bip-schnorr": "^0.3.0", + "bip21": "github:Bitcoin-com/bip21", + "bip32-utils": "github:Bitcoin-com/bip32-utils#0.13.1", + "bip38": "^2.0.2", + "bip39": "^3.0.2", + "bip66": "^1.1.5", + "bitcoincash-ops": "github:christroutner/bitcoincash-ops", + "bitcoincashjs-lib": "github:christroutner/bitcoincashjs-lib#master", + "bitcoinjs-message": "^2.0.0", + "bs58": "^4.0.1", + "buffer": "^5.1.0", + "cashaddrjs": "^0.3.3", + "chalk": "^2.3.0", + "clear": "0.1.0", + "coininfo": "github:Bitcoin-com/coininfo", + "commander": "^3.0.0", + "cp-file": "^7.0.0", + "ecurve": "^1.0.6", + "figlet": "^1.2.0", + "git-clone": "^0.1.0", + "ini": "^1.3.5", + "mkdirp": "^0.5.1", + "node-cmd": "^3.0.0", + "node-emoji": "^1.8.1", + "qrcode": "^1.4.1", + "randombytes": "^2.0.6", + "repl.history": "^0.1.4", + "safe-buffer": "^5.1.2", + "satoshi-bitcoin": "^1.0.4", + "slp-mdm": "0.0.6", + "slp-parser": "0.0.4", + "socket.io": "^2.1.1", + "socket.io-client": "^2.1.1", + "touch": "^3.1.0", + "wif": "^2.0.6" + }, + "dependencies": { + "apidoc": { + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.17.7.tgz", + "integrity": "sha512-9Wf4bRPwCuWOIOxR42dDnsXnFw+rhJg5VrMQK+KmNxJwyIh30UqX6gvjjXSG6YO74MqE87F18bbQXUENK9dPGg==", + "requires": { + "apidoc-core": "~0.8.2", + "commander": "^2.19.0", + "fs-extra": "^7.0.0", + "lodash": "^4.17.10", + "markdown-it": "^8.3.1", + "winston": "^3.0.0" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + } + } + }, + "bitcoincashjs-lib": { + "version": "github:christroutner/bitcoincashjs-lib#715823b5d6eb3e2961e715605ad1464f3223da0b", + "from": "github:christroutner/bitcoincashjs-lib#master", + "requires": { + "bech32": "^1.1.2", + "bigi": "^1.4.0", + "bip66": "^1.1.0", + "bitcoincash-ops": "github:christroutner/bitcoincash-ops", + "bs58check": "^2.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.3", + "ecurve": "^1.0.0", + "merkle-lib": "^2.0.10", + "pushdata-bitcoin": "github:Bitcoin-com/pushdata-bitcoin#1.2.1", + "randombytes": "^2.0.1", + "safe-buffer": "^5.0.1", + "typeforce": "^1.18.0", + "varuint-bitcoin": "^1.0.4", + "wif": "^2.0.1" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, "@semantic-release/commit-analyzer": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-7.0.0.tgz", @@ -2289,27 +2310,6 @@ "resolved": "https://registry.npmjs.org/bitcoincashjs/-/bitcoincashjs-0.1.14.tgz", "integrity": "sha512-x0wMVSCZ56ZQnSQ+Xim7XfS0IYiOyOwo0pdMCmR+cDNB7zRhICFuH5i3lNdLSgUw1e7Pc3FmUlC9wruQNlYMnA==" }, - "bitcoincashjs-lib": { - "version": "github:christroutner/bitcoincashjs-lib#b03ca974d4fd548cb42c7a522863e5d9be6051ef", - "from": "github:christroutner/bitcoincashjs-lib", - "requires": { - "bech32": "^1.1.2", - "bigi": "^1.4.0", - "bip66": "^1.1.0", - "bitcoincash-ops": "github:christroutner/bitcoincash-ops", - "bs58check": "^2.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.3", - "ecurve": "^1.0.0", - "merkle-lib": "^2.0.10", - "pushdata-bitcoin": "github:Bitcoin-com/pushdata-bitcoin#1.2.1", - "randombytes": "^2.0.1", - "safe-buffer": "^5.0.1", - "typeforce": "^1.18.0", - "varuint-bitcoin": "^1.0.4", - "wif": "^2.0.1" - } - }, "bitcoinjs-message": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.1.0.tgz", @@ -2324,42 +2324,28 @@ } }, "bitcore-lib": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.21.0.tgz", - "integrity": "sha512-Z1lDqSReD/Ah/n7qEsR564AlNImwYeB1MQ0fnOD6e2piNsTXQ3cSjgH93MxVARpX4LRkEev2XBnYCJ4NcYTQdQ==", + "version": "8.22.2", + "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.22.2.tgz", + "integrity": "sha512-atl/RN7x2R/JQMaIgUzX0EQZ+I/d7fTaOoS2/5k+H5POGc2vygWZ9MWpGJi3T9Yb5jM/cT5aFsrDq8s8l6lqgQ==", "requires": { "bech32": "=1.1.3", "bn.js": "=4.11.8", "bs58": "^4.0.1", "buffer-compare": "=1.1.1", - "elliptic": "=6.4.0", + "elliptic": "^6.5.3", "inherits": "=2.0.1", - "lodash": "=4.17.15" + "lodash": "^4.17.20" }, "dependencies": { - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - }, "inherits": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" } } }, @@ -2404,9 +2390,9 @@ } }, "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -9081,9 +9067,9 @@ } }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "node-gyp-build": { diff --git a/package.json b/package.json index aa87993..f63a082 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "node": ">=10.15.1" }, "dependencies": { - "@chris.troutner/bch-js": "^3.0.1", + "@psf/bch-js": "^3.5.5", "apidoc": "^0.23.0", "axios": "^0.19.0", "bitcore-lib-cash": "^8.20.3", diff --git a/src/routes/v3/blockbook.js b/src/routes/v3/blockbook.js index 7b5bd07..aafd7a6 100644 --- a/src/routes/v3/blockbook.js +++ b/src/routes/v3/blockbook.js @@ -23,7 +23,7 @@ const router = express.Router() const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() let _this diff --git a/src/routes/v3/electrumx.js b/src/routes/v3/electrumx.js index 5dbb4d0..8e30e7b 100644 --- a/src/routes/v3/electrumx.js +++ b/src/routes/v3/electrumx.js @@ -19,7 +19,7 @@ const config = require('../../../config') const RouteUtils = require('../../util/route-utils') const routeUtils = new RouteUtils() -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() let _this diff --git a/src/routes/v3/encryption.js b/src/routes/v3/encryption.js index d6285f4..859484a 100644 --- a/src/routes/v3/encryption.js +++ b/src/routes/v3/encryption.js @@ -15,7 +15,7 @@ const config = require('../../../config') const RouteUtils = require('../../util/route-utils') const routeUtils = new RouteUtils() -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // Use Blockbook for getting indexer data. diff --git a/src/routes/v3/full-node/blockchain.js b/src/routes/v3/full-node/blockchain.js index 94a5306..b021a48 100644 --- a/src/routes/v3/full-node/blockchain.js +++ b/src/routes/v3/full-node/blockchain.js @@ -17,7 +17,7 @@ const routeUtils = new RouteUtils() const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() let _this diff --git a/src/routes/v3/ninsight.js b/src/routes/v3/ninsight.js index 1a40e66..614be72 100644 --- a/src/routes/v3/ninsight.js +++ b/src/routes/v3/ninsight.js @@ -11,7 +11,7 @@ const express = require('express') const router = express.Router() -// const BCHJS = require('@chris.troutner/bch-js') +// const BCHJS = require('@psf/bch-js') // const bchjs = new BCHJS() // let _this diff --git a/src/routes/v3/route-utils.js b/src/routes/v3/route-utils.js index 1a4cba1..fe86c44 100644 --- a/src/routes/v3/route-utils.js +++ b/src/routes/v3/route-utils.js @@ -10,7 +10,7 @@ const wlogger = require('../../util/winston-logging') const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() module.exports = { diff --git a/src/routes/v3/services/slpdb.js b/src/routes/v3/services/slpdb.js index 9690e4e..c19c52e 100644 --- a/src/routes/v3/services/slpdb.js +++ b/src/routes/v3/services/slpdb.js @@ -1,6 +1,6 @@ const axios = require('axios') -const SLPSDK = require('@chris.troutner/bch-js') +const SLPSDK = require('@psf/bch-js') const SLP = new SLPSDK() class Slpdb { diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 4b85bbb..43e3b42 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -13,7 +13,7 @@ const Slpdb = require('./services/slpdb') // const strftime = require('strftime') const wlogger = require('../../util/winston-logging') -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // Used to convert error messages to strings, to safely pass to users. @@ -79,6 +79,7 @@ class Slp { _this.txsByAddressSingle ) _this.router.post('/generateSendOpReturn', _this.generateSendOpReturn) + _this.router.post('/hydrateUtxos', _this.hydrateUtxos) } // DRY error handler. @@ -1519,6 +1520,75 @@ class Slp { }) } } + + /** + * @api {post} /slp/hydrateUtxos/ hydrateUtxos + * @apiName SLP hydrateUtxos + * @apiGroup SLP + * @apiDescription Hydrate UTXO data with SLP information. + * + * Expects an array of UTXO objects as input. Returns an array of equal size. + * Returns UTXO data hydrated with token information. If the UTXO does not + * belong to a SLP transaction, it will return an isValid property set to + * false. If the UTXO is part of an SLP transaction, it will return the UTXO + * object with additional SLP information attached. An isValid property will + * be included. If its value is true, the UTXO is a valid SLP UTXO. If the + * value is null, then SLPDB has not yet processed that txid and validity has + * not been confirmed. + * + * @apiExample Example usage: + * curl -X POST "https://api.fullstack.cash/v3/slp/hydrateUtxos" -H "accept:application/json" -H "Content-Type: application/json" -d '{"utxos":[{"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 3, "value": "6816", "height": 606848, "confirmations": 13, "satoshis": 6816}, {"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 2, "value": "546", "height": 606848, "confirmations": 13, "satoshis": 546}]}' + * + * + */ + async hydrateUtxos (req, res, next) { + try { + const utxos = req.body.utxos + + // Validate inputs + if (!Array.isArray(utxos)) { + res.status(422) + return res.json({ + error: 'Input must be an array.' + }) + } + + if (!utxos.length) { + res.status(422) + return res.json({ + error: 'Array should not be empty' + }) + } + + if (utxos.length > 20) { + res.status(422) + return res.json({ + error: 'Array too long, max length is 20' + }) + } + + // Get Token Utxo Details + const details = await _this.bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log('details : ', details) + + res.status(200) + return res.json({ slpUtxos: details }) + } catch (err) { + wlogger.error('Error in slp.js/hydrateUtxos().', err) + + // Decode the error message. + const { msg, status } = routeUtils.decodeError(err) + if (msg) { + res.status(status) + return res.json({ error: msg }) + } + + res.status(500) + return res.json({ + error: 'Error in hydrateUtxos()' + }) + } + } } module.exports = Slp diff --git a/src/routes/v3/util.js b/src/routes/v3/util.js index 77f028b..13c663a 100644 --- a/src/routes/v3/util.js +++ b/src/routes/v3/util.js @@ -12,7 +12,7 @@ const blockbook = new Blockbook() const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // const BCHJS_TESTNET = 'https://testnet.bchjs.cash/v3/' diff --git a/src/routes/v3/xpub.js b/src/routes/v3/xpub.js index 353158f..fa1d9d4 100644 --- a/src/routes/v3/xpub.js +++ b/src/routes/v3/xpub.js @@ -16,7 +16,7 @@ const router = express.Router() const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // Connect the route endpoints to their handler functions. diff --git a/src/util/route-utils.js b/src/util/route-utils.js index 1636cba..a12abd4 100644 --- a/src/util/route-utils.js +++ b/src/util/route-utils.js @@ -10,7 +10,7 @@ const wlogger = require('./winston-logging') const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // let _this diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index 2ce5bbe..9f41c70 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -102,7 +102,7 @@ describe('#slp', () => { }) }) - describe('generateSendOpReturn', () => { + describe('#generateSendOpReturn', () => { it('should return OP_RETURN script', async () => { req.body.tokenUtxos = [ { @@ -121,4 +121,66 @@ describe('#slp', () => { assert.isNumber(result.outputs) }) }) + + describe('#hydrateUtxos', () => { + it('should return utxo details', async () => { + const utxos = [ + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 3, + value: '6816', + height: 606848, + confirmations: 13, + satoshis: 6816 + }, + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 2, + value: '546', + height: 606848, + confirmations: 13, + satoshis: 546 + } + ] + + req.body.utxos = utxos + const result = await slp.hydrateUtxos(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // Test the general structure of the output. + assert.isArray(result.slpUtxos) + assert.equal(result.slpUtxos.length, 2) + + // Test the non-slp UTXO. + assert.property(result.slpUtxos[0], 'txid') + assert.property(result.slpUtxos[0], 'vout') + assert.property(result.slpUtxos[0], 'value') + assert.property(result.slpUtxos[0], 'height') + assert.property(result.slpUtxos[0], 'confirmations') + assert.property(result.slpUtxos[0], 'satoshis') + assert.property(result.slpUtxos[0], 'isValid') + assert.equal(result.slpUtxos[0].isValid, false) + + // Test the slp UTXO. + assert.property(result.slpUtxos[1], 'txid') + assert.property(result.slpUtxos[1], 'vout') + assert.property(result.slpUtxos[1], 'value') + assert.property(result.slpUtxos[1], 'height') + assert.property(result.slpUtxos[1], 'confirmations') + assert.property(result.slpUtxos[1], 'satoshis') + assert.property(result.slpUtxos[1], 'isValid') + assert.equal(result.slpUtxos[1].isValid, true) + assert.property(result.slpUtxos[1], 'transactionType') + assert.property(result.slpUtxos[1], 'tokenId') + assert.property(result.slpUtxos[1], 'tokenTicker') + assert.property(result.slpUtxos[1], 'tokenName') + assert.property(result.slpUtxos[1], 'tokenDocumentUrl') + assert.property(result.slpUtxos[1], 'tokenDocumentHash') + assert.property(result.slpUtxos[1], 'decimals') + assert.property(result.slpUtxos[1], 'tokenType') + assert.property(result.slpUtxos[1], 'tokenQty') + }) + }) }) diff --git a/test/v3/slp.js b/test/v3/slp.js index 89e1dbc..34a4d36 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -888,6 +888,148 @@ describe('#SLP', () => { assert.isNumber(result.outputs) }) }) + + describe('#hydrateUtxos', () => { + it('should throw error if input is not an array.', async () => { + req.body.utxos = 'test' + + const result = await slpRoute.hydrateUtxos(req, res) + // console.log(`result: `, result) + + assert.hasAllKeys(result, ['error']) + assert.include(result.error, 'Input must be an array') + }) + + it('should throw error if Array is empty', async () => { + req.body.utxos = [] + + const result = await slpRoute.hydrateUtxos(req, res) + + assert.hasAllKeys(result, ['error']) + assert.include(result.error, 'Array should not be empty') + }) + + it('should throw error if Array is too long', async () => { + const utxo = { + txid: + 'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90', + vout: 3, + amount: 0.00002015, + satoshis: 2015, + height: 594892, + confirmations: 5 + } + + const utxos = [] + + // Populate array with 21 utxos + for (let i = 0; i < 21; i++) { + utxos.push(utxo) + } + + req.body.utxos = utxos + + const result = await slpRoute.hydrateUtxos(req, res) + + assert.hasAllKeys(result, ['error']) + assert.include(result.error, 'Array too long, max length is 20') + }) + + it('should return utxo details', async () => { + const utxos = [ + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 3, + value: '6816', + height: 606848, + confirmations: 13, + satoshis: 6816 + }, + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 2, + value: '546', + height: 606848, + confirmations: 13, + satoshis: 546 + } + ] + + // Mock the external network call. + sandbox.stub(slpRoute.bchjs.SLP.Utils, 'tokenUtxoDetails').resolves([ + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 3, + value: '6816', + height: 606848, + confirmations: 13, + satoshis: 6816, + isValid: false + }, + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 2, + value: '546', + height: 606848, + confirmations: 13, + satoshis: 546, + utxoType: 'token', + transactionType: 'send', + tokenId: + 'dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d', + tokenTicker: 'TAP', + tokenName: 'Thoughts and Prayers', + tokenDocumentUrl: '', + tokenDocumentHash: '', + decimals: 0, + tokenType: 1, + tokenQty: 5, + isValid: true + } + ]) + + req.body.utxos = utxos + const result = await slpRoute.hydrateUtxos(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // Test the general structure of the output. + assert.isArray(result.slpUtxos) + assert.equal(result.slpUtxos.length, 2) + + // Test the non-slp UTXO. + assert.property(result.slpUtxos[0], 'txid') + assert.property(result.slpUtxos[0], 'vout') + assert.property(result.slpUtxos[0], 'value') + assert.property(result.slpUtxos[0], 'height') + assert.property(result.slpUtxos[0], 'confirmations') + assert.property(result.slpUtxos[0], 'satoshis') + assert.property(result.slpUtxos[0], 'isValid') + assert.equal(result.slpUtxos[0].isValid, false) + + // Test the slp UTXO. + assert.property(result.slpUtxos[1], 'txid') + assert.property(result.slpUtxos[1], 'vout') + assert.property(result.slpUtxos[1], 'value') + assert.property(result.slpUtxos[1], 'height') + assert.property(result.slpUtxos[1], 'confirmations') + assert.property(result.slpUtxos[1], 'satoshis') + assert.property(result.slpUtxos[1], 'isValid') + assert.equal(result.slpUtxos[1].isValid, true) + assert.property(result.slpUtxos[1], 'transactionType') + assert.property(result.slpUtxos[1], 'tokenId') + assert.property(result.slpUtxos[1], 'tokenTicker') + assert.property(result.slpUtxos[1], 'tokenName') + assert.property(result.slpUtxos[1], 'tokenDocumentUrl') + assert.property(result.slpUtxos[1], 'tokenDocumentHash') + assert.property(result.slpUtxos[1], 'decimals') + assert.property(result.slpUtxos[1], 'tokenType') + assert.property(result.slpUtxos[1], 'tokenQty') + }) + }) }) /*