mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 09:02:01 -07:00
Compare commits
29
Commits
nft-docs-fixes
...
v5.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9869211572 | ||
|
|
1056c3724c | ||
|
|
6b838ef9cd | ||
|
|
f09573554d | ||
|
|
4a62ae64de | ||
|
|
78de9e390e | ||
|
|
7d8d0c2d69 | ||
|
|
656b0f6809 | ||
|
|
48808aec14 | ||
|
|
d50f4a0d50 | ||
|
|
f09f96d365 | ||
|
|
7f4745f83c | ||
|
|
dd5de1424c | ||
|
|
30604591bd | ||
|
|
aeb1efe23e | ||
|
|
6e43f795d7 | ||
|
|
e07a5b9da0 | ||
|
|
5ada76eace | ||
|
|
e605f6f386 | ||
|
|
eb9d0bea2b | ||
|
|
f185f5de16 | ||
|
|
d795174916 | ||
|
|
1294bc2149 | ||
|
|
8117448fd3 | ||
|
|
c7b51a38ff | ||
|
|
07d490134a | ||
|
|
a8a4eb68a6 | ||
|
|
c05e2f14e2 | ||
|
|
3540d98910 |
+2
-1
@@ -1,7 +1,8 @@
|
||||
Copyright 2021 Chris Troutner
|
||||
Copyright 2021 Permissionless Software Foundation contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@psf/bch-js",
|
||||
"version": "4.0.0",
|
||||
"version": "5.0.0",
|
||||
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
|
||||
"author": "Chris Troutner <chris.troutner@gmail.com>",
|
||||
"contributors": [
|
||||
@@ -14,12 +14,12 @@
|
||||
"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:bchn:slpdb": "export TESTSLP=1 && 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: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/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
||||
"test:integration:decatur:abc": "export RESTURL=http://192.168.0.38:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
|
||||
"test:integration:decatur:bchn": "export RESTURL=http://192.168.2.139:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
||||
"test:integration:decatur:abc": "export RESTURL=http://192.168.2.141:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
|
||||
"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/",
|
||||
|
||||
@@ -186,6 +186,21 @@ class Address {
|
||||
return cashAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.toHash160() toHash160()
|
||||
* @apiName toHash160
|
||||
* @apiGroup Address
|
||||
* @apiDescription Converts any address format to hash160
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // cash address mainnet p2pkh
|
||||
* bchjs.Address.toHash160("bitcoincash:qptnmya5wkly7xf97wm5ak23yqdsz3l2cyj7k9vyyh")
|
||||
* // 573d93b475be4f1925f3b74ed951201b0147eac1
|
||||
*
|
||||
* // cash address mainnet p2sh
|
||||
* bchjs.Address.toHash160("bitcoincash:pp7ushdxf5we8mcpaa3wqgsuqt639cu59ur5xu5fug")
|
||||
* // 7dc85da64d1d93ef01ef62e0221c02f512e3942f
|
||||
*/
|
||||
// Converts any address format to hash160
|
||||
toHash160 (address) {
|
||||
const legacyAddress = this.toLegacyAddress(address)
|
||||
|
||||
+5
-4
@@ -33,10 +33,11 @@ const Encryption = require('./encryption')
|
||||
const Utxo = require('./utxo')
|
||||
const Transaction = require('./transaction')
|
||||
const DSProof = require('./dsproof')
|
||||
const Ecash = require('./ecash')
|
||||
|
||||
// Indexers
|
||||
const Ninsight = require('./ninsight')
|
||||
const Electrumx = require('./electrumx')
|
||||
const PsfSlpIndexer = require('./psf-slp-indexer')
|
||||
|
||||
class BCHJS {
|
||||
constructor (config) {
|
||||
@@ -81,9 +82,6 @@ class BCHJS {
|
||||
|
||||
// console.log(`apiToken: ${this.apiToken}`)
|
||||
|
||||
// Bitcoin.com Ninsight indexer
|
||||
this.Ninsight = new Ninsight(config)
|
||||
|
||||
// ElectrumX indexer
|
||||
this.Electrumx = new Electrumx(libConfig)
|
||||
|
||||
@@ -118,6 +116,9 @@ class BCHJS {
|
||||
this.Transaction = new Transaction(libConfig)
|
||||
|
||||
this.DSProof = new DSProof(libConfig)
|
||||
this.eCash = new Ecash()
|
||||
|
||||
this.PsfSlpIndexer = new PsfSlpIndexer(libConfig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ class Crypto {
|
||||
* // buffer from hex
|
||||
* let buffer = Buffer.from('03123464075c7a5fa6b8680afa2c962a02e7bf071c6b2395b0ac711d462cac9354', 'hex')
|
||||
* bchjs.Crypto.sha256(buffer)
|
||||
* // <Buffer 26 b0 78 0a 68 3a 1e 09 8e 9c b8 cf a1 b0 92 42 28 25 00 97>
|
||||
* // <Buffer 97 8c 09 dd 46 09 1d 19 22 fa 01 e9 f4 a9 75 b9 1a 37 1f 26 ba 83 99 de 27 d5 38 01 15 21 21 de>
|
||||
*
|
||||
* */
|
||||
// Translate address from any address format into a specific format.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Utility library for converting units with eCash.
|
||||
*/
|
||||
|
||||
class eCash {
|
||||
/**
|
||||
* @api eCash.toSatoshi() toSatoshi()
|
||||
* @apiName toSatoshi
|
||||
* @apiGroup eCash
|
||||
* @apiDescription
|
||||
* Convert XEC units into satoshi units
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // convert 10,704.35 XEC to satoshis:
|
||||
* bchjs.eCash.toSatoshi(10704.35)
|
||||
* // 1070435
|
||||
*/
|
||||
toSatoshi (xec) {
|
||||
if (typeof xec !== 'number') {
|
||||
throw new Error('input must be a floating number representing XEC')
|
||||
}
|
||||
|
||||
return Math.floor(xec * 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api eCash.toXec() toXec()
|
||||
* @apiName toXec
|
||||
* @apiGroup eCash
|
||||
* @apiDescription
|
||||
* Convert satoshi units to XEC units
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // convert 1,070,435 satoshis to XEC:
|
||||
* bchjs.eCash.toSatoshi(1070435)
|
||||
* // 10704.35
|
||||
*/
|
||||
toXec (sats) {
|
||||
if (typeof sats !== 'number') {
|
||||
throw new Error('input must be a floating number representing satoshis')
|
||||
}
|
||||
|
||||
return sats / 100
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = eCash
|
||||
-319
@@ -1,319 +0,0 @@
|
||||
/*
|
||||
This library interacts with the ninsight REST API endpoints operated by
|
||||
Bitcoin.com
|
||||
*/
|
||||
|
||||
const axios = require('axios')
|
||||
|
||||
// let _this
|
||||
|
||||
class Ninsight {
|
||||
constructor (config) {
|
||||
// this.restURL = config.restURL
|
||||
// this.apiToken = config.apiToken
|
||||
|
||||
// this.ninsightURL = `https://bch-explorer.api.bitcoin.com/v1/`
|
||||
if (config) {
|
||||
this.ninsightURL = config.ninsightURL
|
||||
? config.ninsightURL
|
||||
: 'https://rest.bitcoin.com/v2'
|
||||
}
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`,
|
||||
timeout: 15000
|
||||
}
|
||||
}
|
||||
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.utxo() utxo()
|
||||
* @apiName Ninsight Utxo
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Return list of uxto for address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Ninsight.utxo('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "txid": "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
|
||||
* // "vout": 1,
|
||||
* // "amount": 0.03608203,
|
||||
* // "satoshis": 3608203,
|
||||
* // "height": 585570,
|
||||
* // "confirmations": 10392
|
||||
* // },
|
||||
* // {
|
||||
* // "txid": "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
|
||||
* // "vout": 0,
|
||||
* // "amount": 0.03608203,
|
||||
* // "satoshis": 3608203,
|
||||
* // "height": 577125,
|
||||
* // "confirmations": 18837
|
||||
* // }
|
||||
* // ]
|
||||
*
|
||||
*/
|
||||
async utxo (address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/utxo`,
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/utxo`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.unconfirmed() unconfirmed()
|
||||
* @apiName Ninsight Unconfirmed Utxo
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Returns a list of unconfirmed UTXOs for an address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Ninsight.unconfirmed('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "txid": "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
|
||||
* // "vout": 1,
|
||||
* // "amount": 0.03608203,
|
||||
* // "satoshis": 3608203,
|
||||
* // "confirmations": 0
|
||||
* // "ts": 1559670801
|
||||
* // },
|
||||
* // {
|
||||
* // "txid": "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
|
||||
* // "vout": 0,
|
||||
* // "amount": 0.03608203,
|
||||
* // "satoshis": 3608203,
|
||||
* // "confirmations": 0
|
||||
* // "ts": 1559670902
|
||||
* // }
|
||||
* // ]
|
||||
*
|
||||
*/
|
||||
async unconfirmed (address) {
|
||||
try {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/unconfirmed`,
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/unconfirmed`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.transactions() transactions()
|
||||
* @apiName Ninsight TX History
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Return transactions history for address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txs = await bchjs.Ninsight.transactions('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
|
||||
* console.log(txs);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "pagesTotal": 1,
|
||||
* // "txs": [
|
||||
* // {
|
||||
* // "txid": "ec7bc8349386e3e1939bbdc4f8092fdbdd6a380734e68486b558cd594c451d5b",
|
||||
* // "version": 2,
|
||||
* // "locktime": 0,
|
||||
* // "vin": [
|
||||
* // {
|
||||
* // "txid": "4f1fc57c33659628938db740449bf92fb75799e1d5750a4aeef80eb52d6df1e0",
|
||||
* // "vout": 0,
|
||||
* // "sequence": 4294967295,
|
||||
* // "n": 0,
|
||||
* // ...
|
||||
* // }
|
||||
* // ...
|
||||
* // ]
|
||||
* // ...
|
||||
* // }
|
||||
* // ],
|
||||
* // "legacyAddress": "1LpbYkEM5cryfhs58tH8c93p4SGzit7UrP",
|
||||
* // "cashAddress": "bitcoincash:qrvk436u4r0ew2wj0rd9pnxhx4w90p2yfc29ta0d2n",
|
||||
* // "currentPage": 0
|
||||
* // }
|
||||
* // ]
|
||||
* //
|
||||
*
|
||||
*/
|
||||
async transactions (address) {
|
||||
try {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/transactions`,
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/transactions`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.txDetails() txDetails()
|
||||
* @apiName Ninsight TX Details
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Return transactions details for address(es).
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let result = await bchjs.Ninsight.txDetails('fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33');
|
||||
* console.log(result);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "txid": "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33",
|
||||
* // "version": 1,
|
||||
* // "locktime": 0,
|
||||
* // "vin": [
|
||||
* // {
|
||||
* // "sequence": 4294967295,
|
||||
* // "n": 0
|
||||
* // }
|
||||
* // ],
|
||||
* // "vout": [
|
||||
* // ...
|
||||
* // ],
|
||||
* // "blockhash": "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09",
|
||||
* // "blockheight": 1000,
|
||||
* // "confirmations": 659909,
|
||||
* // "time": 1232346882,
|
||||
* // "blocktime": 1232346882,
|
||||
* // "firstSeenTime": null,
|
||||
* // "valueOut": 50,
|
||||
* // "size": 135
|
||||
* // }
|
||||
* // ]
|
||||
*
|
||||
*/
|
||||
async txDetails (txid) {
|
||||
try {
|
||||
if (typeof txid === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/transaction/details`,
|
||||
{
|
||||
txids: [txid]
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(txid)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/transaction/details`,
|
||||
{
|
||||
txids: txid
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Transaction ID must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Ninsight
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
This library interacts with the PSF slp indexer REST API endpoints operated
|
||||
by FullStack.cash
|
||||
|
||||
TODO:
|
||||
- detect TXs from tokens in the blacklist.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const axios = require('axios')
|
||||
|
||||
// Local libraries
|
||||
const RawTransaction = require('./raw-transactions')
|
||||
const SlpUtils = require('./slp/utils')
|
||||
|
||||
// let _this
|
||||
|
||||
class PsfSlpIndexer {
|
||||
constructor (config = {}) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
|
||||
if (this.authToken) {
|
||||
// Add Basic Authentication token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.status() status()
|
||||
* @apiName Status
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return status from psf slp indexer.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let status = await bchjs.PsfSlpIndexer.status();
|
||||
* console.log(status);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "status": {
|
||||
* "startBlockHeight": 543376,
|
||||
* "syncedBlockHeight": 723249,
|
||||
* "chainBlockHeight": 722679
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async status () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}psf/slp/status`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.balance() balance()
|
||||
* @apiName SLP Balance
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return slp balance for a single address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n');
|
||||
* console.log(balance);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* balance: {
|
||||
* utxos: [
|
||||
* {
|
||||
* txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
* vout: 1,
|
||||
* type: 'token',
|
||||
* qty: '1800',
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
|
||||
* }
|
||||
* ],
|
||||
* txs: [
|
||||
* {
|
||||
* txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
|
||||
* height: 717796
|
||||
* },
|
||||
* {
|
||||
* txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
* height: 717832
|
||||
* }
|
||||
* ],
|
||||
* balances: [
|
||||
* {
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* qty: '1800'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async balance (address) {
|
||||
try {
|
||||
console.log('balance() address: ', address)
|
||||
// Handle single address.
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}psf/slp/address`,
|
||||
{ address },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
throw new Error('Input address must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.tokenStats() tokenStats()
|
||||
* @apiName Token Stats
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return list stats for a single slp token.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
|
||||
* console.log(tokenStats);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* tokenData: {
|
||||
* type: 1,
|
||||
* ticker: 'TROUT',
|
||||
* name: "Trout's test token",
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* documentUri: 'troutsblog.com',
|
||||
* documentHash: '',
|
||||
* decimals: 2,
|
||||
* mintBatonIsActive: true,
|
||||
* tokensInCirculationBN: '100098953386',
|
||||
* tokensInCirculationStr: '100098953386',
|
||||
* blockCreated: 622414,
|
||||
* totalBurned: '1046614',
|
||||
* totalMinted: '100100000000'
|
||||
* txs: [
|
||||
* {
|
||||
* txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* height: 622414,
|
||||
* type: 'GENESIS',
|
||||
* qty: '100000000000'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
async tokenStats (tokenId) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof tokenId === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}psf/slp/token`,
|
||||
{ tokenId },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
throw new Error('Input tokenId must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.tx() tx()
|
||||
* @apiName SLP Transaction Data
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return slp transaction data.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
|
||||
* console.log(txData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* txData: {
|
||||
* txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* version: 2,
|
||||
* size: 339,
|
||||
* locktime: 0,
|
||||
* vin: [
|
||||
* {
|
||||
* txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
|
||||
* vout: 0,
|
||||
* scriptSig: {
|
||||
* asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
|
||||
* hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
|
||||
* },
|
||||
* sequence: 4294967295,
|
||||
* address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
|
||||
* value: 0.00051303,
|
||||
* tokenQty: 0,
|
||||
* tokenQtyStr: '0',
|
||||
* tokenId: null
|
||||
* }
|
||||
* ],
|
||||
* vout: [
|
||||
* {
|
||||
* value: 0,
|
||||
* n: 0,
|
||||
* scriptPubKey: {
|
||||
* asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
|
||||
* hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
|
||||
* type: 'nulldata'
|
||||
* },
|
||||
* tokenQtyStr: '0',
|
||||
* tokenQty: 0
|
||||
* }
|
||||
* ],
|
||||
* hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
|
||||
* blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
|
||||
* confirmations: 97398,
|
||||
* time: 1581773131,
|
||||
* blocktime: 1581773131,
|
||||
* blockheight: 622414,
|
||||
* isSlpTx: true,
|
||||
* tokenTxType: 'GENESIS',
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* tokenType: 1,
|
||||
* tokenTicker: 'TROUT',
|
||||
* tokenName: "Trout's test token",
|
||||
* tokenDecimals: 2,
|
||||
* tokenUri: 'troutsblog.com',
|
||||
* tokenDocHash: '',
|
||||
* isValidSlp: true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async tx (txid) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof txid === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}psf/slp/txid`,
|
||||
{ txid },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
throw new Error('Input txid must be a string.')
|
||||
} catch (error) {
|
||||
// console.log('error: ', error)
|
||||
|
||||
// Case: txid is not stored in the psf-slp-indexer tx database.
|
||||
// Response: If it's not in the database, then it can be assumed the TX
|
||||
// is not a token TX?
|
||||
if (
|
||||
error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.error &&
|
||||
error.response.data.error.includes('Key not found in database')
|
||||
) {
|
||||
// console.log(
|
||||
// 'TX not found in psf-slp-indexer. Retrieving from full node.'
|
||||
// )
|
||||
|
||||
// Check if this txid belongs to a blacklisted token.
|
||||
const isInBlacklist = await this.checkBlacklist(txid)
|
||||
|
||||
// Get the TX Details from the full node.
|
||||
const txDetails = await this.rawTransaction.getTxData(txid)
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
if (isInBlacklist) {
|
||||
txDetails.isValidSlp = null
|
||||
} else {
|
||||
txDetails.isValidSlp = false
|
||||
}
|
||||
|
||||
const outObj = {
|
||||
txData: txDetails
|
||||
}
|
||||
|
||||
return outObj
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the txid has an OP_RETURN containing a tokenID that is in the
|
||||
// blacklist. In that case, the isValidSlp property should be marked as
|
||||
// null, and not false.
|
||||
async checkBlacklist (txid) {
|
||||
try {
|
||||
// TODO: Add endpoint to psf-slp-indexer to retrieve current blacklist.
|
||||
// This should be done once at startup, and not each time this function
|
||||
// is called.
|
||||
const blacklist = [
|
||||
'dd21be4532d93661e8ffe16db6535af0fb8ee1344d1fef81a193e2b4cfa9fbc9'
|
||||
]
|
||||
|
||||
const outTokenData = await this.slpUtils.decodeOpReturn(txid)
|
||||
// console.log('outTokenData: ', outTokenData)
|
||||
|
||||
// Loop through each token in the blacklist.
|
||||
for (let i = 0; i < blacklist.length; i++) {
|
||||
// If a match is found, return true.
|
||||
if (outTokenData.tokenId === blacklist[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// By default, return false.
|
||||
return false
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
// Exit quietly.
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PsfSlpIndexer
|
||||
+1
-1
@@ -36,7 +36,7 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.toSLPAddress('qzm47qz5ue99y9yl4aca7jnz7dwgdenl85jkfx3znl')
|
||||
* // simpleledger:qzm47qz5ue99y9yl4aca7jnz7dwgdenl857dzayzdp
|
||||
*
|
||||
* // tesnet legacy
|
||||
* // testnet legacy
|
||||
* bchjs.SLP.Address.toSLPAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx')
|
||||
* // slptest:qzq9je6pntpva3wf6scr7mlnycr54sjgeqauyclpwv
|
||||
*
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ const Util = require('../util')
|
||||
let _this
|
||||
|
||||
class Utils {
|
||||
constructor (config) {
|
||||
constructor (config = {}) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.slpParser = slpParser
|
||||
|
||||
+11
-2
@@ -2,18 +2,27 @@
|
||||
High-level functions for working with Transactions
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const BigNumber = require('bignumber.js')
|
||||
|
||||
// Local libraries
|
||||
const RawTransaction = require('./raw-transactions')
|
||||
const SlpUtils = require('./slp/utils')
|
||||
const Blockchain = require('./blockchain')
|
||||
const PsfSlpIndexer = require('./psf-slp-indexer')
|
||||
|
||||
class Transaction {
|
||||
constructor (config) {
|
||||
constructor (config = {}) {
|
||||
// Encapsulate dependencies
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.blockchain = new Blockchain(config)
|
||||
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
||||
}
|
||||
|
||||
// Proxy the call to the psf-slp-indexer.
|
||||
async get (txid) {
|
||||
return await this.psfSlpIndexer.tx(txid)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,7 +51,7 @@ class Transaction {
|
||||
// CT 10/31/21: TODO: this function should be refactored to use get2(), but
|
||||
// add waterfall validation of the TX and its inputs.
|
||||
|
||||
async get (txid) {
|
||||
async getOld (txid) {
|
||||
try {
|
||||
if (typeof txid !== 'string') {
|
||||
throw new Error(
|
||||
|
||||
+154
-6
@@ -8,19 +8,21 @@
|
||||
// Local libraries
|
||||
const Electrumx = require('./electrumx')
|
||||
const Slp = require('./slp/slp')
|
||||
const PsfSlpIndexer = require('./psf-slp-indexer')
|
||||
|
||||
class UTXO {
|
||||
constructor (config) {
|
||||
constructor (config = {}) {
|
||||
// Encapsulate dependencies for easier mocking.
|
||||
this.electrumx = new Electrumx(config)
|
||||
this.slp = new Slp(config)
|
||||
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Utxo.get() get()
|
||||
* @apiName get
|
||||
* @api Utxo.getOld() getOld()
|
||||
* @apiName getOld
|
||||
* @apiGroup UTXO
|
||||
* @apiDescription Get UTXOs for an address
|
||||
* @apiDescription Get UTXOs for an address (from SLPDB)
|
||||
*
|
||||
* Given an address, this function will return an object with thre following
|
||||
* properties:
|
||||
@@ -45,7 +47,7 @@ class UTXO {
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxos = await bchjs.Utxo.get('simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj');
|
||||
* let utxos = await bchjs.Utxo.getOld('simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj');
|
||||
* console.log(utxos);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
@@ -181,8 +183,12 @@ class UTXO {
|
||||
*
|
||||
*
|
||||
*/
|
||||
async get (address, useWhitelist = false) {
|
||||
async getOld (address, useWhitelist = false) {
|
||||
try {
|
||||
if (!address) {
|
||||
throw new Error('Address must be an array or a string')
|
||||
}
|
||||
|
||||
// Convert address to an array if it is a string.
|
||||
if (typeof address === 'string') address = [address]
|
||||
|
||||
@@ -282,6 +288,148 @@ class UTXO {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Utxo.get() get()
|
||||
* @apiName get
|
||||
* @apiGroup UTXO
|
||||
* @apiDescription Get UTXOs for an address (from psf-slp-indexer)
|
||||
*
|
||||
* Given an address, this function will return an object with thre following
|
||||
* properties:
|
||||
* - address: "" - the address these UTXOs are associated with
|
||||
* - bchUtxos: [] - UTXOs confirmed to be spendable as normal BCH
|
||||
* - nullUtxo: [] - UTXOs that did not pass SLP validation. Should be ignored and
|
||||
* not spent, to be safe.
|
||||
* - slpUtxos: {} - UTXOs confirmed to be colored as valid SLP tokens
|
||||
* - type1: {}
|
||||
* - tokens: [] - SLP token Type 1 tokens.
|
||||
* - mintBatons: [] - SLP token Type 1 mint batons.
|
||||
* - nft: {}
|
||||
* - tokens: [] - NFT tokens
|
||||
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
|
||||
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
|
||||
*
|
||||
* Note: You can pass in an optional second Boolean argument. The default
|
||||
* `false` will use the normal waterfall validation method. Set to `true`,
|
||||
* SLP UTXOs will be validated with the whitelist filtered SLPDB. This will
|
||||
* result is many more UTXOs in the `nullUtxos` array.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxos = await bchjs.Utxo.get('simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj');
|
||||
* console.log(utxos);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* [
|
||||
* {
|
||||
* "address": "bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv",
|
||||
* "bchUtxos": [
|
||||
* {
|
||||
* "height": 674513,
|
||||
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
||||
* "tx_pos": 3,
|
||||
* "value": 38134,
|
||||
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
||||
* "vout": 3,
|
||||
* "isValid": false
|
||||
* }
|
||||
* ],
|
||||
*/
|
||||
// This version of get() uses the psf-slp-indexer. It will replace the older
|
||||
// get() function that uses SLPDB.
|
||||
// TODO: NFT UTXOs are identified as non-token UTXOs, which will cause a wallet
|
||||
// to burn them. The psf-slp-indexer needs to be updated to mark these UTXOs.
|
||||
async get (address) {
|
||||
try {
|
||||
// Convert address to an array if it is a string.
|
||||
if (typeof address !== 'string') {
|
||||
throw new Error('address input must be a string')
|
||||
}
|
||||
|
||||
// Ensure the address is a BCH address.
|
||||
const addr = this.slp.Address.toCashAddress(address)
|
||||
|
||||
// Get the UTXOs associated with the address.
|
||||
const utxoData = await this.electrumx.utxo(addr)
|
||||
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
|
||||
const utxos = utxoData.utxos
|
||||
|
||||
// Get SLP UTXOs from the psf-slp-indexer
|
||||
const slpUtxoData = await this.psfSlpIndexer.balance(addr)
|
||||
// console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
|
||||
const slpUtxos = slpUtxoData.balance.utxos
|
||||
|
||||
// Loop through the Fulcrum UTXOs.
|
||||
for (let i = 0; i < utxos.length; i++) {
|
||||
const thisUtxo = utxos[i]
|
||||
|
||||
// Loop through the UTXOs from psf-slp-indexer.
|
||||
for (let j = 0; j < slpUtxos.length; j++) {
|
||||
const thisSlpUtxo = slpUtxos[j]
|
||||
|
||||
// If the non-hydrated UTXO matches the SLP UTXO, then combine the data
|
||||
// and mark the UTXO as an SLP token.
|
||||
if (
|
||||
thisUtxo.tx_hash === thisSlpUtxo.txid &&
|
||||
thisUtxo.tx_pos === thisSlpUtxo.vout
|
||||
) {
|
||||
thisUtxo.txid = thisUtxo.tx_hash
|
||||
thisUtxo.vout = thisUtxo.tx_pos
|
||||
thisUtxo.isSlp = true
|
||||
thisUtxo.type = thisSlpUtxo.type
|
||||
thisUtxo.qty = thisSlpUtxo.qty
|
||||
thisUtxo.tokenId = thisSlpUtxo.tokenId
|
||||
thisUtxo.address = thisSlpUtxo.address
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no match, then this is a normal BCH UTXO. Mark it as such.
|
||||
if (!thisUtxo.isSlp) {
|
||||
thisUtxo.txid = thisUtxo.tx_hash
|
||||
thisUtxo.vout = thisUtxo.tx_pos
|
||||
thisUtxo.isSlp = false
|
||||
thisUtxo.address = addr
|
||||
}
|
||||
}
|
||||
|
||||
const bchUtxos = utxos.filter(x => x.isSlp === false)
|
||||
const type1TokenUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'token'
|
||||
)
|
||||
const type1BatonUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'baton'
|
||||
)
|
||||
const nullUtxos = utxos.filter(x => x.isSlp === null)
|
||||
|
||||
const outObj = {
|
||||
address: addr,
|
||||
bchUtxos,
|
||||
slpUtxos: {
|
||||
type1: {
|
||||
tokens: type1TokenUtxos,
|
||||
mintBatons: type1BatonUtxos
|
||||
},
|
||||
nft: {} // Allocated for future support of NFT spec.
|
||||
},
|
||||
nullUtxos
|
||||
}
|
||||
|
||||
return outObj
|
||||
} catch (err) {
|
||||
// console.error('Error in bchjs.utxo.get2(): ', err)
|
||||
|
||||
if (err.error) throw new Error(err.error)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Utxo.findBiggestUtxo() findBiggestUtxo()
|
||||
* @apiName findBiggestUtxo
|
||||
|
||||
@@ -313,6 +313,14 @@ describe('#blockchain', () => {
|
||||
assert.isString(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getBlockchainInfo', () => {
|
||||
it('should get info about the blockchain', async () => {
|
||||
const result = await bchjs.Blockchain.getBlockchainInfo()
|
||||
|
||||
console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Integration tests for the psf-slp-indexer.js library
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
let bchjs
|
||||
|
||||
describe('#psf-slp-indexer', () => {
|
||||
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('#status', () => {
|
||||
it('should return the status of the indexer.', async () => {
|
||||
const result = await bchjs.PsfSlpIndexer.status()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.status, 'startBlockHeight')
|
||||
assert.property(result.status, 'syncedBlockHeight')
|
||||
assert.property(result.status, 'chainBlockHeight')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#balance', () => {
|
||||
it('should get balance data for an address.', async () => {
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.balance, 'utxos')
|
||||
assert.property(result.balance, 'txs')
|
||||
assert.property(result.balance, 'balances')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tokenStats', () => {
|
||||
it('should get stats on a token', async () => {
|
||||
const tokenId =
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'txs')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tx', () => {
|
||||
it('should get hydrated tx data for an SLP transaction', async () => {
|
||||
const txid =
|
||||
'947ccb2a0d62ca287bc4b0993874ab0f9f6afd454193e631e2bf84dca66731fc'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, true)
|
||||
})
|
||||
|
||||
it('should mark non-SLP transaction as false', async () => {
|
||||
const txid =
|
||||
'03d6e6b63647ce7b02ecc73dc6d41b485be14a3e20eed4474b8a840358ddf14e'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
// FlexUSD transactions
|
||||
// Currently FlexUSD UTXOs are reported as invalid SLP UTXOs, which means
|
||||
// the wallet will burn them. There is a TODO in the code. This test will
|
||||
// need to be changed when it is done.
|
||||
it('should mark blacklisted token as null', async () => {
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, null)
|
||||
})
|
||||
|
||||
it('should throw error for non-existent txid', async () => {
|
||||
try {
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85e'
|
||||
|
||||
await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
assert.include(err.message, 'No such mempool or blockchain transaction')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
+241
-237
@@ -30,265 +30,269 @@ describe('#SLP', () => {
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe('#util', () => {
|
||||
describe('#decodeOpReturn', () => {
|
||||
it('should decode a NFT Child transaction', async () => {
|
||||
const txid =
|
||||
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
|
||||
if (process.env.TESTSLP) {
|
||||
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)}`)
|
||||
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.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')
|
||||
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 = [
|
||||
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
|
||||
{
|
||||
note: 'Malformed SLP tx',
|
||||
tx_hash:
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
|
||||
assert.equal(data[0].isValid, false)
|
||||
|
||||
// Normal TX (non-SLP)
|
||||
{
|
||||
note: 'Normal TX (non-SLP)',
|
||||
tx_hash:
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
tx_pos: 0,
|
||||
value: 400000
|
||||
},
|
||||
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
|
||||
assert.equal(data[1].isValid, false)
|
||||
|
||||
// Valid PSF SLP tx
|
||||
{
|
||||
note: 'Valid PSF SLP tx',
|
||||
tx_hash:
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
|
||||
assert.equal(data[2].isValid, true)
|
||||
|
||||
// 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
|
||||
}
|
||||
]
|
||||
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
|
||||
assert.equal(data[3].isValid, true)
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
// Token send on BCHN network
|
||||
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
|
||||
assert.equal(data[4].isValid, true)
|
||||
|
||||
// Malformed SLP tx
|
||||
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
|
||||
assert.equal(data[0].isValid, false)
|
||||
// Token send on ABC network
|
||||
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
|
||||
assert.equal(data[5].isValid, null)
|
||||
|
||||
// Normal TX (non-SLP)
|
||||
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
|
||||
assert.equal(data[1].isValid, false)
|
||||
// Known invalid SLP token send of PSF tokens
|
||||
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
|
||||
assert.equal(data[6].isValid, false)
|
||||
})
|
||||
})
|
||||
|
||||
// Valid PSF SLP tx
|
||||
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
|
||||
assert.equal(data[2].isValid, true)
|
||||
describe('#validateTxid3', () => {
|
||||
it('should invalidate a known invalid TXID', async () => {
|
||||
const txid =
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
||||
|
||||
// Valid SLP token not in whitelist
|
||||
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
|
||||
assert.equal(data[3].isValid, true)
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Token send on BCHN network
|
||||
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
|
||||
assert.equal(data[4].isValid, true)
|
||||
assert.isArray(result)
|
||||
|
||||
// Token send on ABC network
|
||||
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
|
||||
assert.equal(data[5].isValid, null)
|
||||
assert.property(result[0], 'txid')
|
||||
assert.equal(result[0].txid, txid)
|
||||
|
||||
// Known invalid SLP token send of PSF tokens
|
||||
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
|
||||
assert.equal(data[6].isValid, false)
|
||||
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() but null in validateTxid3()
|
||||
assert.equal(result[3].txid, txids[3])
|
||||
assert.equal(result[3].valid, null)
|
||||
|
||||
// Note: This should change from null to true once SLPDB finishes indexing.
|
||||
assert.equal(result[4].txid, txids[4])
|
||||
assert.equal(result[4].valid, null)
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
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() but null in validateTxid3()
|
||||
assert.equal(result[3].txid, txids[3])
|
||||
assert.equal(result[3].valid, null)
|
||||
|
||||
// Note: This should change from null to true once SLPDB finishes indexing.
|
||||
assert.equal(result[4].txid, txids[4])
|
||||
assert.equal(result[4].valid, null)
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
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
|
||||
|
||||
@@ -13,7 +13,7 @@ describe('#UTXO', () => {
|
||||
|
||||
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'
|
||||
@@ -90,7 +90,40 @@ describe('#UTXO', () => {
|
||||
assert.isAbove(result[0].nullUtxos.length, 1)
|
||||
})
|
||||
})
|
||||
*/
|
||||
describe('#get', () => {
|
||||
it('should hydrate address with BCH and SLP UTXOs', async () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert expected properties exist.
|
||||
assert.property(result, 'address')
|
||||
assert.property(result, 'bchUtxos')
|
||||
assert.property(result, 'slpUtxos')
|
||||
assert.property(result.slpUtxos, 'type1')
|
||||
assert.property(result.slpUtxos, 'nft')
|
||||
assert.property(result, 'nullUtxos')
|
||||
|
||||
assert.isAbove(result.bchUtxos.length, 0)
|
||||
assert.isAbove(result.slpUtxos.type1.tokens.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
|
||||
})
|
||||
|
||||
// TODO: NFTs are currently not identified as different than normal BCH UTXOs.
|
||||
// The psf-slp-indexer needs to be updated to fix this issue.
|
||||
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 that minting batons are correctly identified.
|
||||
assert.isAbove(result.slpUtxos.type1.mintBatons.length, 0)
|
||||
})
|
||||
})
|
||||
/*
|
||||
describe('#findBiggestUtxo', () => {
|
||||
it('should sort UTXOs from Electrumx', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
@@ -118,6 +151,7 @@ describe('#UTXO', () => {
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
|
||||
+921
-915
File diff suppressed because it is too large
Load Diff
@@ -11,71 +11,74 @@ describe('#Transaction', () => {
|
||||
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'
|
||||
if (process.env.TESTSLP) {
|
||||
describe('#getOld', () => {
|
||||
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)}`)
|
||||
const result = await bchjs.Transaction.getOld(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 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)
|
||||
// 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.getOld(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 get problematic transaction', async () => {
|
||||
// const txid = 'a55515de32577e296c512840bcaabed5823bb773fb4f8fd8e5197cc96cbc54d1'
|
||||
//
|
||||
// const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// })
|
||||
|
||||
// TX a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe has
|
||||
// an input TXID (bd84bc1dd5ecd976165892306992401272f6bedeb37d7b2cdbf74fc4a55967a6)
|
||||
// that is also a valid SLP tx, but is unrelated. Both TXs pass DAG validation,
|
||||
// but for separate tokens.
|
||||
it('should get problematic transaction', async () => {
|
||||
const txid =
|
||||
'a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe'
|
||||
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// The token ID should equal the txid for this Vin.
|
||||
assert.equal(result.vin[2].txid, result.vin[2].tokenId)
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
// it('should get problematic transaction', async () => {
|
||||
// const txid = 'a55515de32577e296c512840bcaabed5823bb773fb4f8fd8e5197cc96cbc54d1'
|
||||
//
|
||||
// const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// })
|
||||
|
||||
// TX a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe has
|
||||
// an input TXID (bd84bc1dd5ecd976165892306992401272f6bedeb37d7b2cdbf74fc4a55967a6)
|
||||
// that is also a valid SLP tx, but is unrelated. Both TXs pass DAG validation,
|
||||
// but for separate tokens.
|
||||
it('should get problematic transaction', async () => {
|
||||
const txid = 'a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// The token ID should equal the txid for this Vin.
|
||||
assert.equal(result.vin[2].txid, result.vin[2].tokenId)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
Unit tests for eCash library.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const assert = require('chai').assert
|
||||
const Ecash = require('../../src/ecash')
|
||||
const uut = new Ecash()
|
||||
|
||||
describe('#eCash', () => {
|
||||
describe('#toSatoshi', () => {
|
||||
it('should convert XEC to satoshis', () => {
|
||||
const xec = 10704.35
|
||||
|
||||
const result = uut.toSatoshi(xec)
|
||||
|
||||
assert.equal(result, 1070435)
|
||||
})
|
||||
|
||||
it('should throw an error if input is not a number', () => {
|
||||
try {
|
||||
uut.toSatoshi('test')
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(
|
||||
err.message,
|
||||
'input must be a floating number representing XEC'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#toXec', () => {
|
||||
it('should convert satoshis to XEC', () => {
|
||||
const sats = 1070435
|
||||
|
||||
const result = uut.toXec(sats)
|
||||
|
||||
assert.equal(result, 10704.35)
|
||||
})
|
||||
|
||||
it('should throw an error if input is not a number', () => {
|
||||
try {
|
||||
uut.toXec('test')
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(
|
||||
err.message,
|
||||
'input must be a floating number representing satoshis'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
Mocking data for unit tests for the Ninsight library.
|
||||
*/
|
||||
|
||||
const utxo = {
|
||||
utxos: [
|
||||
{
|
||||
txid: '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
|
||||
vout: 0,
|
||||
amount: 0.00001,
|
||||
satoshis: 1000,
|
||||
height: 602405,
|
||||
confirmations: 36459
|
||||
}
|
||||
],
|
||||
legacyAddress: '15NCRBJsHaJy8As5bX1oh2YauRejnZ1MKF',
|
||||
cashAddress: 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9',
|
||||
slpAddress: 'simpleledger:qqh793x9au6ehvh7r2zflzguanlme760wuwzunhjfm',
|
||||
scriptPubKey: '76a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788ac',
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 2fe2c4c5ef359bb2fe1a849f891cecffbcfb4f77 OP_EQUALVERIFY OP_CHECKSIG'
|
||||
}
|
||||
|
||||
const unconfirmed = {
|
||||
utxos: [
|
||||
{
|
||||
txid: '3904ffe6f8fba4ceda5e887130f60fcb18bdc7dcee10392a57f89475c5c108f1',
|
||||
vout: 0,
|
||||
amount: 0.03608203,
|
||||
satoshis: 3608203,
|
||||
confirmations: 0,
|
||||
ts: 1559670801
|
||||
}
|
||||
],
|
||||
legacyAddress: '1AyWs8U4HUnTLmxxFiGoJbsXauRsvBrcKW',
|
||||
cashAddress: 'bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5',
|
||||
slpAddress: 'simpleledger:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rkuk8mdxlf2',
|
||||
scriptPubKey: '76a9146d695af951760b837c8e58b045b098f9c17e23b788ac'
|
||||
}
|
||||
|
||||
const utxoPost = [utxo, utxo]
|
||||
const unconfirmedPost = [unconfirmed, unconfirmed]
|
||||
|
||||
const transactions = {
|
||||
pagesTotal: 1,
|
||||
txs: [
|
||||
{
|
||||
txid: 'ec7bc8349386e3e1939bbdc4f8092fdbdd6a380734e68486b558cd594c451d5b',
|
||||
version: 2,
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
txid:
|
||||
'4f1fc57c33659628938db740449bf92fb75799e1d5750a4aeef80eb52d6df1e0',
|
||||
vout: 0,
|
||||
sequence: 4294967295,
|
||||
n: 0,
|
||||
scriptSig: {
|
||||
hex:
|
||||
'483045022100a3662a19ae384a1ceddea57765e425e61b04823e976d574da3911ac6b55d7f9b02200e571d985bce987675a2d58587a346fa40c39f4df13dc88548a92c52d5b24422412103f953f7630acc15bd3f5078c698f3af777286ae955b57e4857c158f75d87adb5f',
|
||||
asm:
|
||||
'3045022100a3662a19ae384a1ceddea57765e425e61b04823e976d574da3911ac6b55d7f9b02200e571d985bce987675a2d58587a346fa40c39f4df13dc88548a92c52d5b24422[ALL|FORKID] 03f953f7630acc15bd3f5078c698f3af777286ae955b57e4857c158f75d87adb5f'
|
||||
},
|
||||
addr: '17HPz8RQ4XM6mjre6aspvqyj1j648CZidM',
|
||||
valueSat: 1111,
|
||||
value: 0.00001111,
|
||||
doubleSpentTxID: null
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'126d62c299e7e14c66fe0b485d13082c23641f003690462046bc24ad2d1180c1',
|
||||
vout: 0,
|
||||
sequence: 4294967295,
|
||||
n: 1,
|
||||
scriptSig: {
|
||||
hex:
|
||||
'47304402203e3f923207111ff9bbd2fb5ab1a49a9145ad809ee0cad0e0ddaed64bfe38dc16022012ee288fb413bd500c63f8bb95e46b6b57d34762decd46b7188478a1c398eeda412103f953f7630acc15bd3f5078c698f3af777286ae955b57e4857c158f75d87adb5f',
|
||||
asm:
|
||||
'304402203e3f923207111ff9bbd2fb5ab1a49a9145ad809ee0cad0e0ddaed64bfe38dc16022012ee288fb413bd500c63f8bb95e46b6b57d34762decd46b7188478a1c398eeda[ALL|FORKID] 03f953f7630acc15bd3f5078c698f3af777286ae955b57e4857c158f75d87adb5f'
|
||||
},
|
||||
addr: '17HPz8RQ4XM6mjre6aspvqyj1j648CZidM',
|
||||
valueSat: 1000,
|
||||
value: 0.00001,
|
||||
doubleSpentTxID: null
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: '0.00001736',
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
hex: '76a914d96ac75ca8df9729d278da50ccd7355c5785444e88ac',
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 d96ac75ca8df9729d278da50ccd7355c5785444e OP_EQUALVERIFY OP_CHECKSIG',
|
||||
addresses: ['1LpbYkEM5cryfhs58tH8c93p4SGzit7UrP'],
|
||||
type: 'pubkeyhash'
|
||||
},
|
||||
spentTxId: null,
|
||||
spentIndex: null,
|
||||
spentHeight: null
|
||||
}
|
||||
],
|
||||
blockheight: -1,
|
||||
confirmations: 0,
|
||||
time: 1559673337,
|
||||
valueOut: 0.00001736,
|
||||
size: 339,
|
||||
valueIn: 0.00002111,
|
||||
fees: 0.00000375
|
||||
}
|
||||
],
|
||||
legacyAddress: '1LpbYkEM5cryfhs58tH8c93p4SGzit7UrP',
|
||||
cashAddress: 'bitcoincash:qrvk436u4r0ew2wj0rd9pnxhx4w90p2yfc29ta0d2n',
|
||||
currentPage: 0
|
||||
}
|
||||
|
||||
const transactionsPost = [transactions, transactions]
|
||||
|
||||
const details = {
|
||||
txid: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
version: 1,
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
coinbase: '04ffff001d02fd04',
|
||||
sequence: 4294967295,
|
||||
n: 0
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: '50.00000000',
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
hex:
|
||||
'4104f5eeb2b10c944c6b9fbcfff94c35bdeecd93df977882babc7f3a2cf7f5c81d3b09a68db7f0e04f21de5d4230e75e6dbe7ad16eefe0d4325a62067dc6f369446aac',
|
||||
asm:
|
||||
'04f5eeb2b10c944c6b9fbcfff94c35bdeecd93df977882babc7f3a2cf7f5c81d3b09a68db7f0e04f21de5d4230e75e6dbe7ad16eefe0d4325a62067dc6f369446a OP_CHECKSIG',
|
||||
addresses: ['1BW18n7MfpU35q4MTBSk8pse3XzQF8XvzT'],
|
||||
type: 'pubkeyhash',
|
||||
cashAddrs: ['bitcoincash:qpej6mkrwca4tvy2snq4crhrf88v4ljspysx0ueetk']
|
||||
},
|
||||
spentTxId: null,
|
||||
spentIndex: null,
|
||||
spentHeight: null
|
||||
}
|
||||
],
|
||||
blockhash:
|
||||
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
|
||||
blockheight: 1000,
|
||||
confirmations: 585610,
|
||||
time: 1232346882,
|
||||
blocktime: 1232346882,
|
||||
isCoinBase: true,
|
||||
valueOut: 50,
|
||||
size: 135
|
||||
}
|
||||
|
||||
const detailsPost = [details, details]
|
||||
|
||||
module.exports = {
|
||||
utxo,
|
||||
utxoPost,
|
||||
unconfirmed,
|
||||
unconfirmedPost,
|
||||
transactions,
|
||||
transactionsPost,
|
||||
details,
|
||||
detailsPost
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
Mocking data for unit tests for the PSF SLP INDEXER library.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const tokenStats = {
|
||||
tokenData: {
|
||||
type: 1,
|
||||
ticker: 'TP03',
|
||||
name: 'Test Plugin 03',
|
||||
tokenId: '13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
|
||||
documentUri: 'fullstack.cash',
|
||||
documentHash: '',
|
||||
decimals: 0,
|
||||
mintBatonIsActive: false,
|
||||
tokensInCirculationBN: '1',
|
||||
tokensInCirculationStr: '1',
|
||||
blockCreated: 722420,
|
||||
totalBurned: '0',
|
||||
totalMinted: '1',
|
||||
txs: [
|
||||
{
|
||||
txid:
|
||||
'13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
|
||||
height: 722420,
|
||||
type: 'GENESIS',
|
||||
qty: '1'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const txData = {
|
||||
txData: {
|
||||
txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
version: 2,
|
||||
size: 339,
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
txid:
|
||||
'8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
|
||||
vout: 0,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
|
||||
hex:
|
||||
'47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
|
||||
},
|
||||
sequence: 4294967295,
|
||||
address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
|
||||
value: 0.00051303,
|
||||
tokenQty: 0,
|
||||
tokenQtyStr: '0',
|
||||
tokenId: null
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
|
||||
hex:
|
||||
'6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
|
||||
type: 'nulldata'
|
||||
},
|
||||
tokenQtyStr: '0',
|
||||
tokenQty: 0
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
|
||||
blockhash:
|
||||
'0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
|
||||
confirmations: 97398,
|
||||
time: 1581773131,
|
||||
blocktime: 1581773131,
|
||||
blockheight: 622414,
|
||||
isSlpTx: true,
|
||||
tokenTxType: 'GENESIS',
|
||||
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
tokenType: 1,
|
||||
tokenTicker: 'TROUT',
|
||||
tokenName: "Trout's test token",
|
||||
tokenDecimals: 2,
|
||||
tokenUri: 'troutsblog.com',
|
||||
tokenDocHash: '',
|
||||
isValidSlp: true
|
||||
}
|
||||
}
|
||||
const balance = {
|
||||
balance: {
|
||||
utxos: [
|
||||
{
|
||||
txid:
|
||||
'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '1800',
|
||||
tokenId:
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
|
||||
}
|
||||
],
|
||||
txs: [
|
||||
{
|
||||
txid:
|
||||
'078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
|
||||
height: 717796
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
height: 717832
|
||||
}
|
||||
],
|
||||
balances: [
|
||||
{
|
||||
tokenId:
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
qty: '1800'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const status = {
|
||||
status: {
|
||||
startBlockHeight: 543376,
|
||||
syncedBlockHeight: 722860,
|
||||
chainBlockHeight: 722679
|
||||
}
|
||||
}
|
||||
|
||||
const tokenData01 = {
|
||||
tokenType: 1,
|
||||
txType: 'MINT',
|
||||
tokenId: 'dd21be4532d93661e8ffe16db6535af0fb8ee1344d1fef81a193e2b4cfa9fbc9',
|
||||
mintBatonVout: 2,
|
||||
qty: {}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
tokenStats,
|
||||
txData,
|
||||
balance,
|
||||
status,
|
||||
tokenData01
|
||||
}
|
||||
@@ -280,10 +280,100 @@ const electrumxUtxos = {
|
||||
]
|
||||
}
|
||||
|
||||
const fulcrumUtxos01 = {
|
||||
success: true,
|
||||
utxos: [
|
||||
{
|
||||
height: 674512,
|
||||
tx_hash:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674512,
|
||||
tx_hash:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
tx_pos: 2,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674512,
|
||||
tx_hash:
|
||||
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674513,
|
||||
tx_hash:
|
||||
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674513,
|
||||
tx_hash:
|
||||
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
||||
tx_pos: 2,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674513,
|
||||
tx_hash:
|
||||
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
||||
tx_pos: 3,
|
||||
value: 38134
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const psfSlpIndexerUtxos01 = {
|
||||
balance: {
|
||||
utxos: [
|
||||
{
|
||||
txid:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '10000000000',
|
||||
tokenId:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
address: 'bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv'
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
vout: 2,
|
||||
type: 'baton',
|
||||
tokenId:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
address: 'bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv'
|
||||
}
|
||||
],
|
||||
txs: [
|
||||
{
|
||||
txid:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
height: 674512
|
||||
}
|
||||
],
|
||||
balances: [
|
||||
{
|
||||
tokenId:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
qty: '10000000000'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mockUtxoData,
|
||||
mockHydratedUtxos,
|
||||
mockTwoHydratedAddrs,
|
||||
mockEveryUtxoType,
|
||||
electrumxUtxos
|
||||
electrumxUtxos,
|
||||
fulcrumUtxos01,
|
||||
psfSlpIndexerUtxos01
|
||||
}
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const axios = require('axios')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
const mockData = require('./fixtures/ninsight-mock')
|
||||
|
||||
describe('#Ninsight', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#utxo', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Ninsight.utxo(addr)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
'Input address must be a string or array of strings.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should GET utxos for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.utxo })
|
||||
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'utxos')
|
||||
assert.property(result, 'legacyAddress')
|
||||
assert.property(result, 'cashAddress')
|
||||
assert.property(result, 'slpAddress')
|
||||
assert.property(result, 'scriptPubKey')
|
||||
assert.property(result, 'asm')
|
||||
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], 'txid')
|
||||
assert.property(result.utxos[0], 'vout')
|
||||
assert.property(result.utxos[0], 'amount')
|
||||
assert.property(result.utxos[0], 'satoshis')
|
||||
assert.property(result.utxos[0], 'height')
|
||||
assert.property(result.utxos[0], 'confirmations')
|
||||
})
|
||||
|
||||
it('should POST utxo details for an array of addresses', async () => {
|
||||
// Mock the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.utxoPost })
|
||||
|
||||
const addr = [
|
||||
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7',
|
||||
'bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr'
|
||||
]
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#unconfirmed', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Ninsight.unconfirmed(addr)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Input address must be a string or array of strings.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should POST utxos for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.unconfirmed })
|
||||
|
||||
const addr = 'bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5'
|
||||
|
||||
const result = await bchjs.Ninsight.unconfirmed(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'utxos')
|
||||
assert.property(result, 'legacyAddress')
|
||||
assert.property(result, 'cashAddress')
|
||||
assert.property(result, 'slpAddress')
|
||||
assert.property(result, 'scriptPubKey')
|
||||
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], 'txid')
|
||||
assert.property(result.utxos[0], 'vout')
|
||||
assert.property(result.utxos[0], 'amount')
|
||||
assert.property(result.utxos[0], 'satoshis')
|
||||
assert.property(result.utxos[0], 'confirmations')
|
||||
assert.property(result.utxos[0], 'ts')
|
||||
})
|
||||
|
||||
it('should POST utxo details for an array of addresses', async () => {
|
||||
// Mock the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.unconfirmedPost })
|
||||
|
||||
const addr = [
|
||||
'bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5',
|
||||
'bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr'
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.unconfirmed(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')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#transactions', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Ninsight.transactions(addr)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
'Input address must be a string or array of strings.'
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should POST transaction history for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.transactionsPost })
|
||||
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
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 transaction history for an array of addresses', async () => {
|
||||
// Mock the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.transactionsPost })
|
||||
|
||||
const addr = [
|
||||
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7',
|
||||
'bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr'
|
||||
]
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
describe('#txDetails', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Ninsight.txDetails(txid)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
'Transaction ID must be a string or array of strings.'
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should POST transaction details for a single TxID', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.detailsPost })
|
||||
|
||||
const txid = 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33'
|
||||
|
||||
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], 'isCoinBase')
|
||||
assert.property(result[0], 'valueOut')
|
||||
assert.property(result[0], 'size')
|
||||
})
|
||||
it('should POST transaction details for an array of TxIDs', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.detailsPost })
|
||||
|
||||
const txid = [
|
||||
'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33'
|
||||
]
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,339 @@
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const axios = require('axios')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
const mockData = require('./fixtures/psf-slp-indexer-mock')
|
||||
|
||||
describe('#PsfSlpIndexer', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#status', () => {
|
||||
it('should GET status', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'get').resolves({ data: mockData.status })
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.status()
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, 'status')
|
||||
assert.property(result.status, 'startBlockHeight')
|
||||
assert.property(result.status, 'syncedBlockHeight')
|
||||
assert.property(result.status, 'chainBlockHeight')
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'get').throws(new Error('test error'))
|
||||
|
||||
await bchjs.PsfSlpIndexer.status()
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle request error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
sandbox.stub(axios, 'get').throws(testErr)
|
||||
|
||||
await bchjs.PsfSlpIndexer.status()
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#balance', () => {
|
||||
it('should GET balance', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.balance })
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
const result = await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, 'balance')
|
||||
|
||||
assert.property(result.balance, 'utxos')
|
||||
assert.property(result.balance, 'txs')
|
||||
assert.property(result.balance, 'balances')
|
||||
assert.isArray(result.balance.utxos)
|
||||
assert.isArray(result.balance.txs)
|
||||
assert.isArray(result.balance.balances)
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.PsfSlpIndexer.balance(addr)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, 'Input address must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').throws(new Error('test error'))
|
||||
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
|
||||
await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle request error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
sandbox.stub(axios, 'post').throws(testErr)
|
||||
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
|
||||
await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tokenStats', () => {
|
||||
it('should GET token stats', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.tokenStats })
|
||||
|
||||
const tokenId =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, 'tokenData')
|
||||
assert.property(result.tokenData, 'type')
|
||||
assert.property(result.tokenData, 'ticker')
|
||||
assert.property(result.tokenData, 'name')
|
||||
assert.property(result.tokenData, 'tokenId')
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'documentHash')
|
||||
assert.property(result.tokenData, 'decimals')
|
||||
assert.property(result.tokenData, 'mintBatonIsActive')
|
||||
assert.property(result.tokenData, 'tokensInCirculationBN')
|
||||
assert.property(result.tokenData, 'tokensInCirculationStr')
|
||||
assert.property(result.tokenData, 'blockCreated')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
assert.property(result.tokenData, 'totalMinted')
|
||||
assert.property(result.tokenData, 'txs')
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const tokenId = 12345
|
||||
|
||||
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, 'Input tokenId must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').throws(new Error('test error'))
|
||||
|
||||
const tokenId =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle request error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
sandbox.stub(axios, 'post').throws(testErr)
|
||||
|
||||
const tokenId =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tx', () => {
|
||||
it('should GET transaction data', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.txData })
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, 'txData')
|
||||
assert.property(result.txData, 'txid')
|
||||
assert.property(result.txData, 'hash')
|
||||
assert.property(result.txData, 'version')
|
||||
assert.property(result.txData, 'size')
|
||||
assert.property(result.txData, 'locktime')
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'hex')
|
||||
assert.property(result.txData, 'blockhash')
|
||||
assert.property(result.txData, 'confirmations')
|
||||
assert.property(result.txData, 'time')
|
||||
assert.property(result.txData, 'blocktime')
|
||||
assert.property(result.txData, 'blockheight')
|
||||
assert.property(result.txData, 'isSlpTx')
|
||||
assert.property(result.txData, 'tokenTxType')
|
||||
assert.property(result.txData, 'tokenId')
|
||||
assert.property(result.txData, 'tokenType')
|
||||
assert.property(result.txData, 'tokenTicker')
|
||||
assert.property(result.txData, 'tokenName')
|
||||
assert.property(result.txData, 'tokenDecimals')
|
||||
assert.property(result.txData, 'tokenUri')
|
||||
assert.property(result.txData, 'tokenDocHash')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.PsfSlpIndexer.tx(txid)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, 'Input txid must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').throws(new Error('test error'))
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should get tx from full node if not available from slp indexer', async () => {
|
||||
// Stub the call to the SLP indexer.
|
||||
const testErr = {
|
||||
response: { data: { error: 'Key not found in database' } }
|
||||
}
|
||||
sandbox.stub(axios, 'post').rejects(testErr)
|
||||
|
||||
// Stub the call to the full node
|
||||
sandbox.stub(bchjs.PsfSlpIndexer, 'checkBlacklist').resolves(false)
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
|
||||
.resolves({ txid: 'fakeTxid' })
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.txData.txid, 'fakeTxid')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
it('should return isValidSlp=null for blacklisted token', async () => {
|
||||
// Stub the call to the SLP indexer.
|
||||
const testErr = {
|
||||
response: { data: { error: 'Key not found in database' } }
|
||||
}
|
||||
sandbox.stub(axios, 'post').rejects(testErr)
|
||||
|
||||
// Stub the call to the full node
|
||||
sandbox.stub(bchjs.PsfSlpIndexer, 'checkBlacklist').resolves(true)
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
|
||||
.resolves({ txid: 'fakeTxid' })
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.txData.txid, 'fakeTxid')
|
||||
assert.equal(result.txData.isValidSlp, null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#checkBlacklist', () => {
|
||||
it('should return true if txid contains token in blacklist', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.resolves(mockData.tokenData01)
|
||||
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return false if there is an error', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist()
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should return false if there is no tokenId match', async () => {
|
||||
// Mock dependencies
|
||||
mockData.tokenData01.tokenId = 'abc123'
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.resolves(mockData.tokenData01)
|
||||
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist(txid)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -22,10 +22,10 @@ describe('#TransactionLib', () => {
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#get', () => {
|
||||
describe('#getOld', () => {
|
||||
it('should throw an error if txid is not specified', async () => {
|
||||
try {
|
||||
await bchjs.Transaction.get()
|
||||
await bchjs.Transaction.getOld()
|
||||
|
||||
assert.fail('Unexpected code path!')
|
||||
} catch (err) {
|
||||
@@ -45,7 +45,7 @@ describe('#TransactionLib', () => {
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.nonSlpTxDetails)
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
@@ -84,7 +84,7 @@ describe('#TransactionLib', () => {
|
||||
const txid =
|
||||
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
@@ -114,7 +114,7 @@ describe('#TransactionLib', () => {
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
await bchjs.Transaction.get(txid)
|
||||
await bchjs.Transaction.getOld(txid)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
@@ -147,7 +147,7 @@ describe('#TransactionLib', () => {
|
||||
const txid =
|
||||
'874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
@@ -195,7 +195,7 @@ describe('#TransactionLib', () => {
|
||||
const txid =
|
||||
'4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
@@ -246,7 +246,7 @@ describe('#TransactionLib', () => {
|
||||
const txid =
|
||||
'6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
const result = await bchjs.Transaction.getOld(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
@@ -705,4 +705,15 @@ describe('#TransactionLib', () => {
|
||||
assert.equal(result.isSlpTx, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should proxy psf-slp-indexer', async () => {
|
||||
// console.log('bchjs.Transaction: ', bchjs.Transaction)
|
||||
sandbox.stub(bchjs.Transaction.psfSlpIndexer, 'tx').resolves('test data')
|
||||
|
||||
const result = await bchjs.Transaction.get()
|
||||
|
||||
assert.equal(result, 'test data')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+51
-7
@@ -15,7 +15,7 @@ describe('#utxo', () => {
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#get', () => {
|
||||
describe('#getOld', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// Mock dependencies.
|
||||
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves(mockData.mockUtxoData)
|
||||
@@ -25,7 +25,7 @@ describe('#utxo', () => {
|
||||
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
@@ -46,7 +46,7 @@ describe('#utxo', () => {
|
||||
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
await bchjs.Utxo.get(addr)
|
||||
await bchjs.Utxo.getOld(addr)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
@@ -66,7 +66,7 @@ describe('#utxo', () => {
|
||||
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
@@ -88,7 +88,7 @@ describe('#utxo', () => {
|
||||
addrs.push(addr)
|
||||
}
|
||||
|
||||
await bchjs.Utxo.get(addrs)
|
||||
await bchjs.Utxo.getOld(addrs)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
@@ -105,7 +105,7 @@ describe('#utxo', () => {
|
||||
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
@@ -133,7 +133,7 @@ describe('#utxo', () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
const useWhitelist = true
|
||||
|
||||
const result = await bchjs.Utxo.get(addr, useWhitelist)
|
||||
const result = await bchjs.Utxo.getOld(addr, useWhitelist)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
@@ -188,4 +188,48 @@ describe('#utxo', () => {
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should throw an error if input is not a string', async () => {
|
||||
try {
|
||||
const addr = 123
|
||||
|
||||
await bchjs.Utxo.get(addr)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'address input must be a string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return UTXO information', async () => {
|
||||
// mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.electrumx, 'utxo')
|
||||
.resolves(mockData.fulcrumUtxos01)
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
||||
.resolves(mockData.psfSlpIndexerUtxos01)
|
||||
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert expected properties exist
|
||||
assert.property(result, 'address')
|
||||
assert.property(result, 'bchUtxos')
|
||||
assert.property(result, 'slpUtxos')
|
||||
assert.property(result.slpUtxos, 'type1')
|
||||
assert.property(result.slpUtxos.type1, 'tokens')
|
||||
assert.property(result.slpUtxos.type1, 'mintBatons')
|
||||
assert.property(result.slpUtxos, 'nft')
|
||||
assert.property(result, 'nullUtxos')
|
||||
|
||||
assert.equal(result.bchUtxos.length, 4)
|
||||
assert.equal(result.slpUtxos.type1.tokens.length, 1)
|
||||
assert.equal(result.slpUtxos.type1.mintBatons.length, 1)
|
||||
assert.equal(result.nullUtxos.length, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user