Compare commits

..
58 Commits
Author SHA1 Message Date
Chris Troutner a08a4236d6 Merge pull request #261 from Permissionless-Software-Foundation/ct-unstable
feat(Utxo.get()): Creating infoUtxo category for utxos less than 1000…
2023-03-31 13:43:19 -07:00
Chris Troutner e700a22560 feat(Utxo.get()): Creating infoUtxo category for utxos less than 1000 sats 2023-03-31 13:41:37 -07:00
Chris Troutner 4573c63ca3 Merge pull request #260 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Commenting out integration tests that fails due to 429 er…
2023-03-14 06:48:37 -07:00
Chris Troutner bd18e8a755 fix(tests): Commenting out integration tests that fails due to 429 errors 2023-03-14 06:28:50 -07:00
Chris Troutner cf8f48ffe1 Merge pull request #259 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Commenting out integration tests that are commonly failing
2023-03-11 17:28:44 -08:00
Chris Troutner fc96b04a64 fix(tests): Commenting out integration tests that are commonly failing 2023-03-11 17:26:00 -08:00
Chris Troutner 661a759f80 Merge pull request #258 from Permissionless-Software-Foundation/ct-unstable
feat(tx history): Updating to reflect changes in bch-api
2023-02-07 10:03:22 -08:00
Chris Troutner ba915e84cf feat(tx history): Updating to reflect changes in bch-api 2023-02-07 10:01:41 -08:00
Chris Troutner eae7172e3d Merge pull request #257 from Permissionless-Software-Foundation/ct-unstable
fix(getTokenData2()): Adding updateCache input argument
2023-01-26 17:49:48 -08:00
Chris Troutner 6dd2a0590f fix(getTokenData2()): Adding updateCache input argument 2023-01-26 17:48:11 -08:00
Chris Troutner 3152e87b5d Merge pull request #256 from Permissionless-Software-Foundation/electron
fix(bitcoincashjs-lib): Updating to v4.0.3
2023-01-22 09:41:03 -08:00
Chris Troutner 4b66f3841d fix(bitcoincashjs-lib): Updating to v4.0.3 2023-01-22 09:39:03 -08:00
Chris Troutner 9f98a47682 Merge pull request #255 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Fixing broken integration tests
2023-01-13 07:21:15 -08:00
Chris Troutner 0833e8ca78 fix(tests): Fixing broken integration tests 2023-01-13 07:13:47 -08:00
Chris Troutner e01a18b65e Merge pull request #253 from Permissionless-Software-Foundation/ct-unstable
fix(encryption): Adding documentation for the encryption library
2022-12-14 10:09:43 -08:00
Chris Troutner 055cd7feca fix(encryption): Adding documentation for the encryption library 2022-12-14 10:05:41 -08:00
Chris Troutner 906d9b2094 Merge pull request #252 from Permissionless-Software-Foundation/ct-unstable
fix(utxo.get()): Detecting out-of-sync SLP indexer, protecting tokens
2022-11-01 15:01:44 -07:00
Chris Troutner 9326fd6e59 fix(utxo.get()): Detecting out-of-sync SLP indexer, protecting tokens 2022-11-01 14:58:28 -07:00
Chris Troutner 1d91eed5df Fixing script in package.json 2022-10-16 13:23:16 -07:00
Chris Troutner 62e56c832b Merge pull request #251 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Removing integration tests that trips rate limit
2022-10-13 10:33:55 -07:00
Chris Troutner d5b7e4ab1d fix(tests): Removing integration tests that trips rate limit 2022-10-13 10:31:22 -07:00
Chris Troutner 2137a75fce Merge pull request #250 from Permissionless-Software-Foundation/ct-unstable
fix(getTokenData2): Adding api documentation
2022-10-06 20:43:14 -07:00
Chris Troutner 8966cac210 fix(getTokenData2): Adding api documentation 2022-10-06 20:40:22 -07:00
Chris Troutner 9d32f828bc Cleaning up screen spam 2022-10-06 20:33:36 -07:00
Chris Troutner 91a7106818 Merge pull request #249 from Permissionless-Software-Foundation/ct-unstable
feat(getTokenData2): Adding new endpoint for token icons and other media
2022-10-06 20:27:00 -07:00
Chris Troutner 44e671dfbe fix(price): Removing dead price endpoint tests 2022-10-06 20:24:17 -07:00
Chris Troutner 979fb481de feat(getTokenData2): Adding token media endpoint 2022-10-06 18:30:39 -07:00
Chris Troutner 66253ed5a6 feat(bchjs.Price.current): Removing dead endpoint 2022-10-06 17:58:00 -07:00
Chris Troutner 6ef5ac0f7c feat(getTokenData2): Adding new endpoint for token icons and other media 2022-10-06 17:57:11 -07:00
Chris Troutner 21c322d0df Merge pull request #247 from Permissionless-Software-Foundation/ct-unstable
fix(getTokenData): Adding ability to retrieve tx history
2022-07-12 14:48:22 -07:00
Chris Troutner 1b0e9ef0ff fix(getTokenData): Adding ability to retrieve tx history 2022-07-12 14:24:09 -07:00
Chris Troutner 78776bee93 Merge pull request #246 from Permissionless-Software-Foundation/ct-unstable
fix(deps): npm audit shows now vulns
2022-07-11 07:24:47 -07:00
Chris Troutner a6e3155f37 fix(deps): npm audit shows now vulns 2022-07-11 07:20:20 -07:00
Chris Troutner 47f60bccde Merge pull request #245 from Permissionless-Software-Foundation/ct-unstable
fix(ecash): Adding token support for ecash
2022-07-11 07:11:36 -07:00
Chris Troutner d0a3b27682 fix(ecash): Adding token support for ecash 2022-07-11 07:09:44 -07:00
Chris Troutner 372d1b099d Merge pull request #244 from Permissionless-Software-Foundation/ct-unstable
fix(address): Adding toEtokenAddress() function
2022-07-10 09:50:44 -07:00
Chris Troutner dbfaf9c4ab fix(address): Adding toEtokenAddress() function 2022-07-10 09:47:49 -07:00
Chris Troutner eaa08f74e7 Merge pull request #243 from Permissionless-Software-Foundation/ct-unstable
fix(test): Updating integration test
2022-05-15 07:53:53 -07:00
Chris Troutner 9ac620ee64 fix(test): Updating integration test 2022-05-15 07:50:57 -07:00
Chris Troutner ac0a4b0407 Merge pull request #242 from Permissionless-Software-Foundation/ct-unstable
feat(Utxo.isValid()): can test if a UTXO has been spent
2022-05-10 09:34:37 -07:00
Chris Troutner 686012037f feat(Utxo.isValid()): can test if a UTXO has been spent 2022-05-10 09:30:57 -07:00
Chris Troutner cfb7b9f779 Merge pull request #241 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Fixing broken UTXO.get() test
2022-05-09 10:35:21 -07:00
Chris Troutner 06e5af4b5d fix(tests): Fixing broken UTXO.get() test 2022-05-09 10:33:06 -07:00
Chris Troutner 249e93a3f4 Merge pull request #240 from Permissionless-Software-Foundation/ct-unstable
fix(test): Updating UTXO integration tests
2022-05-09 10:25:58 -07:00
Chris Troutner 761a2d226c fix(test): Updating UTXO integration tests 2022-05-09 10:24:00 -07:00
Chris Troutner 5e5e636831 Merge pull request #239 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Fixing broken integration tests for token data
2022-05-09 09:49:02 -07:00
Chris Troutner 2adc2dfebb fix(tests): Fixing broken integration tests for token data 2022-05-09 09:45:28 -07:00
Chris Troutner f9bc44ccb7 Merge pull request #238 from Permissionless-Software-Foundation/ct-unstable
fix(token data): Returning CID rather than content itself
2022-05-07 20:59:55 -07:00
Chris Troutner 298b8f56ff Merge branch 'temp-token-data' into ct-unstable 2022-05-07 20:54:49 -07:00
Chris Troutner 139da8cf7b fix(token data): Returning CID rather than content itself 2022-05-07 20:54:20 -07:00
Chris Troutner 08e03c14d8 Merge pull request #234 from Permissionless-Software-Foundation/dh-psfSlpIndexer
feat(slpindexer): Added bchjs.PsfSlpIndexer.getTokenData()
2022-05-07 20:25:15 -07:00
Chris Troutner 7da010e7af Using config.restURL instead of hard-coded URL 2022-05-07 20:20:30 -07:00
Chris Troutner 7b6eebe7c7 Merge branch 'master' into dh-psfSlpIndexer 2022-05-07 20:05:25 -07:00
Chris Troutner 843f5eacef Merge pull request #237 from Permissionless-Software-Foundation/ct-unstable
fix(Utxo.get()): Added additional integration tests
2022-05-05 12:08:46 -07:00
Chris Troutner 416e302ca5 fix(Utxo.get()): Added additional integration tests 2022-05-05 12:06:59 -07:00
Chris Troutner 1c66da70b6 Merge pull request #236 from Permissionless-Software-Foundation/ct-unstable
fix(mintNFTGroupOpReturn()): Updating to use UTXOs from psf-slp-indexer
2022-05-05 10:45:59 -07:00
Chris Troutner 59e92adf0b fix(mintNFTGroupOpReturn()): Updating to use UTXOs from psf-slp-indexer 2022-05-05 10:43:40 -07:00
Daniel Gonzalez d144b13500 feat(slpindexer): Added bchjs.PsfSlpIndexer.getTokenData() 2022-05-04 18:29:54 -04:00
26 changed files with 9081 additions and 9600 deletions
+8269 -9520
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -12,14 +12,14 @@
"test": "nyc mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
"test:integration": "npm run test:integration:bchn",
"test:integration:nft": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 -g '#nft1' test/integration/chains/bchn/slp.js",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v5/ && 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: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.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:decatur:abc": "export RESTURL=http://192.168.2.142: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/",
@@ -40,7 +40,7 @@
"@chris.troutner/bip32-utils": "1.0.5",
"@psf/bip21": "2.0.1",
"@psf/bitcoincash-ops": "2.0.0",
"@psf/bitcoincashjs-lib": "4.0.2",
"@psf/bitcoincashjs-lib": "4.0.3",
"@psf/coininfo": "4.0.0",
"axios": "0.26.1",
"bc-bip68": "1.0.5",
+30 -1
View File
@@ -133,7 +133,7 @@ class Address {
* @api Address.toEcashAddress() toEcashAddress()
* @apiName toEcashAddress
* @apiGroup Address
* @apiDescription Convert legacy to cashAddress format
* @apiDescription Convert legacy to eCash (XEC) format
*
* @apiExample Example usage:
* // mainnet
@@ -158,6 +158,35 @@ class Address {
return ecashAddress.split(':')[1]
}
/**
* @api Address.toEtokenAddress() toEtokenAddress()
* @apiName toEtokenAddress
* @apiGroup Address
* @apiDescription Convert legacy to eToken (XEC) format
*
* @apiExample Example usage:
* // mainnet
* bchjs.Address.toEtokenAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2')
* // etoken:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
*
* // mainnet no prefix
* bchjs.Address.toEtokenAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2', false)
* // qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
*
*/
toEtokenAddress (address, prefix = true) {
const decoded = this._decode(address)
const etokenAddress = cashaddr.encode(
'etoken',
decoded.type,
decoded.hash
)
if (prefix) return etokenAddress
return etokenAddress.split(':')[1]
}
/**
* @api Address.ecashtoCashAddress() ecashtoCashAddress()
* @apiName ecashtoCashAddress
+10 -3
View File
@@ -7,6 +7,7 @@ const axios = require('axios')
// Local libraries.
const Blockchain = require('./blockchain')
// const Address = require('./address')
// let _this
@@ -32,7 +33,9 @@ class ElectrumX {
}
}
// Encapsulate dependencies
this.blockchain = new Blockchain(config)
// this.address = new Address(config)
// _this = this
}
@@ -104,6 +107,8 @@ class ElectrumX {
*/
async utxo (address) {
try {
// console.log(`electrumx.js/utxo() restURL: ${this.restURL}`)
// Handle single address.
if (typeof address === 'string') {
const response = await axios.get(
@@ -284,12 +289,12 @@ class ElectrumX {
* }
*
*/
async transactions (address, usrObj = null) {
async transactions (address, usrObj = null, allTxs = false) {
try {
// Handle single address.
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/transactions/${address}`,
`${this.restURL}electrumx/transactions/${address}/${allTxs}`,
this.axiosOptions
)
return response.data
@@ -300,7 +305,8 @@ class ElectrumX {
`${this.restURL}electrumx/transactions`,
{
addresses: address,
usrObj // pass user data when making an internal call.
usrObj, // pass user data when making an internal call.
allTxs
},
this.axiosOptions
)
@@ -310,6 +316,7 @@ class ElectrumX {
throw new Error('Input address must be a string or array of strings.')
} catch (error) {
// console.log('Error in transactions(): ', error)
if (error.response && error.response.data) throw error.response.data
else throw error
}
+22
View File
@@ -32,6 +32,28 @@ class Encryption {
_this = this
}
/**
* @api encryption.getPubKey() getPubKey()
* @apiName Encryption getPubKey()
* @apiGroup Encryption
* @apiDescription Get the public key for an address
* Given an address, the command will search the blockchain for a public
* key associated with that address. The address needs to have made at least
* one spend transaction, in order for its public key to be retrievable.
*
* @apiExample Example usage:
*(async () => {
* try {
* const addr = 'bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d'
* const pubkey = await bchjs.encryption.getPubKey(addr);
* console.log(pubkey);
* } catch(err) {
* console.error(err)
* }
*})()
*
*/
// Search the blockchain for a public key associated with a BCH address.
async getPubKey (addr) {
try {
+13 -13
View File
@@ -30,19 +30,19 @@ class Price {
}
// This endpoint is deprecated. Documentation removed.
async current (currency = 'usd') {
try {
const response = await this.axios.get(
`https://index-api.bitcoin.com/api/v0/cash/price/${currency.toLowerCase()}`
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.price
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
// async current (currency = 'usd') {
// try {
// const response = await this.axios.get(
// `https://index-api.bitcoin.com/api/v0/cash/price/${currency.toLowerCase()}`
// )
// // console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
//
// return response.data.price
// } catch (err) {
// if (err.response && err.response.data) throw err.response.data
// else throw err
// }
// }
/**
* @api price.getUsd() getUsd()
+164 -1
View File
@@ -41,7 +41,7 @@ class PsfSlpIndexer {
this.rawTransaction = new RawTransaction(config)
this.slpUtils = new SlpUtils(config)
// _this = this
// _this = this
}
/**
@@ -194,6 +194,7 @@ class PsfSlpIndexer {
* }
* }
*
*
*/
async tokenStats (tokenId, withTxHistory = false) {
@@ -374,6 +375,168 @@ class PsfSlpIndexer {
return false
}
}
/**
* @api PsfSlpIndexer.getTokenData() getTokenData()
* @apiName Token Data
* @apiGroup PSF SLP
* @apiDescription Get mutable and immutable data if the token contains them.
*
* @apiExample Example usage:
* (async () => {
* try {
* let tokenData = await bchjs.PsfSlpIndexer.getTokenData('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
* console.log(tokenData)
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* genesisData: {
* 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'
* ]
* },
* immutableData :{
* issuer:"FullStack.cash.",
* website:"https://fullstack.cash/",
* dateCreated:"2022-01-11"
* },
* mutableData :{
* "tokenIcon":"https://gateway.ipfs.io/ipfs/bafybeiehitanirn5gmhqjg44xrmdtomn4n5lu5yjoepsvgpswk5mggaw6i/LP_logo-1.png",
* "about":"Mutable data managed with npm package: https://www.npmjs.com/package/slp-mutable-data"
* }
* }
*
*/
async getTokenData (tokenId, withTxHistory = false) {
try {
const url = `${this.restURL}psf/slp/token/data`
// console.log(`url: ${url}`)
// Handle single address.
if (typeof tokenId === 'string') {
const response = await axios.post(
// 'https://bchn.fullstack.cash/v5/psf/slp/token/data/',
url,
{ tokenId, withTxHistory },
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.getTokenData2() getTokenData2()
* @apiName Token Data
* @apiGroup PSF SLP
* @apiDescription Get token icon and other media associated with a token.
*
* Get the icon for a token, given it's token ID.
* This function expects a string input of a token ID property.
* This function returns an object with a tokenIcon property that contains
* the URL to the icon.
*
* The output object always have these properties:
* - tokenIcon: A url to the token icon, if it exists.
* - tokenStats: Data about the token from psf-slp-indexer.
* - optimizedTokenIcon: An alternative, potentially more optimal, url to the token icon, if it exists.
* - iconRepoCompatible: true if the token icon is available via token.bch.sx
* - ps002Compatible: true if the token icon is compatible with PS007 specification.
*
* @apiExample Example usage:
* (async () => {
* try {
* let tokenData = await bchjs.PsfSlpIndexer.getTokenData2('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
* console.log(tokenData)
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* tokenStats: {
* type: 1,
* ticker: 'CTAIA006',
* name: 'CTAIA006 - AI Art by Chris Troutner',
* tokenId: '0e4543f820699294ab57e02ee2b1815a8bbc7b17a4333e4a138034e4b2324a61',
* documentUri: 'ipfs://bafybeia5yuq7rg6jmwquako7t277cwrobcunz7cumqrv4wn6bgfvthemku',
* documentHash: '78a00e9db312b8fff4e5c37cf592be83e6bab7f3bd5a54c9545bad5d4f3ee0f5',
* decimals: 0,
* mintBatonIsActive: false,
* tokensInCirculationBN: '1',
* tokensInCirculationStr: '1',
* blockCreated: 757507,
* totalBurned: '0',
* totalMinted: '1'
* },
* mutableData: {
* tokenIcon: 'https://bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm.ipfs.w3s.link/whale-night-sky-01.png',
* fullSizedUrl: '',
* about: 'This NFT was created using the PSF Token Studio at https://nft-creator.fullstack.cash',
* userData: ''
* },
* immutableData: {
* issuer: 'http://psfoundation.cash',
* website: 'https://nft-creator.fullstack.cash',
* dateCreated: '9/12/2022, 5:17:38 PM',
* userData: '{\n' +
* ' "title": "CTAIA006 - AI Art by Chris Troutner",\n' +
* ' "about": "AI generated art. Generated from DALL-E at https://labs.openai.com",\n' +
* ' "prompt": "whale swimming through a sky full of stars",\n' +
* ' "algorithm": "DALL-E (stable diffusion)",\n' +
* ' "set": "1-of-2"\n' +
* '}'
* },
* tokenIcon: 'https://bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm.ipfs.w3s.link/whale-night-sky-01.png',
* fullSizedUrl: '',
* optimizedTokenIcon: 'https://p2wdb-gateway-678.fullstack.cash/ipfs/bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm/whale-night-sky-01.png',
* optimizedFullSizedUrl: '',
* iconRepoCompatible: false,
* ps002Compatible: true
* }
*
*/
async getTokenData2 (tokenId, updateCache = false) {
try {
const url = `${this.restURL}psf/slp/token/data2`
// console.log(`url: ${url}`)
// Handle single address.
if (typeof tokenId === 'string') {
const response = await axios.post(
// 'https://bchn.fullstack.cash/v5/psf/slp/token/data/',
url,
{ tokenId, updateCache },
this.axiosOptions
)
return response.data
}
throw new Error('Input tokenId must be a string.')
} catch (error) {
// console.log('error: ', error)
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = PsfSlpIndexer
+1
View File
@@ -407,6 +407,7 @@ class RawTransactions {
// pass back the raw transaction data.
/* exit quietly */
}
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
return txDetails
} catch (error) {
+1 -1
View File
@@ -147,7 +147,7 @@ class Nft1 {
// Loop through the tokenUtxos array and find the minting baton.
let mintBatonUtxo
for (let i = 0; i < tokenUtxos.length; i++) {
if (tokenUtxos[i].utxoType === 'minting-baton') {
if (tokenUtxos[i].utxoType === 'minting-baton' || tokenUtxos[i].type === 'baton') {
mintBatonUtxo = tokenUtxos[i]
}
}
+81 -3
View File
@@ -10,6 +10,7 @@ const Electrumx = require('./electrumx')
const Slp = require('./slp/slp')
const PsfSlpIndexer = require('./psf-slp-indexer')
const BigNumber = require('bignumber.js')
const Blockchain = require('./blockchain')
class UTXO {
constructor (config = {}) {
@@ -18,6 +19,7 @@ class UTXO {
this.slp = new Slp(config)
this.psfSlpIndexer = new PsfSlpIndexer(config)
this.BigNumber = BigNumber
this.blockchain = new Blockchain(config)
}
/**
@@ -80,7 +82,10 @@ class UTXO {
}
// Ensure the address is a BCH address.
const addr = this.slp.Address.toCashAddress(address)
let addr = address
if (!addr.includes('ecash')) {
addr = this.slp.Address.toCashAddress(address)
}
// Get the UTXOs associated with the address.
const utxoData = await this.electrumx.utxo(addr)
@@ -92,7 +97,7 @@ class UTXO {
// Get SLP UTXOs from the psf-slp-indexer
try {
const slpUtxoData = await this.psfSlpIndexer.balance(addr)
console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
// console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
slpUtxos = slpUtxoData.balance.utxos
} catch (err) {
@@ -104,6 +109,11 @@ class UTXO {
}
}
// Get the sync status of the SLP indexer
const syncStatus = await this.psfSlpIndexer.status()
const slpIndexerHeight = syncStatus.status.syncedBlockHeight
const chainBlockHeight = syncStatus.status.chainBlockHeight
// Loop through the Fulcrum UTXOs.
for (let i = 0; i < utxos.length; i++) {
const thisUtxo = utxos[i]
@@ -147,6 +157,15 @@ class UTXO {
thisUtxo.isSlp = false
}
// console.log(`thisUtxo.isSlp: ${thisUtxo.isSlp}`)
// If the SLP indexer more than 1 block behind the full node, then
// move any BCH UTXOs of 600 sats or less into the null array. This
// protects token UTXOs from being burned accidentally.
if (slpIndexerHeight < chainBlockHeight - 1) {
if (thisUtxo.value < 601) {
thisUtxo.isSlp = null
}
}
}
}
@@ -158,8 +177,15 @@ class UTXO {
// Hydrate the UTXOs with additional token data.
type1TokenUtxos = await this.hydrateTokenData(type1TokenUtxos)
// Collect BCH UTXOs above 1000 sats
const bchUtxos = utxos.filter(x => x.isSlp === false && x.value > 1000)
// Collect all BCH UTXOs at 1000 sats or smaller
// The prevents SLP tokens, BCMR, and other 'colored' UTXOs from being
// accidentally included into normal BCH transactions.
const infoUtxos = utxos.filter(x => x.isSlp === false && x.value <= 1000)
// Collect and hydrate any type1 baton UTXOs
const bchUtxos = utxos.filter(x => x.isSlp === false)
let type1BatonUtxos = utxos.filter(
x => x.isSlp === true && x.type === 'baton' && x.tokenType === 1
)
@@ -189,6 +215,7 @@ class UTXO {
const outObj = {
address: addr,
bchUtxos,
infoUtxos,
slpUtxos: {
type1: {
tokens: type1TokenUtxos,
@@ -336,6 +363,57 @@ class UTXO {
return utxos[largestIndex]
}
/**
* @api Utxo.isValid() isValid()
* @apiName isValid
* @apiGroup UTXO
* @apiDescription Validate that UTXO exists and is still spendable.
*
* Given a UTXO, this method will return true if the UTXO is still in the
* mempool and still valid for spending. It will return false if the UTXO
* has been spent.
*
* @apiExample Example usage:
* (async () => {
* try {
* const utxos = await bchjs.Utxo.get('bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z');
* const isValid = bchjs.Utxo.isValid(utxos.bchUtxos[0])
* console.log(isValid);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* true
*/
async isValid (utxo) {
try {
// console.log('utxo: ', utxo)
// Convert different properties from different indexers
const txid = utxo.txid || utxo.tx_hash
const vout = utxo.vout | utxo.tx_pos
// Query the full node
const txOut = await this.blockchain.getTxOut(txid, vout, true)
// console.log('txOut: ', txOut)
// Simplify results to either true or false.
let isValid = null
if (txOut === null) {
isValid = false
} else {
isValid = true
}
return isValid
} catch (err) {
console.log('Error in Utxo.isValid()')
throw err
}
}
}
module.exports = UTXO
+27
View File
@@ -0,0 +1,27 @@
/*
This test is used to interrogate the behavior of bch-js when a psf-slp-indexer
panicks and starts indexing from SLP genesis. In this corner-case, bch-js
should detect the indexer is out of sync, and protect any token UTXOs by
moving any UTXO under 600 sats into the null array.
TO RUN THIS TEST:
- Reset a local instance of psf-slp-indexer to sync from genesis.
- Start a local copy of bch-api
*/
const BCHJS = require('../../../src/bch-js.js')
const bchjs = new BCHJS({
restURL: 'http://localhost:3000/v5/'
})
async function startTest () {
try {
const addr = 'bitcoincash:qzkvas58zag9tjry693mflkjze2m20ps7vx7uw7z9d'
const result = await bchjs.Utxo.get(addr)
console.log(`result: ${JSON.stringify(result, null, 2)}`)
} catch (err) {
console.error('Error in startTest(): ', err)
}
}
startTest()
+3 -1
View File
@@ -318,7 +318,9 @@ describe('#blockchain', () => {
it('should get info about the blockchain', async () => {
const result = await bchjs.Blockchain.getBlockchainInfo()
console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
// console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'blocks')
})
})
})
@@ -0,0 +1,38 @@
/*
Integration tests for the psf-slp-indexer.js library, specific to the eCash
blockchain.
*/
// Global npm libraries
const assert = require('chai').assert
// Local libraries
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('#balance', () => {
it('should get token balance for an ecash address', async () => {
const addr = 'ecash:qr5c4hfy52zn87484cucvzle5pljz0gtr5vhtw9z09'
const result = await bchjs.PsfSlpIndexer.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result.balance, 'utxos')
assert.property(result.balance, 'txs')
assert.property(result.balance, 'balances')
})
})
})
// Promise-based sleep function
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+12 -14
View File
@@ -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,25 +14,23 @@ 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'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const addr = 'ecash:qr5c4hfy52zn87484cucvzle5pljz0gtr5vhtw9z09'
// const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
// assert.isArray(result)
assert.property(result, 'address')
assert.property(result, 'bchUtxos')
assert.property(result, 'nullUtxos')
assert.property(result, 'slpUtxos')
assert.isArray(result.bchUtxos)
assert.isArray(result.nullUtxos)
})
})
*/
})
function sleep (ms) {
@@ -1,9 +1,12 @@
/*
Integration tests for the psf-slp-indexer.js library
Integration tests for the psf-slp-indexer.js library, specific to the BCH
blockchain.
*/
// Global npm libraries
const assert = require('chai').assert
// Local libraries
const BCHJS = require('../../../../src/bch-js')
let bchjs
@@ -124,6 +127,57 @@ describe('#psf-slp-indexer', () => {
}
})
})
describe('#getTokenData', () => {
it('should get token data', async () => {
const tokenId =
'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b'
// bchjs.PsfSlpIndexer.restURL = 'http://localhost:3000/v5/'
const result = await bchjs.PsfSlpIndexer.getTokenData(tokenId)
// console.log('result: ', result)
assert.property(result, 'genesisData')
assert.property(result, 'immutableData')
assert.property(result, 'mutableData')
assert.isObject(result.genesisData)
assert.isString(result.immutableData)
assert.isString(result.mutableData)
})
it('should get token data with a transaction history', async () => {
const tokenId = '43eddfb11c9941edffb8c8815574bb0a43969a7b1de39ad14cd043eaa24fd38d'
const result = await bchjs.PsfSlpIndexer.getTokenData(tokenId, true)
// console.log('result: ', result)
assert.isArray(result.genesisData.txs)
})
})
// This test is commented out because it can not succeed in the BVT without
// tripping rate limits.
// describe('#getTokenData2', () => {
// it('should get token data', async () => {
// const tokenId =
// 'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b'
//
// // bchjs.PsfSlpIndexer.restURL = 'http://localhost:3000/v5/'
// const result = await bchjs.PsfSlpIndexer.getTokenData2(tokenId)
// // console.log('result: ', result)
//
// assert.property(result, 'tokenStats')
// assert.property(result, 'mutableData')
// assert.property(result, 'immutableData')
// assert.property(result, 'tokenIcon')
// assert.property(result, 'fullSizedUrl')
// assert.property(result, 'optimizedTokenIcon')
// assert.property(result, 'optimizedFullSizedUrl')
// assert.property(result, 'iconRepoCompatible')
// assert.property(result, 'ps002Compatible')
// })
// })
})
// Promise-based sleep function
@@ -84,10 +84,10 @@ describe('#UTXO', () => {
})
it('should handle Type1 minting batons', async () => {
const addr = 'simpleledger:qz5l5yzz9r09hw9aadcz53elp2knx6gyg5qk3s8md7'
const addr = 'simpleledger:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpv4awvv7m7'
const result = await bchjs.Utxo.get(addr)
console.log(`result: ${JSON.stringify(result, null, 2)}`)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that minting batons are correctly identified.
assert.isAbove(result.slpUtxos.type1.mintBatons.length, 0)
@@ -104,7 +104,7 @@ describe('#UTXO', () => {
})
it('should filter Group tokens and mint batons', async () => {
const addr = 'bitcoincash:qzeqcrpe5fcnslv8rfqjq4gh4gzdwytmdc4qmh0ztv'
const addr = 'bitcoincash:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpvex9he79q'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -113,15 +113,73 @@ describe('#UTXO', () => {
assert.isAbove(result.slpUtxos.group.mintBatons.length, 0)
})
// it('should filter NFTs', async () => {
// const addr = 'bitcoincash:qq7vp2kvejsql898a2760kuq6xz00h0a5vs4h72ysz'
//
// const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// // assert.isAbove(result.slpUtxos.group.tokens.length, 0)
// // assert.isAbove(result.slpUtxos.group.mintBatons.length, 0)
// })
it('should filter NFTs', async () => {
const addr = 'bitcoincash:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpvex9he79q'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(result.slpUtxos.nft.tokens.length, 0)
})
})
describe('#isValid', () => {
it('should return true for valid UTXO with fullnode properties', async () => {
const utxo = {
txid: '260d4fb4006a330660c805327c840d569d7547b7e3e9659fb423b3a041c2e254',
vout: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result)
assert.equal(result, true)
})
it('should return true for valid UTXO with fulcrum properties', async () => {
const utxo = {
tx_hash: '260d4fb4006a330660c805327c840d569d7547b7e3e9659fb423b3a041c2e254',
tx_pos: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result)
assert.equal(result, true)
})
it('should return true for valid UTXO with fullnode properties', async () => {
const utxo = {
txid: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
vout: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result)
assert.equal(result, false)
})
it('should return true for valid UTXO with fulcrum properties', async () => {
const utxo = {
tx_hash: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
tx_pos: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result
assert.equal(result, false)
})
it('should process output of Utxo.get()', async () => {
const utxo = await bchjs.Utxo.get('bitcoincash:qqgahqa5zkknmhmvsc98jndpwn3r77gqgc02x03jce')
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
const result = await bchjs.Utxo.isValid(utxo.bchUtxos[0])
assert.equal(result, true)
})
})
})
+1 -1
View File
@@ -15,7 +15,7 @@ describe('#control', () => {
describe('#getNetworkInfo', () => {
it('should get info on the full node', async () => {
const result = await bchjs.Control.getNetworkInfo()
console.log(`result: ${JSON.stringify(result, null, 2)}`)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'version')
})
+6
View File
@@ -371,8 +371,13 @@ describe('#ElectrumX', () => {
})
})
/*
CT 3/14/23 - This test is frequently failing in BVT due to 429 errors.
describe('#sortAllTxs', () => {
it('should GET transaction history for a single address', async () => {
// Add delay for this endpoint.
await sleep(6000)
const addr = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
const txs = await bchjs.Electrumx.transactions(addr)
@@ -386,6 +391,7 @@ describe('#ElectrumX', () => {
assert.isAbove(sortedTransactions[0].height, sortedTransactions[1].height)
})
})
*/
})
function sleep (ms) {
+12 -3
View File
@@ -1,7 +1,7 @@
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('#Encryption', () => {
beforeEach(async () => {
@@ -9,7 +9,12 @@ describe('#Encryption', () => {
})
describe('#getPubKey', () => {
// Commenting out these tests since they are failing in BVT due to 429 errors
/*
it('should get a public key', async () => {
// Add delay for this endpoint.
await sleep(8000)
const addr = 'bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq'
const result = await bchjs.encryption.getPubKey(addr)
@@ -21,6 +26,9 @@ describe('#Encryption', () => {
})
it('should report when public key can not be found', async () => {
// Add delay for this endpoint.
await sleep(8000)
const addr = 'bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh'
const result = await bchjs.encryption.getPubKey(addr)
@@ -31,6 +39,7 @@ describe('#Encryption', () => {
assert.property(result, 'publicKey')
assert.equal(result.publicKey, 'not found')
})
*/
})
})
+9 -8
View File
@@ -7,14 +7,14 @@ describe('#price', () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#current', () => {
describe('#single currency', () => {
it('should get current price for single currency', async () => {
const result = await bchjs.Price.current('usd')
assert.notEqual(0, result)
})
})
})
// describe('#current', () => {
// describe('#single currency', () => {
// it('should get current price for single currency', async () => {
// const result = await bchjs.Price.current('usd')
// assert.notEqual(0, result)
// })
// })
// })
describe('#getUsd', () => {
it('should get the USD price of BCH', async () => {
@@ -33,6 +33,7 @@ describe('#price', () => {
assert.isNumber(result)
})
})
describe('#getBchUsd', () => {
it('should get the USD price of BCH', async () => {
const result = await bchjs.Price.getBchUsd()
+3 -1
View File
@@ -204,7 +204,9 @@ describe('#rawtransaction', () => {
const result = await bchjs.RawTransactions.decodeScript(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['asm', 'type', 'p2sh'])
assert.property(result, 'asm')
assert.property(result, 'type')
assert.property(result, 'p2sh')
})
// CT 2/20/19 - Waiting for this PR to be merged complete the test:
+40
View File
@@ -935,4 +935,44 @@ describe('#address.js', () => {
})
})
})
describe('#toEcashAddress', () => {
it('should convert a BCH address to an eCash address', () => {
const cashAddr = 'bitcoincash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s7yw984vk'
const result = bchjs.Address.toEcashAddress(cashAddr)
// console.log('result: ', result)
assert.equal(result, 'ecash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s8f6wu02p')
})
it('should convert a BCH address to an eCash address without a prefix', () => {
const cashAddr = 'bitcoincash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s7yw984vk'
const result = bchjs.Address.toEcashAddress(cashAddr, false)
// console.log('result: ', result)
assert.equal(result, 'qr24z7q6s26d9wr078lr2pxsmxg22cyn9s8f6wu02p')
})
})
describe('#toEtokenAddress', () => {
it('should convert a BCH address to an eToken address', () => {
const cashAddr = 'bitcoincash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s7yw984vk'
const result = bchjs.Address.toEtokenAddress(cashAddr)
// console.log('result: ', result)
assert.equal(result, 'etoken:qr24z7q6s26d9wr078lr2pxsmxg22cyn9sfhnv2gwk')
})
it('should convert a BCH address to an eToken address without a prefix', () => {
const cashAddr = 'bitcoincash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s7yw984vk'
const result = bchjs.Address.toEtokenAddress(cashAddr, false)
// console.log('result: ', result)
assert.equal(result, 'qr24z7q6s26d9wr078lr2pxsmxg22cyn9sfhnv2gwk')
})
})
})
+62 -1
View File
@@ -144,10 +144,71 @@ const tokenData01 = {
qty: {}
}
const tokenData = {
genesisData: tokenStats.tokenData,
immutableData: 'ipfs://bafybeidzfi3sictag4noy746gniyiwwcgio565xuzelmjfwxnga6qyiyai',
mutableData: 'ipfs://bafybeie6t5uyupddc7azms737xg4hxrj7i5t5ov3lb5g2qeehaujj6ak64'
}
const tokenMedia01 = {
tokenStats: {
type: 65,
ticker: 'TEST02',
name: 'How to Send BCH and Tokens',
tokenId: 'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b',
documentUri: 'ipfs://bafybeigjujbb55wqr5lns7z7vleg5cfp4yjwqp4swisxpgxl4xsp5tngai',
documentHash: '845f803d8dd3c2b6be94a6cf3a445b54c36f2e01bd5c9ab4c23f37dbd531489e',
decimals: 0,
mintBatonIsActive: false,
tokensInCirculationBN: '0',
tokensInCirculationStr: '0',
blockCreated: 740089,
totalBurned: '1',
totalMinted: '1',
parentGroupId: 'c9c425f2c6352697c6665a53e035cbad8a44c4b1e36491a1838dc4655479aa09'
},
mutableData: {
updated: '2022-05-14T15:27:44.429Z',
tokenIcon: 'https://bafybeiefg3nd5iognbqztkpi5hj34dmwkqfe7v7xeayn2nhhatinmjmzcy.ipfs.dweb.link/send-bch-token-icon.png',
tokenInfo: 'https://token.fullstack.cash/?tokenid=d9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b',
description: 'This is an NFT representing a video. This is a test token.',
issuer: 'Chris Troutner',
forSale: false,
display: true,
currentOwner: {
bchAddr: 'bitcoincash:qqy7jcrm3vmtqs96r878hy2kx90mn84f25ujqw9z5h',
name: 'Chris Troutner',
contactEmail: 'chris.troutner@gmail.com'
},
content: {
youtube: 'https://youtu.be/WZRwkLPtkaI',
rumble: 'https://rumble.com/v14n00h-how-to-send-bch-and-tokens.html',
odysee: 'https://odysee.com/@trout:5/how-to-send-bch-tokens:0',
lbry: 'lbry://@trout#5/how-to-send-bch-tokens#0',
ipfs: 'bafybeigf3ky5i6fyxwk5bjmtsr6urqmlx4zq2lqathgguytey67iinh4be',
filecoin: 'https://bafybeigf3ky5i6fyxwk5bjmtsr6urqmlx4zq2lqathgguytey67iinh4be.ipfs.dweb.link/send-bch-2022-02-20_08.22.45.mp4'
}
},
immutableData: {
creationDate: '2022-05-13T21:32:11.619Z',
issuer: 'Chris Troutner',
license: 'https://bafybeidnkhjfsbihp4gquwqrs6y35jfpcriafymceszwvkundjkwk546pi.ipfs.dweb.link/copyright.txt',
name: 'How to Send BCH and Tokens'
},
tokenIcon: 'https://bafybeiefg3nd5iognbqztkpi5hj34dmwkqfe7v7xeayn2nhhatinmjmzcy.ipfs.dweb.link/send-bch-token-icon.png',
fullSizedUrl: '',
optimizedTokenIcon: 'https://p2wdb-gateway-678.fullstack.cash/ipfs/bafybeiefg3nd5iognbqztkpi5hj34dmwkqfe7v7xeayn2nhhatinmjmzcy/send-bch-token-icon.png',
optimizedFullSizedUrl: '',
iconRepoCompatible: false,
ps002Compatible: true
}
module.exports = {
tokenStats,
txData,
balance,
status,
tokenData01
tokenData01,
tokenData,
tokenMedia01
}
+12 -12
View File
@@ -19,18 +19,18 @@ describe('#price', () => {
afterEach(() => sandbox.restore())
describe('#current', () => {
it('should get current price for single currency', async () => {
sandbox
.stub(bchjs.Price.axios, 'get')
.resolves({ data: { price: 24905 } })
const result = await bchjs.Price.current('usd')
// console.log(result)
assert.isNumber(result)
})
})
// describe('#current', () => {
// it('should get current price for single currency', async () => {
// sandbox
// .stub(bchjs.Price.axios, 'get')
// .resolves({ data: { price: 24905 } })
//
// const result = await bchjs.Price.current('usd')
// // console.log(result)
//
// assert.isNumber(result)
// })
// })
describe('#getUsd', () => {
it('should get the USD price of BCH', async () => {
+99
View File
@@ -336,4 +336,103 @@ describe('#PsfSlpIndexer', () => {
assert.equal(result, false)
})
})
describe('#getTokenData', () => {
it('should GET token data', async () => {
// Stub the network call.
sandbox.stub(axios, 'post').resolves({ data: mockData.tokenData })
const tokenId =
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
const result = await bchjs.PsfSlpIndexer.getTokenData(tokenId)
assert.property(result, 'genesisData')
assert.property(result, 'immutableData')
assert.property(result, 'mutableData')
assert.isObject(result.genesisData)
assert.isString(result.immutableData)
assert.isString(result.mutableData)
assert.property(result.genesisData, 'type')
assert.property(result.genesisData, 'ticker')
assert.property(result.genesisData, 'name')
assert.property(result.genesisData, 'tokenId')
assert.property(result.genesisData, 'documentUri')
assert.property(result.genesisData, 'documentHash')
assert.property(result.genesisData, 'decimals')
assert.property(result.genesisData, 'mintBatonIsActive')
assert.property(result.genesisData, 'tokensInCirculationBN')
assert.property(result.genesisData, 'tokensInCirculationStr')
assert.property(result.genesisData, 'blockCreated')
assert.property(result.genesisData, 'totalBurned')
assert.property(result.genesisData, 'totalMinted')
})
it('should throw an error for improper input', async () => {
try {
const tokenId = 12345
await bchjs.PsfSlpIndexer.getTokenData(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.getTokenData(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.getTokenData(tokenId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.equal(err.status, 422)
}
})
})
describe('#getTokenData2', () => {
it('should get token data', async () => {
// Stub the network call.
sandbox.stub(axios, 'post').resolves({ data: mockData.tokenMedia01 })
const tokenId =
'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b'
// bchjs.PsfSlpIndexer.restURL = 'http://localhost:3000/v5/'
const result = await bchjs.PsfSlpIndexer.getTokenData2(tokenId)
// console.log('result: ', result)
assert.property(result, 'tokenStats')
assert.property(result, 'mutableData')
assert.property(result, 'immutableData')
assert.property(result, 'tokenIcon')
assert.property(result, 'fullSizedUrl')
assert.property(result, 'optimizedTokenIcon')
assert.property(result, 'optimizedFullSizedUrl')
assert.property(result, 'iconRepoCompatible')
assert.property(result, 'ps002Compatible')
})
})
})
+38 -1
View File
@@ -121,6 +121,8 @@ describe('#utxo', () => {
sandbox
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
.resolves({ txData: { isValidSlp: false } })
sandbox.stub(bchjs.Utxo.psfSlpIndexer, 'status')
.resolves({ status: { syncedBlockHeight: 600000, chainBlockHeight: 600000 } })
// Mock function to return the same input. Good enough for this test.
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x)
@@ -140,7 +142,8 @@ describe('#utxo', () => {
assert.property(result.slpUtxos, 'nft')
assert.property(result, 'nullUtxos')
assert.equal(result.bchUtxos.length, 4)
assert.equal(result.bchUtxos.length, 1)
assert.equal(result.infoUtxos.length, 3)
assert.equal(result.slpUtxos.type1.tokens.length, 1)
assert.equal(result.slpUtxos.type1.mintBatons.length, 1)
assert.equal(result.nullUtxos.length, 0)
@@ -154,6 +157,8 @@ describe('#utxo', () => {
sandbox
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
.resolves({ txData: { isValidSlp: false } })
sandbox.stub(bchjs.Utxo.psfSlpIndexer, 'status')
.resolves({ status: { syncedBlockHeight: 600000, chainBlockHeight: 600000 } })
// Force psf-slp-indexer to return no UTXOs
sandbox
@@ -174,4 +179,36 @@ describe('#utxo', () => {
assert.equal(result.nullUtxos.length, 0)
})
})
describe('#isValid', () => {
it('should return false if getTxOut() returns null', async () => {
// Mock dependencies
sandbox.stub(bchjs.Utxo.blockchain, 'getTxOut').resolves(null)
const utxo = {
tx_hash: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
tx_pos: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result
assert.equal(result, false)
})
it('should return true if getTxOut() returns non-null output', async () => {
// Mock dependencies
sandbox.stub(bchjs.Utxo.blockchain, 'getTxOut').resolves({ a: 'b' })
const utxo = {
tx_hash: 'b94e1ff82eb5781f98296f0af2488ff06202f12ee92b0175963b8dba688d1b40',
tx_pos: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result
assert.equal(result, true)
})
})
})