Compare commits

..
44 Commits
Author SHA1 Message Date
Chris Troutner 4c4a45841b Merge pull request #150 from Permissionless-Software-Foundation/ct-unstable
fix(package.json): Using /v5 in npm scripts
2021-06-16 19:17:14 -07:00
Chris Troutner 010afd10a3 Merge branch 'master' into ct-unstable 2021-06-16 19:13:03 -07:00
Chris Troutner 82b4d2ab9e fix(package.json): Using /v5 in npm scripts 2021-06-16 19:12:48 -07:00
Chris Troutner 8eb6a21dc2 Merge pull request #149 from Permissionless-Software-Foundation/ct-unstable
feat(/v5 REST API): Using the new v5 route in bch-api
2021-06-16 19:03:45 -07:00
Chris Troutner 5f2a762d4f feat(/v5 REST API): Using the new v5 route in bch-api 2021-06-16 17:00:08 -07:00
Chris Troutner 24e2c78d03 Merge pull request #148 from Permissionless-Software-Foundation/ct-unstable
fix(testnet): Removing testnet ninsight tests as they rarely pass
2021-05-22 07:58:39 -07:00
Chris Troutner eab02fdbc9 fix(testnet): Removing testnet ninsight tests as they rarely pass 2021-05-22 07:55:02 -07:00
Chris Troutner f7bd9c75db Merge pull request #147 from Permissionless-Software-Foundation/ct-unstable
fix(nft docs): Fixing a typo in docs
2021-05-21 15:54:07 -07:00
Chris Troutner 2a96f3a4f5 fix(nft docs): Fixing a typo in docs 2021-05-21 15:51:43 -07:00
Chris Troutner 3dc4a8ba6f Merge pull request #146 from Permissionless-Software-Foundation/ct-unstable
fix(dependencies): Updating dependencies. 0 npm vulns
2021-05-21 15:39:46 -07:00
Chris Troutner 65916f2c5c fix(dependencies): Updating dependencies. 0 npm vulns 2021-05-21 15:38:02 -07:00
Chris Troutner 18188bfdce Merge pull request #145 from Permissionless-Software-Foundation/nft-group-get-call
Return parent NFT group token information
2021-05-18 12:40:16 -07:00
Stoyan Zhekov 45acc2b0b7 Return parent NFT group token information 2021-05-16 14:22:33 +09:00
Chris Troutner 31913c3fb7 Merge pull request #144 from Permissionless-Software-Foundation/nft-children-get-call
NFT children get call
2021-05-15 08:54:58 -07:00
Stoyan Zhekov 2311643398 Example fixed 2021-05-14 21:58:52 +09:00
Stoyan Zhekov 7501aa0238 Lint fixes 2021-05-14 21:18:08 +09:00
Stoyan Zhekov 92a89a543d List NFT children per group 2021-05-14 21:17:07 +09:00
Chris Troutner cdf8171994 Merge pull request #143 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): Adding integration tests for bchn with no whi…
2021-05-03 19:18:34 -07:00
Chris Troutner 385baf0afd fix(integration tests): Adding integration tests for bchn with no whitelist SLPDB 2021-05-03 19:15:41 -07:00
Chris Troutner 9a9aababd2 Merge pull request #142 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): Adding tests for bchn with no whitelist SLPDB
2021-05-03 19:06:14 -07:00
Chris Troutner c228acd28a fix(integration tests): Adding tests for bchn with no whitelist SLPDB 2021-05-03 18:53:41 -07:00
Chris Troutner c1c140bdce Merge pull request #139 from Permissionless-Software-Foundation/dh-getblock
feat(getBlock): Added getBlock endpoint to bch-js
2021-04-07 13:07:05 -07:00
Daniel Gonzalez 54d9b80f26 feat(getBlock): Added getBlock endpoint to bch-js 2021-04-06 21:33:33 -04:00
Chris Troutner 961a190309 Merge pull request #138 from Permissionless-Software-Foundation/ct-unstable
fix(debugging): Removing all the debugging statements
2021-03-26 18:05:04 -08:00
Chris Troutner 2d2a91dfde fix(debugging): Removing all the debugging statements 2021-03-26 18:57:08 -07:00
Chris Troutner fbac18d456 Merge pull request #137 from Permissionless-Software-Foundation/ct-unstable
More connection reset debugging
2021-03-26 17:32:23 -08:00
Chris Troutner 62ae67ceab Logging axios options in decodeOpReturn 2021-03-26 18:29:48 -07:00
Chris Troutner 8e64d854cf fix(hydrateUtxos): Adding debugging info for axiosOptions 2021-03-26 18:26:18 -07:00
Chris Troutner 5fe8e515ff Merge pull request #136 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Adding debugging information
2021-03-26 16:30:50 -08:00
Chris Troutner f11297339b fix(decodeOpReturn): Adding debugging information 2021-03-26 17:27:57 -07:00
Chris Troutner 61d52c8ebe Merge pull request #135 from Permissionless-Software-Foundation/ct-unstable
fix(debugging): Adding additional debugging info to SLP utils
2021-03-26 12:53:35 -08:00
Chris Troutner d89bbe0bf6 fix(debugging): Adding additional debugging info to SLP utils 2021-03-26 13:51:29 -07:00
Chris Troutner 2a1c2500e6 Merge pull request #134 from Permissionless-Software-Foundation/ct-unstable
fix(slp): Adding more verbose error messages for SLP utility functions
2021-03-26 12:16:55 -08:00
Chris Troutner 8879a5170a fix(slp): Adding more verbose error messages for SLP utility functions 2021-03-26 13:11:40 -07:00
Chris Troutner b29cb7c12b Merge pull request #133 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): slowing down integration tests for the sake o…
2021-03-26 08:28:46 -07:00
Chris Troutner 69fa10d3a6 fix(integration tests): slowing down integration tests for the sake of rate limits 2021-03-26 08:25:39 -07:00
Chris Troutner d16431d165 Merge pull request #132 from Permissionless-Software-Foundation/ct-unstable
Adding Transaction.get()
2021-03-24 07:30:48 -07:00
Chris Troutner faa2972460 fix(Transaction.get()): Added documentation 2021-03-24 07:28:38 -07:00
Chris Troutner 5d3ea2b559 fix(Transaction.get()): Added unit tests 2021-03-24 07:24:08 -07:00
Chris Troutner 40fa6440f4 feat(Transactions.get()): Added tx hydration method 2021-03-23 18:49:27 -07:00
Chris Troutner ce951afddb fix(RawTransactions.getTxData): Adding BCH value to tx inputs 2021-03-23 17:37:00 -07:00
Chris Troutner d2e847d3fb feat(transactions): Created transaction library 2021-03-23 17:11:37 -07:00
Chris Troutner fe697455c0 Merge branch 'master' into ct-unstable 2021-03-09 08:09:01 -08:00
Chris Troutner 25832d52fd fix(raw-transactions): Adding proper axios header for Basic Auth 2021-03-09 08:04:14 -08:00
41 changed files with 17635 additions and 407 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
Examples have been moved to this repository:
Examples have been moved to this repository
https://github.com/Permissionless-Software-Foundation/bch-js-examples
+16118 -76
View File
File diff suppressed because it is too large Load Diff
+14 -12
View File
@@ -4,24 +4,26 @@
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
"author": "Chris Troutner <chris.troutner@gmail.com>",
"contributors": [
"Gabriel Cardona <gabriel@bitcoin.com>"
"Gabriel Cardona <gabriel@bitcoin.com>",
"Stoyan Zhekov <zh@zhware.net>"
],
"main": "src/bch-js",
"scripts": {
"test": "nyc mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
"test:integration": "npm run test:integration:bchn",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:nft": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 -g '#nft1' test/integration/chains/bchn/slp.js",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/chains/testnet/",
"test:integration:local:abc": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:local:bchn": "export 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/chains/testnet",
"test:integration:decatur:bchn": "export RESTURL=http://192.168.0.36:3000/v4/ && mocha --timeout 30000 test/integration/",
"test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v4/ && mocha -g '#hydrateUtxosWL' --timeout 30000 test/integration/",
"test:temp": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 -g '#hydrateUtxos' test/integration/",
"test:temp2": "mocha --timeout=30000 -g '#_hydrateUtxo' test/unit/",
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#hydrateUtxos' test/integration/",
"test:temp4": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 -g '#decodeOpReturn' test/integration/",
"test:integration:local:abc": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:local:bchn": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v5/ mocha --timeout 30000 test/integration/chains/testnet",
"test:integration:decatur:bchn": "export RESTURL=http://192.168.0.36:3000/v5/ && mocha --timeout 30000 test/integration/",
"test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v5/ && mocha --timeout 30000 test/integration/",
"test:temp": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
"test:temp2": "mocha --timeout=30000 -g '#TransactionLib' test/unit/",
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v5/ && mocha --timeout 30000 -g '#Transaction' test/integration/",
"test:temp4": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#decodeOpReturn' test/integration/",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs",
+1 -1
View File
@@ -6,7 +6,7 @@ const coininfo = require('@psf/coininfo')
class Address {
constructor (config) {
const tmp = {}
if (!config || !config.restURL) tmp.restURL = 'https://api.bchjs.cash/v4/'
if (!config || !config.restURL) tmp.restURL = 'https://api.bchjs.cash/v5/'
else tmp.restURL = config.restURL
this.restURL = tmp.restURL
+4 -2
View File
@@ -7,8 +7,8 @@
*/
// bch-api mainnet.
const DEFAULT_REST_API = 'https://api.fullstack.cash/v4/'
// const DEFAULT_REST_API = "http://localhost:3000/v4/"
const DEFAULT_REST_API = 'https://api.fullstack.cash/v5/'
// const DEFAULT_REST_API = "http://localhost:3000/v5/"
// local deps
const BitcoinCash = require('./bitcoincash')
@@ -31,6 +31,7 @@ const SLP = require('./slp/slp')
const IPFS = require('./ipfs')
const Encryption = require('./encryption')
const Utxo = require('./utxo')
const Transaction = require('./transaction')
// Indexers
const Ninsight = require('./ninsight')
@@ -113,6 +114,7 @@ class BCHJS {
this.IPFS = new IPFS()
this.Utxo = new Utxo(libConfig)
this.Transaction = new Transaction(libConfig)
}
}
+32 -22
View File
@@ -68,8 +68,8 @@ class Blockchain {
* @apiName getBlock
* @apiGroup Blockchain
* @apiDescription
* If verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is true, returns an Object with information about block hash.
*
* If verbose is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is 1, returns an Object with information about block hash.
* If verbose is 2, returns an Object with information about block hash and information about tx.
* @apiExample Example usage:
* (async () => {
* try {
@@ -80,28 +80,38 @@ class Blockchain {
* }
* })()
*
* // { hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
* // confirmations: 528236,
* // size: 216,
* // height: 1000,
* // version: 1,
* // versionHex: '00000001',
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
* // tx:
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
* // time: 1232346882,
* // mediantime: 1232344831,
* // nonce: 2595206198,
* // bits: '1d00ffff',
* // difficulty: 1,
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6' }
* // {
* // hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
* // confirmations: 528236,
* // size: 216,
* // height: 1000,
* // version: 1,
* // versionHex: '00000001',
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
* // tx:
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
* // time: 1232346882,
* // mediantime: 1232344831,
* // nonce: 2595206198,
* // bits: '1d00ffff',
* // difficulty: 1,
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6'
* // }
*/
async getBlock (blockhash, verbose = true) {
async getBlock (blockhash, verbosity = 1) {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBlock/${blockhash}?verbose=${verbose}`,
// Input validation
if (!blockhash || typeof blockhash !== 'string') {
throw new Error('blockhash must be a string')
}
const response = await axios.post(
`${this.restURL}blockchain/getblock`,
{
blockhash,
verbosity
},
_this.axiosOptions
)
return response.data
+5 -90
View File
@@ -1,7 +1,5 @@
const axios = require('axios')
const SlpUtils = require('./slp/utils')
let _this
class RawTransactions {
@@ -29,9 +27,6 @@ class RawTransactions {
// Encapsulate dependencies
this.axios = axios
// Dependencies
this.slpUtils = new SlpUtils(config)
_this = this
}
@@ -116,9 +111,7 @@ class RawTransactions {
data: {
hexes: hex
},
headers: {
authorization: `Token ${_this.apiToken}`
}
headers: _this.axiosOptions.headers
}
const response = await axios(options)
@@ -183,9 +176,7 @@ class RawTransactions {
data: {
hexes: script
},
headers: {
authorization: `Token ${this.apiToken}`
}
headers: _this.axiosOptions.headers
}
const response = await axios(options)
@@ -328,7 +319,8 @@ class RawTransactions {
retArray.push({
vin: i,
address: voutSender.scriptPubKey.addresses[0]
address: voutSender.scriptPubKey.addresses[0],
value: voutSender.value
})
}
@@ -382,84 +374,7 @@ class RawTransactions {
// Add the input address to the transaction data.
for (let i = 0; i < inAddrs.length; i++) {
txDetails.vin[i].address = inAddrs[i].address
}
return txDetails
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
// 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
txDetails.vin[i].value = inAddrs[i].value
}
return txDetails
+100 -2
View File
@@ -8,16 +8,21 @@
(Parent) token.
*/
// Public npm libraries
const axios = require('axios')
// Local libraries.
const Address = require('./address')
// const BigNumber = require('bignumber.js')
const slpMdm = require('slp-mdm')
let _this
// const addy = new Address()
let addy
const TransactionBuilder = require('../transaction-builder')
class TokenType1 {
class Nft1 {
constructor (config) {
this.restURL = config.restURL
@@ -25,6 +30,8 @@ class TokenType1 {
// Instantiate the transaction builder.
TransactionBuilder.setAddress(addy)
_this = this
}
/**
@@ -412,6 +419,97 @@ class TokenType1 {
throw err
}
}
/**
* @api SLP.NFT1.listNFTGroupChildren() listNFTGroupChildren()
* @apiName listNFTGroupChildren
* @apiGroup SLP NFT1
* @apiDescription Return list of NFT children tokens in a NFT Group.
* It's assumed provided groupId parameter is for an NFT Group token (type=129)
*
* Returns an Array with GENESIS transaction IDs of the children tokens.
*
* @apiExample Example usage:
*
* const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
* const children = await bchjs.SLP.Nft1.listNFTGroupChildren(groupId)
*
* children = {
* "nftChildren": [
* "45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9",
* "928ce61fe1006b1325a0ba0dce700bf83986a6f0691ba26e121c9ac035d12a55"
* ]
* }
*/
async listNFTGroupChildren (groupId) {
try {
if (typeof groupId === 'string') {
const response = await axios.get(
`${this.restURL}slp/nftChildren/${groupId}`,
_this.axiosOptions
)
return response.data
}
throw new Error('groupId must be a string.')
} catch (error) {
// console.log('Error in listNFTGroupChildren()')
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api SLP.NFT1.parentNFTGroup() parentNFTGroup()
* @apiName parentNFTGroup
* @apiGroup SLP NFT1
* @apiDescription Return parent NFT Group information for a given NFT child token.
* It's assumed provided groupId parameter is for an NFT Child token (type=65)
*
* Returns a JSON with NFT group information.
*
* @apiExample Example usage:
*
* const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
* const group = await bchjs.SLP.Nft1.parentNFTGroup(tokenId)
*
* group = {
* "nftGroup": {
* "decimals": 0,
* "timestamp": "2021-05-03 10:36:01",
* "timestamp_unix": 1620038161,
* "versionType": 129,
* "documentUri": "psfoundation.cash",
* "symbol": "PSF.TEST.GROUP",
* "name": "PSF Test NFT Group",
* "containsBaton": true,
* "id": "68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a",
* "documentHash": null,
* "initialTokenQty": 1000000,
* "blockCreated": 686117,
* "totalMinted": null,
* "totalBurned": null,
* "circulatingSupply": null
* }
* }
*/
async parentNFTGroup (tokenId) {
try {
if (typeof tokenId === 'string') {
const response = await axios.get(
`${this.restURL}slp/nftGroup/${tokenId}`,
_this.axiosOptions
)
return response.data
}
throw new Error('tokenId must be a string.')
} catch (error) {
// console.log('Error in parentNFTGroup()')
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = TokenType1
module.exports = Nft1
+1 -1
View File
@@ -41,7 +41,7 @@ class SLP {
this.Address = new Address(config)
this.ECPair = ECPair
this.TokenType1 = new TokenType1(config)
this.NFT1 = new NFT1(this.restURL)
this.NFT1 = new NFT1(config)
this.Utils = new Utils(config)
}
}
+2 -5
View File
@@ -1097,11 +1097,6 @@ class Utils {
* "tokenType": 1
* }
*/
// CT 1/11/20: Refactored to comply with this GitHub Issue:
// https://github.com/Bitcoin-com/slp-sdk/issues/84
// CT 5/31/20: Refactored to use slp-parse library.
async tokenUtxoDetails (utxos, usrObj = null) {
try {
// Throw error if input is not an array.
@@ -1163,6 +1158,7 @@ class Utils {
return outAry
} catch (error) {
// console.log('Error in tokenUtxoDetails()')
if (error.response && error.response.data) throw error.response.data
throw error
}
@@ -1643,6 +1639,7 @@ class Utils {
// could not be validated.
return isValid
} catch (error) {
// console.log('Error in waterfallValidateTxid()')
if (error.response && error.response.data) throw error.response.data
throw error
}
+145
View File
@@ -0,0 +1,145 @@
/*
High-level functions for working with Transactions
*/
const RawTransaction = require('./raw-transactions')
const SlpUtils = require('./slp/utils')
class Transaction {
constructor (config) {
// Encapsulate dependencies
this.slpUtils = new SlpUtils(config)
this.rawTransaction = new RawTransaction(config)
}
/**
* @api Transaction.get() get()
* @apiName get
* @apiGroup Transaction
* @apiDescription
* Returns an object of transaction data, including addresses for input UTXOs.
* If it is a SLP token transaction, the token information for inputs and
* outputs will also be included.
*
* This is an API heavy call. This function will only work with a single txid.
* It does not yet support an array of TXIDs.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txData = await bchjs.Transaction.get("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
* console.log(txData);
* } catch(error) {
* console.error(error)
* }
* })()
*/
async get (txid) {
try {
if (typeof txid !== 'string') {
throw new Error('Input must be a string or array of strings.')
}
const txDetails = await this.rawTransaction.getTxData(txid)
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
// Setup default SLP properties.
txDetails.isValidSLPTx = false
// 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)}`)
// Get Genesis data for this token.
const genesisData = await this.slpUtils.decodeOpReturn(
outTokenData.tokenId
// decodeOpReturnCache
// usrObj // pass user data when making an internal call.
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// Add token information to the tx details object.
txDetails.tokenTxType = outTokenData.txType
txDetails.tokenId = outTokenData.tokenId
txDetails.tokenTicker = genesisData.ticker
txDetails.tokenName = genesisData.name
txDetails.tokenDecimals = genesisData.decimals
txDetails.tokenUri = genesisData.documentUri
txDetails.tokenDocHash = genesisData.documentHash
// Add the token quantity to each output.
for (let i = 0; i < outTokenData.amounts.length; i++) {
const rawQty = outTokenData.amounts[i]
const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
txDetails.vout[i + 1].tokenQty = realQty
}
// Add tokenQty = null to any outputs that don't have a value.
for (let i = 0; i < txDetails.vout.length; i++) {
const thisVout = txDetails.vout[i]
if (!thisVout.tokenQty && thisVout.tokenQty !== 0) {
thisVout.tokenQty = null
}
}
// 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) {
const realQty =
Number(tokenQty) / Math.pow(10, txDetails.tokenDecimals)
thisVin.tokenQty = realQty
// txDetails.vin[i].tokenQty = tokenQty
} else {
thisVin.tokenQty = null
}
} catch (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
}
}
// Finally, validate the SLP TX.
// this.slpUtils.waterfallValidateTxid(txid, usrObj)
txDetails.isValidSLPTx = await this.slpUtils.waterfallValidateTxid(txid)
} catch (err) {
// console.log('Error: ', err)
// If decoding the op_return fails, then it's not an SLP transaction,
// and the non-hyrated TX details can be returned.
return txDetails
}
return txDetails
} catch (err) {
console.error('Error in transactions.js/get()')
throw err
}
}
}
module.exports = Transaction
@@ -14,9 +14,9 @@
const assert = require('chai').assert
// const RESTURL = `https://abc.fullstack.cash/v4/`
const RESTURL = 'https://bchn.fullstack.cash/v4/'
// const RESTURL = `http://localhost:3000/v4/`
// const RESTURL = `https://abc.fullstack.cash/v5/`
const RESTURL = 'https://bchn.fullstack.cash/v5/'
// const RESTURL = `http://localhost:3000/v5/`
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL })
@@ -13,9 +13,9 @@ const PRO_PASS = 'testpassword'
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`,
restURL: 'https://abc.fullstack.cash/v4/',
// restURL: `http://localhost:3000/v4/`,
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https://abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
authPass: PRO_PASS
})
+2 -2
View File
@@ -18,8 +18,8 @@ const JWT_TOKEN =
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
restURL: 'https://api.fullstack.cash/v4/',
// restURL: `http://localhost:3000/v4/`,
restURL: 'https://api.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN
})
@@ -18,9 +18,9 @@ const JWT_TOKEN =
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`,
restURL: 'https:/abc.fullstack.cash/v4/',
// restURL: `http://localhost:3000/v4/`,
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https:/abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN
})
@@ -65,7 +65,7 @@ describe('#full node rate limits', () => {
it('should throw error for more than 100 RPM to fullnode', async () => {
try {
console.log('This test usually doesn\'t pass, because of latency.')
console.log("This test usually doesn't pass, because of latency.")
for (let i = 0; i < 100; i++) await bchjs.Control.getNetworkInfo()
} catch (err) {
// console.log(`validating after 10th call`)
+3 -3
View File
@@ -14,9 +14,9 @@ const JWT_TOKEN =
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`,
restURL: 'https://abc.fullstack.cash/v4/',
// restURL: `http://localhost:3000/v4/`,
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https://abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN
})
+37
View File
@@ -276,6 +276,43 @@ describe('#blockchain', () => {
assert.equal(result, null)
})
})
describe('#getBlock', () => {
it('should get block information with default verbosity', async () => {
const blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
const result = await bchjs.Blockchain.getBlock(blockhash)
assert.hasAllKeys(result, [
'hash',
'confirmations',
'size',
'height',
'version',
'versionHex',
'merkleroot',
'tx',
'time',
'mediantime',
'nonce',
'bits',
'difficulty',
'chainwork',
'nTx',
'previousblockhash',
'nextblockhash'
])
assert.isArray(result.tx)
})
it('should get block information with verbosity 0', async () => {
const blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
const verbosity = 0
const result = await bchjs.Blockchain.getBlock(blockhash, verbosity)
assert.isString(result)
})
})
})
function sleep (ms) {
@@ -0,0 +1,71 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
TODO
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
/*
Testing sentRawTransaction isn't really possible with an integration test,
as the endpoint really needs an e2e test to be properly tested. The tests
below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently.
*/
describe('sendRawTransaction', () => {
it('should send a single transaction hex', async () => {
try {
const hex =
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'Missing inputs')
}
})
it('should send an array of tx hexes', async () => {
try {
const hexes = [
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'Missing inputs')
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -0,0 +1,273 @@
/*
Integration tests for the bchjs covering SLP tokens.
These tests are specific to the ABC chain.
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../../../src/bch-js')
let bchjs
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 1
}
describe('#SLP', () => {
// before(() => {
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
// })
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS()
})
describe('#util', () => {
describe('#decodeOpReturn', () => {
it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [
// Malformed SLP tx
{
note: 'Malformed SLP tx',
tx_hash:
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1,
value: 546
},
// Normal TX (non-SLP)
{
note: 'Normal TX (non-SLP)',
tx_hash:
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
tx_pos: 0,
value: 400000
},
// Valid PSF SLP tx
{
note: 'Valid PSF SLP tx',
tx_hash:
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
tx_pos: 1,
value: 546
},
// Valid SLP token not in whitelist
{
note: 'Valid SLP token not in whitelist',
tx_hash:
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1,
value: 546
},
// Token send on BCHN network.
{
note: 'Token send on BCHN network',
tx_hash:
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
tx_pos: 1,
value: 546
},
// Token send on ABC network.
{
note: 'Token send on ABC network',
tx_hash:
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
tx_pos: 1,
value: 546
},
// Known invalid SLP token send of PSF tokens.
{
note: 'Known invalid SLP token send of PSF tokens',
tx_hash:
'2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// Malformed SLP tx
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
assert.equal(data[0].isValid, false)
// Normal TX (non-SLP)
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
assert.equal(data[1].isValid, false)
// Valid PSF SLP tx
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
assert.equal(data[2].isValid, true)
// Valid SLP token not in whitelist
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
assert.equal(data[3].isValid, true)
// Token send on BCHN network
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
assert.equal(data[4].isValid, true)
// Token send on ABC network
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
assert.equal(data[5].isValid, null)
// Known invalid SLP token send of PSF tokens
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
assert.equal(data[6].isValid, false)
})
})
describe('#validateTxid3', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, null)
})
it('should validate a known valid TXID', async () => {
const txid =
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, true)
})
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Unprocessed SLP TX
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
]
const result = await bchjs.SLP.Utils.validateTxid3(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() (no whitelist SLPDB) but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
// Note: This should change from null to true once SLPDB finishes indexing.
assert.equal(result[4].txid, txids[4])
// True when no whitelist SLPDB is used.
assert.equal(result[4].valid, true)
})
})
describe('#validateTxid', () => {
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Token send on BCHN network.
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
// Token send on ABC network.
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
]
const result = await bchjs.SLP.Utils.validateTxid(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, true)
assert.equal(result[5].txid, txids[5])
assert.equal(result[5].valid, null)
})
})
})
})
// Promise-based sleep function
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -0,0 +1,105 @@
/*
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#UTXO', () => {
beforeEach(async () => {
// sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle an array of addresses', async () => {
const addr = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
]
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle NFTs and minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
assert.isArray(result[0].slpUtxos.type1.mintBatons)
assert.isArray(result[0].slpUtxos.type1.tokens)
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
assert.isArray(result[0].slpUtxos.nft.groupTokens)
assert.isArray(result[0].slpUtxos.nft.tokens)
})
})
describe('#findBiggestUtxo', () => {
it('should sort UTXOs from Electrumx', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
it('should sort UTXOs from Utxos.get()', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const utxos = await bchjs.Utxo.get(addr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -20,7 +20,7 @@ util.inspect.defaultOptions = {
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
/*
+26 -1
View File
@@ -25,7 +25,7 @@ describe('#SLP', () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS()
})
@@ -264,6 +264,31 @@ describe('#SLP', () => {
})
})
})
describe('#nft1', () => {
describe('#listNFTGroupChildren', () => {
it('should return array of children GENESIS transactions IDs', async () => {
const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
const result = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftChildren')
assert.isArray(result.nftChildren)
})
})
describe('#parentNFTGroup', () => {
it('should return parent NFT group information', async () => {
const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
const result = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftGroup')
assert.property(result.nftGroup, 'id')
assert.property(result.nftGroup, 'name')
assert.property(result.nftGroup, 'symbol')
assert.property(result.nftGroup, 'documentUri')
assert.property(result.nftGroup, 'versionType')
assert.property(result.nftGroup, 'initialTokenQty')
})
})
})
})
// Promise-based sleep function
@@ -11,7 +11,7 @@ describe('#UTXO', () => {
beforeEach(async () => {
// sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
describe('#get', () => {
@@ -12,11 +12,11 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
+1 -1
View File
@@ -7,7 +7,7 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
+1 -1
View File
@@ -5,7 +5,7 @@ const util = require('util')
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
+130 -128
View File
@@ -1,9 +1,9 @@
const chai = require('chai')
const assert = chai.assert
// const chai = require('chai')
// const assert = chai.assert
const sinon = require('sinon')
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ ninsightURL: 'https://trest.bitcoin.com/v2' })
// const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ ninsightURL: 'https://trest.bitcoin.com/v2' })
describe('#Ninsight', () => {
let sandbox
@@ -16,130 +16,132 @@ describe('#Ninsight', () => {
afterEach(() => sandbox.restore())
describe('#utxo', () => {
it('should GET utxos for a single address', async () => {
const addr = 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr'
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result[0], 'utxos')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'slpAddress')
assert.property(result[0], 'scriptPubKey')
assert.property(result[0], 'asm')
assert.isArray(result[0].utxos)
assert.property(result[0].utxos[0], 'txid')
assert.property(result[0].utxos[0], 'vout')
assert.property(result[0].utxos[0], 'amount')
assert.property(result[0].utxos[0], 'satoshis')
assert.property(result[0].utxos[0], 'height')
assert.property(result[0].utxos[0], 'confirmations')
})
it('should POST utxo details for an array of addresses', async () => {
const addr = [
'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
'bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul'
]
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isArray(result[0].utxos)
assert.property(result[0], 'utxos')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'slpAddress')
assert.property(result[0], 'scriptPubKey')
assert.property(result[0], 'asm')
// assert.hasAnyKeys(result[0][0], [
// "txid",
// "vout",
// "value",
// "height",
// "confirmations"
// ])
})
})
describe('#transactions', () => {
it('should POST transactions history for a single address', async () => {
const addr = 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr'
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'txs')
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], 'txid')
assert.property(result[0].txs[0], 'vin')
assert.property(result[0].txs[0], 'vout')
})
it('should POST transactions history for an array of addresses', async () => {
const addr = [
'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
'bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul'
]
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'txs')
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], 'txid')
assert.property(result[0].txs[0], 'vin')
assert.property(result[0].txs[0], 'vout')
})
})
describe('#txDetails', () => {
it('should POST transactions details for a single address', async () => {
const txid =
'76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea'
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.property(result[0], 'version')
assert.property(result[0], 'locktime')
assert.property(result[0], 'vin')
assert.property(result[0], 'vout')
assert.property(result[0], 'blockhash')
assert.property(result[0], 'blockheight')
assert.property(result[0], 'confirmations')
assert.property(result[0], 'time')
assert.property(result[0], 'blocktime')
assert.property(result[0], 'valueOut')
assert.property(result[0], 'size')
})
it('should POST transactions details for an array of addresses', async () => {
const txid = [
'f191d1062789d7071da6f2168ba306869a829294f6b1c09d03492db4ca3a8e77',
'76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea'
]
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.property(result[0], 'vin')
assert.property(result[0], 'vout')
})
})
// describe('#utxo', () => {
// it('should GET utxos for a single address', async () => {
// const addr = 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr'
//
// const result = await bchjs.Ninsight.utxo(addr)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.property(result[0], 'utxos')
// assert.property(result[0], 'legacyAddress')
// assert.property(result[0], 'cashAddress')
// assert.property(result[0], 'slpAddress')
// assert.property(result[0], 'scriptPubKey')
// assert.property(result[0], 'asm')
//
// assert.isArray(result[0].utxos)
//
// assert.property(result[0].utxos[0], 'txid')
// assert.property(result[0].utxos[0], 'vout')
// assert.property(result[0].utxos[0], 'amount')
// assert.property(result[0].utxos[0], 'satoshis')
// assert.property(result[0].utxos[0], 'height')
// assert.property(result[0].utxos[0], 'confirmations')
// })
//
// it('should POST utxo details for an array of addresses', async () => {
// const addr = [
// 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
// 'bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul'
// ]
//
// const result = await bchjs.Ninsight.utxo(addr)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
// assert.isArray(result[0].utxos)
//
// assert.property(result[0], 'utxos')
// assert.property(result[0], 'legacyAddress')
// assert.property(result[0], 'cashAddress')
// assert.property(result[0], 'slpAddress')
// assert.property(result[0], 'scriptPubKey')
// assert.property(result[0], 'asm')
//
// // assert.hasAnyKeys(result[0][0], [
// // "txid",
// // "vout",
// // "value",
// // "height",
// // "confirmations"
// // ])
// })
// })
//
// describe('#transactions', () => {
// it('should POST transactions history for a single address', async () => {
// const addr = 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr'
//
// const result = await bchjs.Ninsight.transactions(addr)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
// assert.property(result[0], 'cashAddress')
// assert.property(result[0], 'legacyAddress')
// assert.property(result[0], 'txs')
// assert.isArray(result[0].txs)
// assert.property(result[0].txs[0], 'txid')
// assert.property(result[0].txs[0], 'vin')
// assert.property(result[0].txs[0], 'vout')
// })
// it('should POST transactions history for an array of addresses', async () => {
// const addr = [
// 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
// 'bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul'
// ]
//
// const result = await bchjs.Ninsight.transactions(addr)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
// assert.property(result[0], 'cashAddress')
// assert.property(result[0], 'legacyAddress')
// assert.property(result[0], 'txs')
// assert.isArray(result[0].txs)
// assert.property(result[0].txs[0], 'txid')
// assert.property(result[0].txs[0], 'vin')
// assert.property(result[0].txs[0], 'vout')
// })
// })
//
// describe('#txDetails', () => {
// it('should POST transactions details for a single address', async () => {
// const txid =
// '76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea'
//
// const result = await bchjs.Ninsight.txDetails(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
// assert.property(result[0], 'txid')
// assert.property(result[0], 'version')
// assert.property(result[0], 'locktime')
// assert.property(result[0], 'vin')
// assert.property(result[0], 'vout')
// assert.property(result[0], 'blockhash')
// assert.property(result[0], 'blockheight')
// assert.property(result[0], 'confirmations')
// assert.property(result[0], 'time')
// assert.property(result[0], 'blocktime')
// assert.property(result[0], 'valueOut')
// assert.property(result[0], 'size')
// })
// it('should POST transactions details for an array of addresses', async () => {
// const txid = [
// 'f191d1062789d7071da6f2168ba306869a829294f6b1c09d03492db4ca3a8e77',
// '76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea'
// ]
//
// const result = await bchjs.Ninsight.txDetails(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
// assert.property(result[0], 'txid')
// assert.property(result[0], 'vin')
// assert.property(result[0], 'vout')
// })
// })
})
function sleep (ms) {
@@ -10,11 +10,11 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
+2 -2
View File
@@ -7,11 +7,11 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
@@ -1,6 +1,6 @@
#!/bin/bash
export RESTURL=https://free-test.fullstack.cash/v4/
export RESTURL=https://free-test.fullstack.cash/v5/
export NETWORK=testnet
export IS_USING_FREE_TIER=true
+2 -2
View File
@@ -8,11 +8,11 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
+26 -25
View File
@@ -1,6 +1,5 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
Integration tests for the bchjs.
TODO
*/
@@ -263,6 +262,7 @@ describe('#rawtransaction', () => {
assert.equal(result.length, 1)
assert.property(result[0], 'vin')
assert.property(result[0], 'address')
assert.property(result[0], 'value')
})
})
@@ -274,32 +274,33 @@ describe('#rawtransaction', () => {
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
})
})
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)
})
})
// 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) {
+1 -1
View File
@@ -24,7 +24,7 @@ describe('#SLP', () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS()
})
@@ -0,0 +1,60 @@
/*
Integration tests for the transaction.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
describe('#Transaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#get', () => {
it('should get details about a non-SLP transaction', async () => {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[0].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, false)
})
it('should get details about a SLP transaction', async () => {
const txid =
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vout[0], 'tokenQty')
assert.equal(result.vout[0].tokenQty, null)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result.vin[0], 'tokenQty')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, true)
})
})
})
+28 -7
View File
@@ -61,16 +61,40 @@ describe('#Blockchain', () => {
it('should get block by hash', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, 'get').returns(resolved)
sandbox.stub(axios, 'post').returns(resolved)
bchjs.Blockchain.getBlock(
const blockhash =
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
)
bchjs.Blockchain.getBlock(blockhash)
.then(result => {
assert.deepStrictEqual(data, result)
})
.then(done, done)
})
it('should throw error if blockhash is not provided', async () => {
try {
await bchjs.Blockchain.getBlock()
assert2.fail('Unexpected result')
} catch (err) {
assert2.include(err.message, 'blockhash must be a string')
}
})
it('should handle response error', async () => {
try {
const error = new Error()
error.response = {
data: 'Test Error'
}
sandbox.stub(axios, 'post').throws(error)
const blockhash =
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
await bchjs.Blockchain.getBlock(blockhash)
assert2.fail('Unexpected result')
} catch (err) {
assert2.include(err, 'Test Error')
}
})
})
describe('#getBlockchainInfo', () => {
@@ -355,10 +379,7 @@ describe('#Blockchain', () => {
try {
await bchjs.Blockchain.getTxOut('badtxid')
} catch (err) {
assert2.include(
err.message,
'txid needs to be a proper transaction ID'
)
assert2.include(err.message, 'txid needs to be a proper transaction ID')
}
})
+29
View File
@@ -119,6 +119,33 @@ const mockTokens = [
}
]
const mockNftChildrenList = {
nftChildren: [
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9',
'928ce61fe1006b1325a0ba0dce700bf83986a6f0691ba26e121c9ac035d12a55'
]
}
const mockNftGroup = {
nftGroup: {
decimals: 0,
timestamp: '2021-05-03 10:36:01',
timestamp_unix: 1620038161,
versionType: 129,
documentUri: 'psfoundation.cash',
symbol: 'PSF.TEST.GROUP',
name: 'PSF Test NFT Group',
containsBaton: true,
id: '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a',
documentHash: null,
initialTokenQty: 1000000,
blockCreated: 686117,
totalMinted: null,
totalBurned: null,
circulatingSupply: null
}
}
const balancesForAddress = [
{
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
@@ -1388,6 +1415,8 @@ module.exports = {
mockList,
mockToken,
mockTokens,
mockNftChildrenList,
mockNftGroup,
balancesForAddress,
balancesForAddresses,
mockRawTx,
+184
View File
@@ -0,0 +1,184 @@
/*
Mocking data used in the transaction-unit.js tests.
*/
const nonSlpTxDetails = {
txid: '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
hash: '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
version: 2,
size: 225,
locktime: 0,
vin: [
{
txid: '5f09d317e24c5d376f737a2711f3bd1d381abdb41743fff3819b4f76382e1eac',
vout: 1,
scriptSig: {
asm:
'3044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c[ALL|FORKID] 038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9',
hex:
'473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9'
},
sequence: 4294967295,
address: 'bitcoincash:qqxy8hycqe89j7wa79gnggq6z3gaqu2uvqy26xehfe',
value: 0.00047504
}
],
vout: [
{
value: 0.00001,
n: 0,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 2fe2c4c5ef359bb2fe1a849f891cecffbcfb4f77 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9']
}
},
{
value: 0.00046256,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 2dbf5e1804c39a497b908c876097d63210c84902 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9142dbf5e1804c39a497b908c876097d63210c8490288ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqkm7hscqnpe5jtmjzxgwcyh6ceppjzfqg3jdn422e']
}
}
],
hex:
'0200000001ac1e2e38764f9b81f3ff4317b4bd1a381dbdf311277a736f375d4ce217d3095f010000006a473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9ffffffff02e8030000000000001976a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788acb0b40000000000001976a9142dbf5e1804c39a497b908c876097d63210c8490288ac00000000',
blockhash: '0000000000000000010903a1fc4274499037c9339be9ec7338ee980331c20ce5',
confirmations: 77741,
time: 1569792892,
blocktime: 1569792892
}
const slpTxDetails = {
txid: '266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1',
hash: '266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1',
version: 2,
size: 479,
locktime: 0,
vin: [
{
txid: 'abc685f1f2a95f51e5e05a350f3fb9c74676e9f78c835b2a019c888ac0a2a736',
vout: 2,
scriptSig: {
asm:
'3045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f7[ALL|FORKID] 0245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07ac',
hex:
'483045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f741210245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07ac'
},
sequence: 4294967295,
address: 'bitcoincash:qzv7t2pzn2d0pklnetdjt65crh6fe8vnhuwvhsk2nn',
value: 0.00000546
},
{
txid: '58c8576404c01c23a224053307399483d3a070599b3e9eb6d45be9714b8d6856',
vout: 1,
scriptSig: {
asm:
'30430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db12753[ALL|FORKID] 0209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505',
hex:
'4630430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db1275341210209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505'
},
sequence: 4294967295,
address: 'bitcoincash:qppzuxemgqyxf07nz3kan33gmc83mf3z3yz295c4s7',
value: 0.68369626
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm:
'OP_RETURN 5262419 1 1145980243 497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7 0000000005f5e100 00005ad7e49d9100',
hex:
'6a04534c500001010453454e4420497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7080000000005f5e1000800005ad7e49d9100',
type: 'nulldata'
}
},
{
value: 0.00000546,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 36be3b7d185a85b6cf6fc61d63c16f2f10e54260 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91436be3b7d185a85b6cf6fc61d63c16f2f10e5426088ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqmtuwmarpdgtdk0dlrp6c7pduh3pe2zvqrkys2ex8']
}
},
{
value: 0.00000546,
n: 2,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 99e5a8229a9af0dbf3cadb25ea981df49c9d93bf OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91499e5a8229a9af0dbf3cadb25ea981df49c9d93bf88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qzv7t2pzn2d0pklnetdjt65crh6fe8vnhuwvhsk2nn']
}
},
{
value: 0.68368564,
n: 3,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 422e1b3b400864bfd3146dd9c628de0f1da62289 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914422e1b3b400864bfd3146dd9c628de0f1da6228988ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qppzuxemgqyxf07nz3kan33gmc83mf3z3yz295c4s7']
}
}
],
hex:
'020000000236a7a2c08a889c012a5b838cf7e97646c7b93f0f355ae0e5515fa9f2f185c6ab020000006b483045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f741210245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07acffffffff56688d4b71e95bd4b69e3e9b5970a0d383943907330524a2231cc0046457c85801000000694630430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db1275341210209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505ffffffff040000000000000000406a04534c500001010453454e4420497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7080000000005f5e1000800005ad7e49d910022020000000000001976a91436be3b7d185a85b6cf6fc61d63c16f2f10e5426088ac22020000000000001976a91499e5a8229a9af0dbf3cadb25ea981df49c9d93bf88acb4381304000000001976a914422e1b3b400864bfd3146dd9c628de0f1da6228988ac00000000',
blockhash: '0000000000000000015284202422a688554b7fc80c54f18122847a99c4f79607',
confirmations: 76722,
time: 1570392893,
blocktime: 1570392893
}
const mockOpReturnData01 = {
tokenType: 1,
txType: 'SEND',
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['100000000', '99883300000000']
}
const mockOpReturnData02 = {
tokenType: 1,
txType: 'GENESIS',
ticker: 'TOK-CH',
name: 'TokyoCash',
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
documentUri: '',
documentHash: '',
decimals: 8,
mintBatonVout: 0,
qty: '2100000000000000'
}
const mockOpReturnData03 = {
tokenType: 1,
txType: 'SEND',
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['1000000000', '99883400000000']
}
module.exports = {
nonSlpTxDetails,
slpTxDetails,
mockOpReturnData01,
mockOpReturnData02,
mockOpReturnData03
}
+1 -2
View File
@@ -1,6 +1,5 @@
/*
TODO:
-Create a mocking library of data to compare unit and integration tests.
Unit tests for raw-transactions.js library.
*/
// Public npm libraries
+93 -2
View File
@@ -4,7 +4,7 @@
const assert = require('chai').assert
const sinon = require('sinon')
// const axios = require("axios")
const axios = require('axios')
// Default to unit tests unless some other value for TEST is passed.
if (!process.env.TEST) process.env.TEST = 'unit'
@@ -14,7 +14,7 @@ const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
// Mock data used for unit tests
// const mockData = require("./fixtures/slp/mock-utils")
const mockData = require('./fixtures/slp/mock-utils')
// Default to unit tests unless some other value for TEST is passed.
if (!process.env.TEST) process.env.TEST = 'unit'
@@ -253,4 +253,95 @@ describe('#SLP NFT1', () => {
assert.isNumber(result.outputs)
})
})
describe('#listNFTGroupChildren', () => {
it('should throw an error for improper input', async () => {
try {
const groupId = 12345
await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(
err.message,
'groupId must be a string'
)
}
})
it('should handle API response error', async () => {
try {
const error = new Error()
error.response = {
error: 'NFT group does not exists'
}
sandbox.stub(axios, 'get').throws(error)
const groupId = 'non-exists'
await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
} catch (err) {
assert.include(
err.response,
{ error: 'NFT group does not exists' }
)
}
})
it('should return array of children GENESIS transactions IDs', async () => {
sandbox.stub(axios, 'get').resolves({ data: mockData.mockNftChildrenList })
const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
const result = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftChildren')
assert.isArray(result.nftChildren)
})
})
describe('#parentNFTGroup', () => {
it('should throw an error for improper input', async () => {
try {
const tokenId = 12345
await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(
err.message,
'tokenId must be a string'
)
}
})
it('should handle API response error', async () => {
try {
const error = new Error()
error.response = {
error: 'NFT child does not exists'
}
sandbox.stub(axios, 'get').throws(error)
const tokenId = 'non-exists'
await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
} catch (err) {
assert.include(
err.response,
{ error: 'NFT child does not exists' }
)
}
})
it('should return parent NFT group information', async () => {
sandbox.stub(axios, 'get').resolves({ data: mockData.mockNftGroup })
const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
const result = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftGroup')
assert.property(result.nftGroup, 'id')
assert.property(result.nftGroup, 'name')
assert.property(result.nftGroup, 'symbol')
assert.property(result.nftGroup, 'documentUri')
assert.property(result.nftGroup, 'versionType')
assert.property(result.nftGroup, 'initialTokenQty')
})
})
})
+1 -1
View File
@@ -10,7 +10,7 @@ let uut
const REST_URL = process.env.RESTURL
? process.env.RESTURL
: 'https://bchn.fullstack.cash/v4/'
: 'https://bchn.fullstack.cash/v5/'
// Mock data used for unit tests
const mockDataLib = require('./fixtures/slp/mock-utils')
+119
View File
@@ -0,0 +1,119 @@
/*
Unit tests for the transaction.js library.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
const mockData = require('./fixtures/transaction-mock.js')
describe('#TransactionLib', () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
describe('#get', () => {
it('should throw an error if txid is not specified', async () => {
try {
await bchjs.Transaction.get()
assert.fail('Unexpected code path!')
} catch (err) {
assert.include(
err.message,
'Input must be a string or array of strings.'
)
}
})
it('should get details about a non-SLP transaction', async () => {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.nonSlpTxDetails)
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[0].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, false)
})
it('should get details about a SLP transaction', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.slpTxDetails)
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.mockOpReturnData01)
.onCall(1)
.resolves(mockData.mockOpReturnData02)
.onCall(2)
.resolves(mockData.mockOpReturnData03)
.onCall(3)
.rejects(new Error('No OP_RETURN'))
sandbox
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
.resolves(true)
const txid =
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vout[0], 'tokenQty')
assert.equal(result.vout[0].tokenQty, null)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result.vin[0], 'tokenQty')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, true)
})
it('should catch and throw error on network error', async () => {
try {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
// Force an error
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.rejects(new Error('test error'))
await bchjs.Transaction.get(txid)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
})