Compare commits

..
4 Commits
8 changed files with 26 additions and 2854 deletions
+8
View File
@@ -0,0 +1,8 @@
[
{
"srcDir": "",
"destDir": "",
"files": "**/*.js",
"command": "npm run lint"
}
]
+14 -14
View File
@@ -1,12 +1,12 @@
{
"name": "@psf/bch-js",
"version": "5.0.0",
"version": "6.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@psf/bch-js",
"version": "5.0.0",
"version": "6.0.0",
"license": "MIT",
"dependencies": {
"@chris.troutner/bip32-utils": "1.0.5",
@@ -14,7 +14,7 @@
"@psf/bitcoincash-ops": "2.0.0",
"@psf/bitcoincashjs-lib": "4.0.2",
"@psf/coininfo": "4.0.0",
"axios": "^0.21.4",
"axios": "0.26.1",
"bc-bip68": "1.0.5",
"bchaddrjs-slp": "0.2.5",
"bigi": "1.4.2",
@@ -35,7 +35,7 @@
"wif": "2.0.6"
},
"devDependencies": {
"apidoc": "^0.50.5",
"apidoc": "0.50.5",
"assert": "2.0.0",
"chai": "4.1.2",
"coveralls": "3.0.2",
@@ -45,9 +45,9 @@
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-standard": "4.0.0",
"husky": "^4.3.8",
"husky": "4.3.8",
"lodash.clonedeep": "4.5.0",
"mocha": "^9.2.1",
"mocha": "9.2.1",
"node-mocks-http": "1.7.0",
"nyc": "15.1.0",
"semantic-release": "^19.0.2",
@@ -1740,12 +1740,12 @@
"dev": true
},
"node_modules/axios": {
"version": "0.21.4",
"resolved": "http://94.130.170.209:4873/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"version": "0.26.1",
"resolved": "http://94.130.170.209:4873/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.14.0"
"follow-redirects": "^1.14.8"
}
},
"node_modules/balanced-match": {
@@ -16179,11 +16179,11 @@
"dev": true
},
"axios": {
"version": "0.21.4",
"resolved": "http://94.130.170.209:4873/axios/-/axios-0.21.4.tgz",
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
"version": "0.26.1",
"resolved": "http://94.130.170.209:4873/axios/-/axios-0.26.1.tgz",
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
"requires": {
"follow-redirects": "^1.14.0"
"follow-redirects": "^1.14.8"
}
},
"balanced-match": {
+4 -4
View File
@@ -42,7 +42,7 @@
"@psf/bitcoincash-ops": "2.0.0",
"@psf/bitcoincashjs-lib": "4.0.2",
"@psf/coininfo": "4.0.0",
"axios": "^0.21.4",
"axios": "0.26.1",
"bc-bip68": "1.0.5",
"bchaddrjs-slp": "0.2.5",
"bigi": "1.4.2",
@@ -63,7 +63,7 @@
"wif": "2.0.6"
},
"devDependencies": {
"apidoc": "^0.50.5",
"apidoc": "0.50.5",
"assert": "2.0.0",
"chai": "4.1.2",
"coveralls": "3.0.2",
@@ -73,9 +73,9 @@
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-standard": "4.0.0",
"husky": "^4.3.8",
"husky": "4.3.8",
"lodash.clonedeep": "4.5.0",
"mocha": "^9.2.1",
"mocha": "9.2.1",
"node-mocks-http": "1.7.0",
"nyc": "15.1.0",
"semantic-release": "^19.0.2",
-1241
View File
File diff suppressed because it is too large Load Diff
-270
View File
@@ -20,276 +20,6 @@ class UTXO {
this.BigNumber = BigNumber
}
/**
* @api Utxo.getOld() getOld()
* @apiName getOld
* @apiGroup UTXO
* @apiDescription Get UTXOs for an address (from SLPDB)
*
* 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.getOld('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
* }
* ],
* "nullUtxos": [],
* "slpUtxos": {
* "type1": {
* "mintBatons": [
* {
* "height": 674512,
* "tx_hash": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tx_pos": 2,
* "value": 546,
* "txid": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "vout": 2,
* "utxoType": "minting-baton",
* "tokenId": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tokenTicker": "SLPTEST",
* "tokenName": "SLP Test Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 8,
* "tokenType": 1,
* "isValid": true
* }
* ],
* "tokens": [
* {
* "height": 674512,
* "tx_hash": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tx_pos": 1,
* "value": 546,
* "txid": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "100",
* "tokenId": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tokenTicker": "SLPTEST",
* "tokenName": "SLP Test Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 8,
* "tokenType": 1,
* "isValid": true
* }
* ]
* },
* "nft": {
* "groupMintBatons": [
* {
* "height": 674513,
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "tx_pos": 2,
* "value": 546,
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "vout": 2,
* "utxoType": "minting-baton",
* "transactionType": "mint",
* "tokenId": "a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395",
* "tokenType": 129,
* "tokenTicker": "NFTGT",
* "tokenName": "NFT Test Group Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "mintBatonVout": 2,
* "isValid": true
* }
* ],
* "groupTokens": [
* {
* "height": 674513,
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "tx_pos": 1,
* "value": 546,
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "10",
* "transactionType": "mint",
* "tokenId": "a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395",
* "tokenType": 129,
* "tokenTicker": "NFTGT",
* "tokenName": "NFT Test Group Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "mintBatonVout": 2,
* "isValid": true
* }
* ],
* "tokens": [
* {
* "height": 674512,
* "tx_hash": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "tx_pos": 1,
* "value": 546,
* "txid": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "1",
* "tokenId": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "tokenTicker": "NFT004",
* "tokenName": "NFT Child",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "tokenType": 65,
* "isValid": true
* }
* ]
* }
* }
* }
* ]
*
*
*/
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]
// Throw an error if there are more than 20 addresses passed in at a time.
if (address.length > 20) throw new Error('Too many elements, 20 max.')
// Covert each address to a BCH address.
const addr = address.map(elem => this.slp.Address.toCashAddress(elem))
// Get the UTXOs associated with the address.
const utxoData = await this.electrumx.utxo(addr)
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
// Hydate the utxos with token information.
let hydratedUtxos
if (useWhitelist) {
hydratedUtxos = await this.slp.Utils.hydrateUtxosWL(utxoData.utxos)
} else {
hydratedUtxos = await this.slp.Utils.hydrateUtxos(utxoData.utxos)
}
// console.log(`hydratedUtxos: ${JSON.stringify(hydratedUtxos, null, 2)}`)
const retAry = [] // Return array
// Loop through each address.
for (let i = 0; i < hydratedUtxos.slpUtxos.length; i++) {
const thisAddr = hydratedUtxos.slpUtxos[i]
const addrObj = {
address: thisAddr.address
}
// Filter out the different types of UTXOs.
addrObj.bchUtxos = thisAddr.utxos.filter(elem => elem.isValid === false)
addrObj.nullUtxos = thisAddr.utxos.filter(elem => elem.isValid === null)
// Break down the SLP UTXOs.
addrObj.slpUtxos = {
type1: {},
nft: {}
}
// Token Type 1 Minting Batons.
addrObj.slpUtxos.type1.mintBatons = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs1 = elem.tokenType === 1
const isMintingBaton = elem.utxoType === 'minting-baton'
return isValid && tokenTypeIs1 && isMintingBaton
})
// Token Type 1 tokens.
addrObj.slpUtxos.type1.tokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs1 = elem.tokenType === 1
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs1 && isToken
})
// NFT Group Minting Batons
addrObj.slpUtxos.nft.groupMintBatons = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs129 = elem.tokenType === 129
const isMintingBaton = elem.utxoType === 'minting-baton'
return isValid && tokenTypeIs129 && isMintingBaton
})
// NFT Group tokens
addrObj.slpUtxos.nft.groupTokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs129 = elem.tokenType === 129
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs129 && isToken
})
// NFT (Child) tokens
addrObj.slpUtxos.nft.tokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs65 = elem.tokenType === 65
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs65 && isToken
})
retAry.push(addrObj)
}
return retAry
} catch (err) {
console.error('Error in bchjs.utxo.get()')
if (err.error) throw new Error(err.error)
throw err
}
}
/**
* @api Utxo.get() get()
* @apiName get
-470
View File
@@ -497,476 +497,6 @@ describe('#SLP', () => {
})
})
describe('#tokenUtxoDetailsWL', () => {
it('should return details for a simple SEND SLP token utxo', async () => {
const utxos = [
{
height: 660554,
tx_hash:
'89b3f0c84efe8b01b24e2d7ac08636de5781f31dbb84478e3de868ca0a7ed93a',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data[0], 'txid')
assert.property(data[0], 'vout')
assert.property(data[0], 'height')
assert.property(data[0], 'utxoType')
assert.property(data[0], 'tokenId')
assert.property(data[0], 'tokenTicker')
assert.property(data[0], 'tokenName')
assert.property(data[0], 'tokenDocumentUrl')
assert.property(data[0], 'tokenDocumentHash')
assert.property(data[0], 'decimals')
assert.property(data[0], 'tokenQty')
assert.property(data[0], 'isValid')
assert.equal(data[0].isValid, true)
})
it('should return false for BCH-only UTXOs', async () => {
const utxos = [
{
txid:
'a937f792c7c9eb23b4f344ce5c233d1ac0909217d0a504d71e6b1e4efb864a3b',
vout: 0,
amount: 0.00001,
satoshis: 1000,
confirmations: 0,
ts: 1578424704
},
{
txid:
'53fd141c2e999e080a5860887441a2c45e9cbe262027e2bd2ac998fc76e43c44',
vout: 0,
amount: 0.00001,
satoshis: 1000,
confirmations: 0,
ts: 1578424634
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data)
assert.equal(data[0].isValid, false)
assert.equal(data[1].isValid, false)
})
it('should handle a dust attack', async () => {
// it("#dustattack", async () => {
const utxos = [
{
height: 655965,
tx_hash:
'a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e',
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
'a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e',
vout: 151,
address: 'bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww',
hdIndex: 11
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
it('should handle null SLPDB validations', async () => {
const utxos = [
{
height: 665577,
tx_hash:
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419',
tx_pos: 1,
value: 546
},
{
height: 665577,
tx_hash:
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data)
// assert.equal(data[0].isValid, null)
})
})
describe('#balancesForAddress', () => {
it('should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9', async () => {
const balances = await bchjs.SLP.Utils.balancesForAddress(
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.hasAllKeys(balances[0], [
'tokenId',
'balanceString',
'balance',
'decimalCount',
'slpAddress'
])
})
it('should fetch balances for multiple addresses', async () => {
const addresses = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57'
]
const balances = await bchjs.SLP.Utils.balancesForAddress(addresses)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.isArray(balances[0])
assert.hasAllKeys(balances[0][0], [
'tokenId',
'balanceString',
'balance',
'decimalCount',
'slpAddress'
])
})
})
describe('#hydrateUtxos-', () => {
it('should hydrate UTXOs', async () => {
const utxos = [
{
utxos: [
{
txid:
'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
vout: 3,
value: '6816',
height: 606848,
confirmations: 13,
satoshis: 6816
},
{
txid:
'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
vout: 2,
value: '546',
height: 606848,
confirmations: 13,
satoshis: 546
}
]
}
]
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 1)
assert.equal(result.slpUtxos[0].utxos.length, 2)
// Test the non-slp UTXO.
assert.property(result.slpUtxos[0].utxos[0], 'txid')
assert.property(result.slpUtxos[0].utxos[0], 'vout')
assert.property(result.slpUtxos[0].utxos[0], 'value')
assert.property(result.slpUtxos[0].utxos[0], 'height')
assert.property(result.slpUtxos[0].utxos[0], 'confirmations')
assert.property(result.slpUtxos[0].utxos[0], 'satoshis')
assert.property(result.slpUtxos[0].utxos[0], 'isValid')
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
// Test the slp UTXO.
assert.property(result.slpUtxos[0].utxos[1], 'txid')
assert.property(result.slpUtxos[0].utxos[1], 'vout')
assert.property(result.slpUtxos[0].utxos[1], 'value')
assert.property(result.slpUtxos[0].utxos[1], 'height')
assert.property(result.slpUtxos[0].utxos[1], 'confirmations')
assert.property(result.slpUtxos[0].utxos[1], 'satoshis')
assert.property(result.slpUtxos[0].utxos[1], 'isValid')
assert.equal(result.slpUtxos[0].utxos[1].isValid, true)
assert.property(result.slpUtxos[0].utxos[1], 'transactionType')
assert.property(result.slpUtxos[0].utxos[1], 'tokenId')
assert.property(result.slpUtxos[0].utxos[1], 'tokenTicker')
assert.property(result.slpUtxos[0].utxos[1], 'tokenName')
assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentUrl')
assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentHash')
assert.property(result.slpUtxos[0].utxos[1], 'decimals')
assert.property(result.slpUtxos[0].utxos[1], 'tokenType')
assert.property(result.slpUtxos[0].utxos[1], 'tokenQty')
})
it('should process data directly from Electrumx', async () => {
const addrs = [
'bitcoincash:qqnt53cfw990u6y38xgezm0lfa8hknw0wueezcpagp',
'bitcoincash:qrh3qgtax6adegzc7zxm6m4sgrv9wq28yqnfn33ce5',
'bitcoincash:qqcaee5w4ws77n8s2gzy6fwtma6uxd7ctq8553e495'
]
const utxos = await bchjs.Electrumx.utxo(addrs)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 3)
assert.equal(result.slpUtxos[0].utxos.length, 1)
assert.equal(result.slpUtxos[1].utxos.length, 1)
assert.equal(result.slpUtxos[2].utxos.length, 2)
try {
// Test the expected values.
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
assert.equal(result.slpUtxos[1].utxos[0].isValid, true)
assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, 'TROUT')
assert.equal(result.slpUtxos[2].utxos[0].isValid, false)
assert.equal(result.slpUtxos[2].utxos[1].isValid, true)
assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, 'VALENTINE')
} catch (err) {
console.error(
'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"'
)
console.log('Error: ', err)
}
})
it('should handle null SLPDB validations', async () => {
const utxos = [
{
height: 665577,
tx_hash:
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419',
tx_pos: 1,
value: 546
},
{
height: 665577,
tx_hash:
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.hydrateUtxos([{ utxos: utxos }])
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data.slpUtxos)
assert.equal(data.slpUtxos[0].isValid, null)
})
})
describe('#hydrateUtxosWL', () => {
it('should hydrate UTXOs', async () => {
const utxos = [
{
utxos: [
{
txid:
'89b3f0c84efe8b01b24e2d7ac08636de5781f31dbb84478e3de868ca0a7ed93a',
vout: 1,
value: 546
}
]
}
]
const result = await bchjs.SLP.Utils.hydrateUtxosWL(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 1)
assert.equal(result.slpUtxos[0].utxos.length, 1)
// Test the non-slp UTXO.
assert.property(result.slpUtxos[0].utxos[0], 'txid')
assert.property(result.slpUtxos[0].utxos[0], 'vout')
assert.property(result.slpUtxos[0].utxos[0], 'value')
assert.property(result.slpUtxos[0].utxos[0], 'isValid')
assert.equal(result.slpUtxos[0].utxos[0].isValid, true)
})
it('should process data directly from Electrumx', async () => {
const addrs = [
'bitcoincash:qqnt53cfw990u6y38xgezm0lfa8hknw0wueezcpagp',
'bitcoincash:qrh3qgtax6adegzc7zxm6m4sgrv9wq28yqnfn33ce5',
'bitcoincash:qqcaee5w4ws77n8s2gzy6fwtma6uxd7ctq8553e495'
]
const utxos = await bchjs.Electrumx.utxo(addrs)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = await bchjs.SLP.Utils.hydrateUtxosWL(utxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 3)
assert.equal(result.slpUtxos[0].utxos.length, 1)
assert.equal(result.slpUtxos[1].utxos.length, 1)
assert.equal(result.slpUtxos[2].utxos.length, 2)
try {
// Test the expected values.
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
assert.equal(result.slpUtxos[1].utxos[0].isValid, true)
assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, 'TROUT')
assert.equal(result.slpUtxos[2].utxos[0].isValid, false)
// assert.equal(result.slpUtxos[2].utxos[1].isValid, null)
assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, 'VALENTINE')
} catch (err) {
console.error(
'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"'
)
console.log('Error: ', err)
}
})
it('should handle null SLPDB validations', async () => {
const utxos = [
{
height: 665577,
tx_hash:
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419',
tx_pos: 1,
value: 546
},
{
height: 665577,
tx_hash:
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.hydrateUtxosWL([{ utxos: utxos }])
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data.slpUtxos)
assert.equal(data.slpUtxos[0].isValid, null)
})
})
describe('#validateTxid', () => {
// This test is not necessary.
it('should handle a null response from SLPDB', async () => {
const txid =
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419'
const result = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txid)
// assert.equal(result[0].valid, null)
})
it('should handle a null response from SLPDB', async () => {
const txid = [
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419',
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
]
const result = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txid[0])
// assert.equal(result[0].valid, null)
})
})
describe('#validateTxid2', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'txid')
assert.equal(result.txid, txid)
assert.property(result, 'isValid')
assert.equal(result.isValid, false)
})
it('should validate a known valid TXID', async () => {
const txid =
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'txid')
assert.equal(result.txid, txid)
assert.property(result, 'isValid')
assert.equal(result.isValid, true)
})
})
describe('#getWhitelist', () => {
it('should get the whitelist', async () => {
const result = await bchjs.SLP.Utils.getWhitelist()
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'name')
assert.property(result[1], 'tokenId')
})
})
describe('#getStatus', () => {
it('should return the current block height of the SLPDB indexer', async () => {
const result = await bchjs.SLP.Utils.getStatus()
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'bchBlockHeight')
assert.property(result, 'slpProcessedBlockHeight')
})
})
describe('#waterfallValidateTxid', () => {
it('should validate known good txid not in whitelist', async () => {
const txid =
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
const result = await bchjs.SLP.Utils.waterfallValidateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, true)
})
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.waterfallValidateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, false)
})
})
describe('#tokentype1', () => {
describe('#getHexOpReturn', () => {
it('should return OP_RETURN object ', async () => {
-724
View File
@@ -45,216 +45,6 @@ describe('#SLP Utils', () => {
sandbox.restore()
})
describe('#list', () => {
it('should list single SLP token by id', async () => {
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.mockToken })
const tokenId =
'4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84'
const list = await uut.Utils.list(tokenId)
// console.log(`list: ${JSON.stringify(list, null, 2)}`)
assert.equal(list.id, tokenId)
assert.property(list, 'decimals')
assert.property(list, 'symbol')
assert.property(list, 'documentUri')
assert.property(list, 'name')
})
it('should list multiple SLP tokens by array of ids', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'post')
.resolves({ data: mockData.mockList })
const tokenIds = [
'4276533bb702e7f8c9afd8aa61ebf016e95011dc3d54e55faa847ac1dd461e84',
'8fc284dcbc922f7bb7e2a443dc3af792f52923bba403fcf67ca028c88e89da0e'
]
const list = await uut.Utils.list(tokenIds)
// console.log(`list: ${JSON.stringify(list, null, 2)}`)
assert.isArray(list)
assert.property(list[0], 'symbol')
assert.property(list[1], 'symbol')
})
})
describe('#balancesForAddress', () => {
it('should throw an error if input is not a string or array of strings', async () => {
try {
const address = 1234
await uut.Utils.balancesForAddress(address)
assert.equal(true, false, 'Uh oh. Code path should not end here.')
} catch (err) {
// console.log(`Error: `, err)
assert.include(
err.message,
'Input address must be a string or array of strings'
)
}
})
it('should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9', async () => {
// Mock the call to the REST API
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.balancesForAddress })
}
// sandbox
// .stub(uut.Utils, "balancesForAddress")
// .resolves(mockData.balancesForAddress)
const balances = await uut.Utils.balancesForAddress(
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.hasAllKeys(balances[0], [
'tokenId',
'balanceString',
'balance',
'decimalCount',
'slpAddress'
])
})
it('should fetch balances for multiple addresses.', async () => {
const addresses = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57'
]
// Mock the call to the REST API
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.Utils.axios, 'post')
.resolves({ data: mockData.balancesForAddresses })
}
const balances = await uut.Utils.balancesForAddress(addresses)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.isArray(balances[0])
assert.hasAllKeys(balances[0][0], [
'tokenId',
'balanceString',
'balance',
'decimalCount',
'slpAddress'
])
})
})
describe('#validateTxid', () => {
it('should validate slp txid', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'post')
.resolves({ data: mockData.mockIsValidTxid })
const isValid = await uut.Utils.validateTxid(
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb'
)
assert.deepEqual(isValid, [
{
txid:
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
valid: true
}
])
})
})
describe('#balancesForToken', () => {
it('should retrieve token balances for a given tokenId', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.mockBalancesForToken })
const balances = await uut.Utils.balancesForToken(
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb'
)
assert.hasAnyKeys(balances[0], ['tokenBalance', 'slpAddress'])
})
})
describe('#tokenStats', () => {
it('should retrieve stats for a given tokenId', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.mockTokenStats })
const tokenStats = await uut.Utils.tokenStats(
'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb'
)
assert.hasAnyKeys(tokenStats, [
'circulatingSupply',
'decimals',
'documentUri',
'name',
'satoshisLockedUp',
'symbol',
'tokenId',
'totalBurned',
'totalMinted',
'txnsSinceGenesis',
'validAddresses',
'validUtxos'
])
})
})
describe('#transactions', () => {
it('should retrieve transactions for a given tokenId and address', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.mockTransactions })
const transactions = await uut.Utils.transactions(
'495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a',
'simpleledger:qrhvcy5xlegs858fjqf8ssl6a4f7wpstaqnt0wauwu'
)
assert.hasAnyKeys(transactions[0], ['txid', 'tokenDetails'])
})
})
describe('#burnTotal', () => {
it('should retrieve input, output and burn totals', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.mockBurnTotal })
const burnTotal = await uut.Utils.burnTotal(
'c7078a6c7400518a513a0bde1f4158cf740d08d3b5bfb19aa7b6657e2f4160de'
)
// console.log(`burnTotal: ${JSON.stringify(burnTotal, null, 2)}`)
assert.hasAnyKeys(burnTotal, [
'transactionId',
'inputTotal',
'outputTotal',
'burnTotal'
])
})
})
describe('#decodeOpReturn', () => {
it('should throw an error for a non-string input', async () => {
try {
@@ -1910,518 +1700,4 @@ describe('#SLP Utils', () => {
assert.equal(data[0].isValid, false)
})
})
describe('#tokenUtxoDetailsWL', () => {
it('should return details for a simple SEND SLP token utxo', async () => {
// Mock the call to REST API
// Stub the calls to decodeOpReturn.
sandbox
.stub(uut.Utils, 'decodeOpReturn')
.onCall(0)
.resolves({
tokenType: 1,
txType: 'SEND',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['200000000', '99887500000000']
})
.onCall(1)
.resolves({
tokenType: 1,
txType: 'GENESIS',
ticker: 'TOK-CH',
name: 'TokyoCash',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
documentUri: '',
documentHash: '',
decimals: 8,
mintBatonVout: 0,
qty: '2100000000000000'
})
// Stub the call to validateTxid
sandbox.stub(uut.Utils, 'validateTxid3').resolves([
{
txid:
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
valid: true
}
])
const utxos = [
{
txid:
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
const data = await uut.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data[0], 'txid')
assert.property(data[0], 'vout')
assert.property(data[0], 'height')
assert.property(data[0], 'utxoType')
assert.property(data[0], 'tokenId')
assert.property(data[0], 'tokenTicker')
assert.property(data[0], 'tokenName')
assert.property(data[0], 'tokenDocumentUrl')
assert.property(data[0], 'tokenDocumentHash')
assert.property(data[0], 'decimals')
assert.property(data[0], 'tokenQty')
assert.property(data[0], 'isValid')
assert.equal(data[0].isValid, true)
})
})
describe('#txDetails', () => {
it('should throw an error if txid is not included', async () => {
try {
await uut.Utils.txDetails()
} catch (err) {
assert.include(
err.message,
'txid string must be included',
'Expected error message.'
)
}
})
it('should throw error for non-existent txid', async () => {
try {
// Mock the call to the REST API
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.Utils.axios, 'get')
// .resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn })
.throws({ error: 'TXID not found' })
}
const txid =
'd284e71227ec89f714b964d8eda595be6392bebd2fac46082bc5a9ce6fb7b33e'
await uut.Utils.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.fail('Unexpected result')
} catch (err) {
// console.log(`err: `, err)
assert.include(err.error, 'TXID not found', 'Expected error message.')
}
})
it('should return details for an SLP txid', async () => {
// Mock the call to the REST API
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.mockTxDetails })
}
const txid =
'9dbaaafc48c49a21beabada8de632009288a2cd52eecefd0c00edcffca9955d0'
const result = await uut.Utils.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
'txid',
'version',
'locktime',
'vin',
'vout',
'blockhash',
'blockheight',
'confirmations',
'time',
'blocktime',
'valueOut',
'size',
'valueIn',
'fees',
'tokenInfo',
'tokenIsValid'
])
})
})
describe('#hydrateUtxos', () => {
it('should throw an error if input is not an array', async () => {
try {
const utxos = 1234
await uut.Utils.hydrateUtxos(utxos)
assert.equal(true, false, 'Uh oh. Code path should not end here.')
} catch (err) {
// console.log(`Error: `, err)
assert.include(err.message, 'Input must be an array.')
}
})
})
describe('#hydrateUtxosWL', () => {
it('should throw an error if input is not an array', async () => {
try {
const utxos = 1234
await uut.Utils.hydrateUtxosWL(utxos)
assert.equal(true, false, 'Uh oh. Code path should not end here.')
} catch (err) {
// console.log(`Error: `, err)
assert.include(err.message, 'Input must be an array.')
}
})
})
describe('#validateTxid2', () => {
it('should throw an error if the input is an array', async () => {
try {
const txid = [
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
]
await uut.Utils.validateTxid2(txid)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log("err: ", err)
assert.include(err.message, 'txid must be 64 character string')
}
})
it('should throw an error for a malformed txid', async () => {
try {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783'
await uut.Utils.validateTxid2(txid)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log("err: ", err)
assert.include(err.message, 'txid must be 64 character string')
}
})
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
// Mock live network calls.
sandbox.stub(uut.Utils.axios, 'get').resolves({
data: {
txid: txid,
isValid: false,
msg: ''
}
})
const result = await uut.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'txid')
assert.equal(result.txid, txid)
assert.property(result, 'isValid')
assert.equal(result.isValid, false)
})
it('should validate a known valid TXID', async () => {
const txid =
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
// Mock live network calls.
sandbox.stub(uut.Utils.axios, 'get').resolves({
data: {
txid: txid,
isValid: true,
msg: ''
}
})
const result = await uut.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'txid')
assert.equal(result.txid, txid)
assert.property(result, 'isValid')
assert.equal(result.isValid, true)
})
// slp-validate can take a long time. bch-api cuts it off if it fails to
// return is less than 10 seconds. This test case handle this situation.
it('should handle timeout errors', async () => {
try {
const txid =
'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579'
// Mock live network calls.
sandbox.stub(uut.Utils.axios, 'get').rejects({
error:
'Network error: Could not communicate with full node or other external service.'
})
await uut.Utils.validateTxid2(txid)
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log("err: ", err)
assert.include(err.message, 'slp-validate timed out')
}
})
})
describe('#validateTxid', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
// Mock live network calls.
sandbox.stub(uut.Utils.axios, 'post').resolves({
data: mockData.mockValidateTxid3Invalid
})
const result = await uut.Utils.validateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, null)
})
/*
it("should 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"
]
const result = await uut.Utils.validateTxid(txids)
console.log(`result: ${JSON.stringify(result, null, 2)}`)
})
*/
})
describe('#waterfallValidateTxid', () => {
// This test ensures that the whitelist SLPDB is called before the
// general purpose SLPDB.
it('should call validateTxid3() first', async () => {
const txid = 'fakeTxid'
const retVal = [{ txid, valid: true }]
// Use stubs to force the desired code path.
sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal)
sandbox
.stub(uut.Utils, 'validateTxid')
.rejects(new Error('Unexpected code path'))
sandbox
.stub(uut.Utils, 'validateTxid2')
.rejects(new Error('Unexpected code path'))
const result = await uut.Utils.waterfallValidateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, true)
})
// This test ensures that the general purpose SLPDB is called after the
// whitelist SLPDB, but before slp-api.
it('should call validateTxid() second', async () => {
const txid = 'fakeTxid'
const retVal1 = [{ txid, valid: null }]
const retVal2 = [{ txid, valid: true }]
// Use stubs to force the desired code path.
sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal1)
sandbox.stub(uut.Utils, 'validateTxid').resolves(retVal2)
sandbox
.stub(uut.Utils, 'validateTxid2')
.rejects(new Error('Unexpected code path'))
const result = await uut.Utils.waterfallValidateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, true)
})
// This test ensures that slp-api is called if both SLPDBs return null.
it('should call slp-api last', async () => {
const txid = 'fakeTxid'
const retVal1 = [{ txid, valid: null }]
// Use stubs to force the desired code path.
sandbox.stub(uut.Utils, 'validateTxid3').resolves(retVal1)
sandbox.stub(uut.Utils, 'validateTxid').resolves(retVal1)
sandbox.stub(uut.Utils, 'validateTxid2').resolves({ isValid: true })
const result = await uut.Utils.waterfallValidateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, true)
})
// CT 08-09-21 Changed behavior. validateTxid3() is now wrapped in a
// try/catch statement that exits quietly if there is an error.
// it('catches and throws an error', async () => {
// try {
// const txid = 'fakeTxid'
//
// // Force an error
// sandbox
// .stub(uut.Utils, 'validateTxid3')
// .rejects(new Error('fake error'))
//
// await await uut.Utils.waterfallValidateTxid(txid)
//
// assert.fail('Unexpected result')
// } catch (err) {
// // console.log(`err.message: ${err.message}`)
// assert.include(err.message, 'fake error')
// }
// })
})
describe('#getWhitelist', () => {
it('should return the list', async () => {
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.mockWhitelist })
const result = await uut.Utils.getWhitelist()
assert.isArray(result)
assert.property(result[0], 'name')
assert.property(result[1], 'tokenId')
})
it('catches and throws an error', async () => {
try {
sandbox.stub(uut.Utils.axios, 'get').rejects({
error:
'Network error: Could not communicate with full node or other external service.'
})
await uut.Utils.getWhitelist()
assert.fail('Unexpected result')
} catch (err) {
console.log('err: ', err)
assert.include(err.error, 'Network error')
}
})
})
describe('#validateTxid3', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
// Mock live network calls.
sandbox.stub(uut.Utils.axios, 'post').resolves({
data: mockData.mockValidateTxid3Invalid
})
const result = await uut.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, null)
})
it('should handle an array with a single element', async () => {
sandbox
.stub(uut.Utils.axios, 'post')
.resolves({ data: mockData.mockValidateTxid3Valid })
const txid = [
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
]
const result = await uut.Utils.validateTxid3(txid)
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 an single string input', async () => {
sandbox
.stub(uut.Utils.axios, 'post')
.resolves({ data: mockData.mockValidateTxid3Valid })
const txid =
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await uut.Utils.validateTxid3(txid)
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('catches and throws an error', async () => {
try {
sandbox.stub(uut.Utils.axios, 'post').rejects({
error:
'Network error: Could not communicate with full node or other external service.'
})
await uut.Utils.validateTxid3()
assert.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log("err: ", err)
assert.include(err.error, 'Network error')
}
})
})
describe('#getStatus', () => {
it('should return the current block height of the SLPDB indexer', async () => {
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.slpdbStatus })
const result = await uut.Utils.getStatus()
// console.log(`result: `, result)
assert.property(result, 'bchBlockHeight')
assert.property(result, 'slpProcessedBlockHeight')
})
})
})
-131
View File
@@ -15,137 +15,6 @@ describe('#utxo', () => {
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
describe('#getOld', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// Mock dependencies.
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves(mockData.mockUtxoData)
sandbox
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
.resolves(mockData.mockHydratedUtxos)
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
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 catch and throw an error', async () => {
try {
// Force an error
sandbox
.stub(bchjs.Utxo.electrumx, 'utxo')
.rejects(new Error('test error'))
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
await bchjs.Utxo.getOld(addr)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should handle an array of addresses', async () => {
// Mock dependencies.
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves({ utxos: {} })
sandbox
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
.resolves(mockData.mockTwoHydratedAddrs)
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.equal(result.length, 2)
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 throw an error for array of 21 elements', async () => {
try {
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const addrs = []
for (let i = 0; i < 21; i++) {
addrs.push(addr)
}
await bchjs.Utxo.getOld(addrs)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Too many elements, 20 max.')
}
})
it('should handle NFTs and minting batons', async () => {
// Mock dependencies.
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves({ utxos: {} })
sandbox
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
.resolves(mockData.mockEveryUtxoType)
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 () => {
// Mock dependencies.
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves(mockData.mockUtxoData)
sandbox
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxosWL')
.resolves(mockData.mockHydratedUtxos)
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)
})
})
describe('#findBiggestUtxo', () => {
it('should throw error for non-array input', async () => {
try {