mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-21 16:51:59 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71755964c8 | ||
|
|
bd1c3b622d | ||
|
|
bdced13230 | ||
|
|
c925bfce46 | ||
|
|
056a47f6b2 | ||
|
|
51038681d3 | ||
|
|
1a2f144baf | ||
|
|
6d70568082 | ||
|
|
0b1cf57852 | ||
|
|
48f7f75c9c | ||
|
|
0bc96fded5 |
+1
-1
@@ -18,7 +18,7 @@
|
||||
"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.2.139:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
||||
"test:integration:decatur:bchn": "export RESTURL=http://192.168.2.129: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/",
|
||||
|
||||
+15
-2
@@ -638,13 +638,24 @@ class ElectrumX {
|
||||
// Sort confirmed Transactions by the block height
|
||||
sortConfTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// console.log(`sortConfTxs txs: ${JSON.stringify(txs, null, 2)}`)
|
||||
|
||||
// Filter out unconfirmed transactions, with a height of 0 or less.
|
||||
txs = txs.filter(elem => elem.height > 0)
|
||||
|
||||
if (sortingOrder === 'DESCENDING') {
|
||||
return txs.sort((a, b) => b.height - a.height)
|
||||
// console.log('Sorting in descending order')
|
||||
return txs.sort((a, b) => {
|
||||
// console.log(`descending b.height: ${b.height}, a.height: ${a.height}`)
|
||||
return b.height - a.height
|
||||
})
|
||||
}
|
||||
return txs.sort((a, b) => a.height - b.height)
|
||||
|
||||
// console.log('Sorting in ascending order')
|
||||
return txs.sort((a, b) => {
|
||||
// console.log(`ascending b.height: ${b.height}, a.height: ${a.height}`)
|
||||
return a.height - b.height
|
||||
})
|
||||
} catch (err) {
|
||||
console.log('Error in util.js/sortConfTxs()')
|
||||
throw err
|
||||
@@ -685,6 +696,8 @@ class ElectrumX {
|
||||
// Substitute zero-conf txs with the current block-height + 1
|
||||
async sortAllTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// console.log(`sortingOrder: ${sortingOrder}`)
|
||||
|
||||
// Calculate the height of the next block
|
||||
const nextBlock = (await this.blockchain.getBlockCount()) + 1
|
||||
|
||||
|
||||
@@ -132,7 +132,8 @@ class PsfSlpIndexer {
|
||||
*/
|
||||
async balance (address) {
|
||||
try {
|
||||
console.log('balance() address: ', address)
|
||||
// console.log('balance() address: ', address)
|
||||
|
||||
// Handle single address.
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
|
||||
+50
@@ -150,6 +150,56 @@ class Util {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.chunk100() chunk100()
|
||||
* @apiName chunk100
|
||||
* @apiGroup Util
|
||||
* @apiDescription chunk up an array into multiple arrays of 100 elements each.
|
||||
* Input: arrayToSlice - a one-dimensional array of elements.
|
||||
* Returns a two-dimensional array. An array of 100-element arrays.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,...,148, 149, 150]
|
||||
*
|
||||
* const chunked = bchjs.Util.chunk20(bigArray)
|
||||
* console.log(chunked)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* [
|
||||
* [0,1,2,3,4,5,6,7,8,9,10,11,...,98,99],
|
||||
* [100,101,102,...,148,149,150]
|
||||
* ]
|
||||
*/
|
||||
chunk100 (arrayToSlice) {
|
||||
try {
|
||||
// Validate inputs
|
||||
if (!Array.isArray(arrayToSlice)) {
|
||||
throw new Error('input must be an array')
|
||||
}
|
||||
|
||||
let offset = 0
|
||||
const result = []
|
||||
|
||||
// Loop over the array and slice off chunks of 100 elements.
|
||||
while (offset < arrayToSlice.length) {
|
||||
const chunk = arrayToSlice.slice(offset, offset + 100)
|
||||
result.push(chunk)
|
||||
offset = offset + 100
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error('Error in chunk100()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.sleep() sleep()
|
||||
* @apiName sleep
|
||||
|
||||
+78
-9
@@ -9,6 +9,7 @@
|
||||
const Electrumx = require('./electrumx')
|
||||
const Slp = require('./slp/slp')
|
||||
const PsfSlpIndexer = require('./psf-slp-indexer')
|
||||
const BigNumber = require('bignumber.js')
|
||||
|
||||
class UTXO {
|
||||
constructor (config = {}) {
|
||||
@@ -16,6 +17,7 @@ class UTXO {
|
||||
this.electrumx = new Electrumx(config)
|
||||
this.slp = new Slp(config)
|
||||
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
||||
this.BigNumber = BigNumber
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,10 +311,6 @@ class UTXO {
|
||||
* - 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 () => {
|
||||
@@ -359,10 +357,22 @@ class UTXO {
|
||||
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
|
||||
const utxos = utxoData.utxos
|
||||
|
||||
let slpUtxos = []
|
||||
|
||||
// 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
|
||||
try {
|
||||
const slpUtxoData = await this.psfSlpIndexer.balance(addr)
|
||||
// console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
|
||||
|
||||
slpUtxos = slpUtxoData.balance.utxos
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
|
||||
// Exit quietly if address has no SLP UTXOs. Otherwise, throw the error.
|
||||
if (err.error && !err.error.includes('Key not found in database')) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through the Fulcrum UTXOs.
|
||||
for (let i = 0; i < utxos.length; i++) {
|
||||
@@ -399,10 +409,15 @@ class UTXO {
|
||||
}
|
||||
}
|
||||
|
||||
const bchUtxos = utxos.filter(x => x.isSlp === false)
|
||||
const type1TokenUtxos = utxos.filter(
|
||||
// Get token UTXOs
|
||||
let type1TokenUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'token'
|
||||
)
|
||||
|
||||
// Hydrate the UTXOs with additional token data.
|
||||
type1TokenUtxos = await this.hydrateTokenData(type1TokenUtxos)
|
||||
|
||||
const bchUtxos = utxos.filter(x => x.isSlp === false)
|
||||
const type1BatonUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'baton'
|
||||
)
|
||||
@@ -430,6 +445,60 @@ class UTXO {
|
||||
}
|
||||
}
|
||||
|
||||
// Hydrate an array of token UTXOs with token information.
|
||||
// Returns an array of token UTXOs with additional data.
|
||||
async hydrateTokenData (utxoAry) {
|
||||
try {
|
||||
// console.log('utxoAry: ', utxoAry)
|
||||
|
||||
// Create a list of token IDs without duplicates.
|
||||
let tokenIds = utxoAry.map(x => x.tokenId)
|
||||
|
||||
// Remove duplicates. https://stackoverflow.com/questions/9229645/remove-duplicate-values-from-js-array
|
||||
tokenIds = [...new Set(tokenIds)]
|
||||
// console.log('tokenIds: ', tokenIds)
|
||||
|
||||
// Get Genesis data for each tokenId
|
||||
const genesisData = []
|
||||
for (let i = 0; i < tokenIds.length; i++) {
|
||||
const thisTokenId = tokenIds[i]
|
||||
const thisTokenData = await this.psfSlpIndexer.tokenStats(thisTokenId)
|
||||
// console.log('thisTokenData: ', thisTokenData)
|
||||
|
||||
genesisData.push(thisTokenData)
|
||||
}
|
||||
// console.log('genesisData: ', genesisData)
|
||||
|
||||
// Hydrate each token UTXO with data from the genesis transaction.
|
||||
for (let i = 0; i < utxoAry.length; i++) {
|
||||
const thisUtxo = utxoAry[i]
|
||||
|
||||
// Get the genesis data for this token.
|
||||
const genData = genesisData.filter(
|
||||
x => x.tokenData.tokenId === thisUtxo.tokenId
|
||||
)
|
||||
// console.log('genData: ', genData)
|
||||
|
||||
thisUtxo.ticker = genData[0].tokenData.ticker
|
||||
thisUtxo.name = genData[0].tokenData.name
|
||||
thisUtxo.documentUri = genData[0].tokenData.documentUri
|
||||
thisUtxo.documentHash = genData[0].tokenData.documentHash
|
||||
thisUtxo.decimals = genData[0].tokenData.decimals
|
||||
|
||||
// Calculate the real token quantity
|
||||
const qty = new BigNumber(thisUtxo.qty).dividedBy(
|
||||
10 ** parseInt(thisUtxo.decimals)
|
||||
)
|
||||
thisUtxo.qtyStr = qty.toString()
|
||||
}
|
||||
|
||||
return utxoAry
|
||||
} catch (err) {
|
||||
console.log('Error in hydrateTokenData()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Utxo.findBiggestUtxo() findBiggestUtxo()
|
||||
* @apiName findBiggestUtxo
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
Integration tests for the utxo.js library.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
// const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
// const BCHJS = require('../../../../src/bch-js')
|
||||
// const bchjs = new BCHJS()
|
||||
|
||||
describe('#UTXO', () => {
|
||||
beforeEach(async () => {
|
||||
@@ -14,6 +14,7 @@ describe('#UTXO', () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
/*
|
||||
describe('#get', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
@@ -31,6 +32,7 @@ describe('#UTXO', () => {
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Integration tests for the transaction.js library.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe('#Transaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should get a tx details for a non-SLP TX with an OP_RETURN', async () => {
|
||||
const txid =
|
||||
'01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'txid')
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -13,84 +13,170 @@ 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'
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
if (process.env.TESTSLP) {
|
||||
describe('#getOld', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should handle an array of addresses', async () => {
|
||||
const addr = [
|
||||
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
||||
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should handle NFTs and minting batons', async () => {
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
assert.isArray(result[0].slpUtxos.type1.mintBatons)
|
||||
assert.isArray(result[0].slpUtxos.type1.tokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupTokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.tokens)
|
||||
})
|
||||
|
||||
it('should use the whitelist when flag is set', async () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
const useWhitelist = true
|
||||
|
||||
const result = await bchjs.Utxo.getOld(addr, useWhitelist)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
// Most token UTXOs should end up in the nullUtxos array.
|
||||
assert.isAbove(result[0].bchUtxos.length, 0)
|
||||
assert.isAbove(result[0].nullUtxos.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
it('should handle an array of addresses', async () => {
|
||||
const addr = [
|
||||
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
||||
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
describe('#findBiggestUtxo', () => {
|
||||
it('should sort UTXOs from Electrumx', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
|
||||
it('should sort UTXOs from Utxos.get()', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const utxos = await bchjs.Utxo.getOld(addr)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('#hydrateTokenData', () => {
|
||||
it('should hydrate token UTXOs', async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid:
|
||||
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '10000000',
|
||||
tokenId:
|
||||
'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '100000000',
|
||||
tokenId:
|
||||
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '4764',
|
||||
tokenId:
|
||||
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '238',
|
||||
tokenId:
|
||||
'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
}
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
const result = await bchjs.Utxo.hydrateTokenData(utxos)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should handle NFTs and minting batons', async () => {
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
assert.isArray(result[0].slpUtxos.type1.mintBatons)
|
||||
assert.isArray(result[0].slpUtxos.type1.tokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupTokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.tokens)
|
||||
})
|
||||
|
||||
it('should use the whitelist when flag is set', async () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
const useWhitelist = true
|
||||
|
||||
const result = await bchjs.Utxo.get(addr, useWhitelist)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
// Most token UTXOs should end up in the nullUtxos array.
|
||||
assert.isAbove(result[0].bchUtxos.length, 0)
|
||||
assert.isAbove(result[0].nullUtxos.length, 1)
|
||||
assert.property(result[0], 'ticker')
|
||||
assert.property(result[0], 'name')
|
||||
assert.property(result[0], 'qtyStr')
|
||||
assert.property(result[0], 'documentUri')
|
||||
assert.property(result[0], 'documentHash')
|
||||
})
|
||||
})
|
||||
*/
|
||||
|
||||
describe('#get', () => {
|
||||
it('should hydrate address with BCH and SLP UTXOs', async () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
@@ -122,36 +208,17 @@ describe('#UTXO', () => {
|
||||
// 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'
|
||||
|
||||
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
|
||||
it('should return UTXOs for address with no SLP tokens', async () => {
|
||||
const addr = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
|
||||
it('should sort UTXOs from Utxos.get()', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const utxos = await bchjs.Utxo.get(addr)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
assert.isAbove(result.bchUtxos.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
||||
})
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
|
||||
@@ -328,6 +328,19 @@ const fulcrumUtxos01 = {
|
||||
]
|
||||
}
|
||||
|
||||
const fulcrumUtxos02 = {
|
||||
success: true,
|
||||
utxos: [
|
||||
{
|
||||
height: 674513,
|
||||
tx_hash:
|
||||
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
||||
tx_pos: 3,
|
||||
value: 38134
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const psfSlpIndexerUtxos01 = {
|
||||
balance: {
|
||||
utxos: [
|
||||
@@ -368,6 +381,101 @@ const psfSlpIndexerUtxos01 = {
|
||||
}
|
||||
}
|
||||
|
||||
const tokenUtxos01 = [
|
||||
{
|
||||
txid: '384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '10000000',
|
||||
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '100000000',
|
||||
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '4764',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '238',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
}
|
||||
]
|
||||
|
||||
const genesisData01 = {
|
||||
tokenData: {
|
||||
type: 1,
|
||||
ticker: 'sleven',
|
||||
name: 'sleven',
|
||||
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
documentUri: 'sleven',
|
||||
documentHash: '',
|
||||
decimals: 7,
|
||||
mintBatonIsActive: false,
|
||||
tokensInCirculationBN: '770059999999',
|
||||
tokensInCirculationStr: '770059999999',
|
||||
blockCreated: 555483,
|
||||
totalBurned: '7711234568',
|
||||
totalMinted: '777771234567'
|
||||
}
|
||||
}
|
||||
|
||||
const genesisData02 = {
|
||||
tokenData: {
|
||||
type: 1,
|
||||
ticker: 'NAKAMOTO',
|
||||
name: 'NAKAMOTO',
|
||||
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
documentUri: '',
|
||||
documentHash: '',
|
||||
decimals: 8,
|
||||
mintBatonIsActive: false,
|
||||
tokensInCirculationBN: '2099260968799900',
|
||||
tokensInCirculationStr: '2099260968799900',
|
||||
blockCreated: 555671,
|
||||
totalBurned: '739031200100',
|
||||
totalMinted: '2100000000000000'
|
||||
}
|
||||
}
|
||||
|
||||
const genesisData03 = {
|
||||
tokenData: {
|
||||
type: 1,
|
||||
ticker: 'AUDC',
|
||||
name: 'AUD Coin',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
documentUri: 'audcoino@gmail.com',
|
||||
documentHash: '',
|
||||
decimals: 6,
|
||||
mintBatonIsActive: false,
|
||||
tokensInCirculationBN: '974791786216512742',
|
||||
tokensInCirculationStr: '974791786216512742',
|
||||
blockCreated: 603311,
|
||||
totalBurned: '1025208213783487258',
|
||||
totalMinted: '2000000000000000000'
|
||||
}
|
||||
}
|
||||
|
||||
const noUtxoErr = {
|
||||
success: false,
|
||||
error:
|
||||
'Key not found in database [bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7]'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mockUtxoData,
|
||||
mockHydratedUtxos,
|
||||
@@ -375,5 +483,11 @@ module.exports = {
|
||||
mockEveryUtxoType,
|
||||
electrumxUtxos,
|
||||
fulcrumUtxos01,
|
||||
psfSlpIndexerUtxos01
|
||||
fulcrumUtxos02,
|
||||
psfSlpIndexerUtxos01,
|
||||
tokenUtxos01,
|
||||
genesisData01,
|
||||
genesisData02,
|
||||
genesisData03,
|
||||
noUtxoErr
|
||||
}
|
||||
|
||||
@@ -189,6 +189,45 @@ describe('#utxo', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('#hydrateTokenData', () => {
|
||||
it('should hydrate token UTXOs', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'tokenStats')
|
||||
.onCall(0)
|
||||
.resolves(mockData.genesisData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.genesisData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.genesisData03)
|
||||
|
||||
const result = await bchjs.Utxo.hydrateTokenData(mockData.tokenUtxos01)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.length, 4)
|
||||
assert.property(result[0], 'qtyStr')
|
||||
assert.property(result[0], 'ticker')
|
||||
assert.property(result[0], 'name')
|
||||
assert.property(result[0], 'documentUri')
|
||||
assert.property(result[0], 'documentHash')
|
||||
})
|
||||
|
||||
it('should should catch and throw errors', async () => {
|
||||
try {
|
||||
// Force error
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'tokenStats')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
await bchjs.Utxo.hydrateTokenData(mockData.tokenUtxos01)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should throw an error if input is not a string', async () => {
|
||||
try {
|
||||
@@ -210,6 +249,8 @@ describe('#utxo', () => {
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
||||
.resolves(mockData.psfSlpIndexerUtxos01)
|
||||
// Mock function to return the same input. Good enough for this test.
|
||||
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x)
|
||||
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
@@ -231,5 +272,30 @@ describe('#utxo', () => {
|
||||
assert.equal(result.slpUtxos.type1.mintBatons.length, 1)
|
||||
assert.equal(result.nullUtxos.length, 0)
|
||||
})
|
||||
|
||||
it('should handle an address with no SLP UTXOs', async () => {
|
||||
// mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.electrumx, 'utxo')
|
||||
.resolves(mockData.fulcrumUtxos02)
|
||||
|
||||
// Force psf-slp-indexer to return no UTXOs
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
||||
.rejects(mockData.noUtxoErr)
|
||||
|
||||
// Mock function to return the same input. Good enough for this test.
|
||||
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(() => [])
|
||||
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.bchUtxos.length, 1)
|
||||
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
|
||||
assert.equal(result.nullUtxos.length, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user