Compare commits

...
68 Commits
Author SHA1 Message Date
Chris Troutner 13c69593b2 Merge pull request #271 from Permissionless-Software-Foundation/ct-unstable
fix(llm): Adding LLM summary of library
2025-11-06 08:54:19 -08:00
Chris Troutner 8316c4eabb fix(llm): Adding LLM summary of library 2025-11-06 08:52:32 -08:00
Chris Troutner 30cf010e80 Merge pull request #270 from Permissionless-Software-Foundation/ct-unstable
fix(axios): Updating axios
2025-10-07 17:42:39 -07:00
Chris Troutner f28eeea5b0 fix(axios): Updating axios 2025-10-07 17:41:12 -07:00
Chris Troutner 9758eb9995 Merge pull request #269 from Permissionless-Software-Foundation/ct-unstable
fix(utxo.get()): Handling corner-case when SLP indexer is not returni…
2025-06-29 14:38:18 -07:00
Chris Troutner 4260a74d91 fix(utxo.get()): Handling corner-case when SLP indexer is not returning data 2025-06-29 14:36:44 -07:00
Chris Troutner 01625abb37 Merge pull request #267 from Permissionless-Software-Foundation/ct-unstable
feat(getPsffppPrice): New endpoint to get PSFFPP pin price
2024-09-03 19:29:59 -07:00
Chris Troutner b34040cd08 feat(getPsffppPrice): New endpoint to get PSFFPP pin price 2024-09-03 19:28:22 -07:00
Chris Troutner 01a4d0f1aa Adding integration-test-pearson.sh to gitignore 2023-06-02 15:03:25 -07:00
Chris Troutner 7c7c6395e1 Merge pull request #265 from Permissionless-Software-Foundation/ct-unstable
fix(nft): Removing NFT parent and child functions from SLPDB that hav…
2023-06-02 06:27:00 -07:00
Chris Troutner 3408a8fd3b fix(nft): Removing NFT parent and child functions from SLPDB that have been replaced with psf-slp-indexer 2023-06-02 06:24:57 -07:00
Chris Troutner 86a1cadfac Merge pull request #264 from Permissionless-Software-Foundation/ct-unstable
fix(Utxo.get()): Updating documentation
2023-04-09 05:57:08 -07:00
Chris Troutner 7000a83a37 fix(Utxo.get()): Updating documentation 2023-04-09 05:54:22 -07:00
Chris Troutner b331450473 Merge pull request #263 from Permissionless-Software-Foundation/ct-unstable
fix(getPubKey): Debugging errors with getPubKey()
2023-04-08 12:20:32 -07:00
Chris Troutner af7c8ffa6d fix(getPubKey): Debugging errors with getPubKey() 2023-04-08 12:17:40 -07:00
Chris Troutner 2e04548ade Merge pull request #262 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Fixing integration tests that broke with infoUtxo impleme…
2023-04-01 16:38:05 -07:00
Chris Troutner 412fa5e1ce fix(tests): Fixing integration tests that broke with infoUtxo implementation 2023-04-01 16:36:07 -07:00
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
30 changed files with 19672 additions and 9826 deletions
+1
View File
@@ -5,6 +5,7 @@ wallet.json
integration-test-sweet.sh
integration-test-ss.sh
integration-test-bitfinex.sh
integration-test-pearson.sh
docs/
coverage/
+10451
View File
File diff suppressed because it is too large Load Diff
+8518 -9557
View File
File diff suppressed because it is too large Load Diff
+4 -4
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,9 +40,9 @@
"@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",
"axios": "1.12.2",
"bc-bip68": "1.0.5",
"bchaddrjs-slp": "0.2.5",
"bigi": "1.4.2",
+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
+3
View File
@@ -197,6 +197,9 @@ class Blockchain {
)
return response.data
} catch (error) {
console.log('Error in bch-js/blockchain.js/getBlockCount()')
console.log('blockchain.js restURL: ', this.restURL)
if (error.response && error.response.data) throw error.response.data
else throw error
}
+12 -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
}
@@ -713,6 +720,8 @@ class ElectrumX {
return this.sortConfTxs(modifiedTxs, sortingOrder)
} catch (err) {
console.log('Error in util.js/sort0ConfTxs')
console.log('electrumx.js restURL: ', this.restURL)
throw err
}
}
+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 {
+49 -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()
@@ -230,6 +230,42 @@ class Price {
else throw err
}
}
/**
* @api price.getPsffppPrice() getPsffppPrice()
* @apiName Price getPsffppPrice()
* @apiGroup Price
* @apiDescription Return the cost in PSF tokens to write 1MB of data to the PSFFPP
* Find out more at PSFFPP.com. This is a IPFS pinning service that can pin
* up to 100MB per transaction into its network. The cost is denominated in
* PSF SLP tokens. The endpoint returns the cost to pin 1MB of data to the
* PSFFPP network.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getPsffppPrice();
* console.log(current);
* } catch(err) {
* console.error(err)
* }
*})()
*
* // 0.08335233
*/
async getPsffppPrice () {
try {
const response = await this.axios.get(
`${this.restURL}price/psffpp`,
this.axiosOptions
)
return response.data.writePrice
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
}
module.exports = Price
+98 -2
View File
@@ -194,6 +194,7 @@ class PsfSlpIndexer {
* }
* }
*
*
*/
async tokenStats (tokenId, withTxHistory = false) {
@@ -420,7 +421,7 @@ class PsfSlpIndexer {
* }
*
*/
async getTokenData (tokenId) {
async getTokenData (tokenId, withTxHistory = false) {
try {
const url = `${this.restURL}psf/slp/token/data`
// console.log(`url: ${url}`)
@@ -430,7 +431,7 @@ class PsfSlpIndexer {
const response = await axios.post(
// 'https://bchn.fullstack.cash/v5/psf/slp/token/data/',
url,
{ tokenId },
{ tokenId, withTxHistory },
this.axiosOptions
)
return response.data
@@ -441,6 +442,101 @@ class PsfSlpIndexer {
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 -92
View File
@@ -9,7 +9,7 @@
*/
// Public npm libraries
const axios = require('axios')
// const axios = require('axios')
// Local libraries.
const Address = require('./address')
@@ -419,97 +419,6 @@ class Nft1 {
throw err
}
}
/**
* @api SLP.NFT1.listNFTGroupChildren() listNFTGroupChildren()
* @apiName listNFTGroupChildren
* @apiGroup SLP NFT1
* @apiDescription Return list of NFT children tokens in a NFT Group.
* It's assumed provided groupId parameter is for an NFT Group token (type=129)
*
* Returns an Array with GENESIS transaction IDs of the children tokens.
*
* @apiExample Example usage:
*
* const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
* const children = await bchjs.SLP.Nft1.listNFTGroupChildren(groupId)
*
* children = {
* "nftChildren": [
* "45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9",
* "928ce61fe1006b1325a0ba0dce700bf83986a6f0691ba26e121c9ac035d12a55"
* ]
* }
*/
async listNFTGroupChildren (groupId) {
try {
if (typeof groupId === 'string') {
const response = await axios.get(
`${this.restURL}slp/nftChildren/${groupId}`,
this.axiosOptions
)
return response.data
}
throw new Error('groupId must be a string.')
} catch (error) {
// console.log('Error in listNFTGroupChildren()')
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api SLP.NFT1.parentNFTGroup() parentNFTGroup()
* @apiName parentNFTGroup
* @apiGroup SLP NFT1
* @apiDescription Return parent NFT Group information for a given NFT child token.
* It's assumed provided groupId parameter is for an NFT Child token (type=65)
*
* Returns a JSON with NFT group information.
*
* @apiExample Example usage:
*
* const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
* const group = await bchjs.SLP.Nft1.parentNFTGroup(tokenId)
*
* group = {
* "nftGroup": {
* "decimals": 0,
* "timestamp": "2021-05-03 10:36:01",
* "timestamp_unix": 1620038161,
* "versionType": 129,
* "documentUri": "psfoundation.cash",
* "symbol": "PSF.TEST.GROUP",
* "name": "PSF Test NFT Group",
* "containsBaton": true,
* "id": "68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a",
* "documentHash": null,
* "initialTokenQty": 1000000,
* "blockCreated": 686117,
* "totalMinted": null,
* "totalBurned": null,
* "circulatingSupply": null
* }
* }
*/
async parentNFTGroup (tokenId) {
try {
if (typeof tokenId === 'string') {
const response = await axios.get(
`${this.restURL}slp/nftGroup/${tokenId}`,
this.axiosOptions
)
return response.data
}
throw new Error('tokenId must be a string.')
} catch (error) {
// console.log('Error in parentNFTGroup()')
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = Nft1
+85 -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)
}
/**
@@ -30,6 +32,8 @@ class UTXO {
* properties:
* - address: "" - the address these UTXOs are associated with
* - bchUtxos: [] - UTXOs confirmed to be spendable as normal BCH
* - infoUtxos: [] - UTXOs under of 1000 sats or less that can not be categorized
* as another type of UTXO (like a token).
* - 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
@@ -80,7 +84,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)
@@ -104,6 +111,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]
@@ -141,12 +153,23 @@ class UTXO {
// the indexer.
const txData = await this.psfSlpIndexer.tx(thisUtxo.tx_hash)
// console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
if (txData.txData.isValidSlp === null) {
// txData.txData === undefined if the SLP indexer is syncing.
if (!txData.txData || txData.txData.isValidSlp === null) {
thisUtxo.isSlp = null
} else {
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 +181,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 +219,7 @@ class UTXO {
const outObj = {
address: addr,
bchUtxos,
infoUtxos,
slpUtxos: {
type1: {
tokens: type1TokenUtxos,
@@ -336,6 +367,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
@@ -128,8 +131,9 @@ describe('#psf-slp-indexer', () => {
describe('#getTokenData', () => {
it('should get token data', async () => {
const tokenId =
'f055256b938f1ecfa270459d6f12c7c8c82b66d3263c03d5074445a2b1a498a3'
'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b'
// bchjs.PsfSlpIndexer.restURL = 'http://localhost:3000/v5/'
const result = await bchjs.PsfSlpIndexer.getTokenData(tokenId)
// console.log('result: ', result)
@@ -138,10 +142,42 @@ describe('#psf-slp-indexer', () => {
assert.property(result, 'mutableData')
assert.isObject(result.genesisData)
assert.isObject(result.immutableData)
assert.isObject(result.mutableData)
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
@@ -79,12 +79,13 @@ describe('#UTXO', () => {
assert.property(result, 'nullUtxos')
assert.isAbove(result.bchUtxos.length, 0)
assert.isAbove(result.infoUtxos.length, 0)
assert.isAbove(result.slpUtxos.type1.tokens.length, 0)
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
})
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)}`)
@@ -104,7 +105,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)}`)
@@ -114,7 +115,7 @@ describe('#UTXO', () => {
})
it('should filter NFTs', async () => {
const addr = 'simpleledger:qq7vp2kvejsql898a2760kuq6xz00h0a5vuwu9lywu'
const addr = 'bitcoincash:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpvex9he79q'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -122,6 +123,65 @@ describe('#UTXO', () => {
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)
})
})
})
function sleep (ms) {
+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')
})
*/
})
})
+18 -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()
@@ -51,6 +52,15 @@ describe('#price', () => {
assert.property(result, 'CAD')
})
})
describe('#getPsffppPrice', () => {
it('should get the price of BCH in several currencies', async () => {
const result = await bchjs.Price.getPsffppPrice()
// console.log(result)
assert.isNumber(result)
})
})
})
function sleep (ms) {
+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')
})
})
})
+56 -8
View File
@@ -146,21 +146,69 @@ const tokenData01 = {
const tokenData = {
genesisData: tokenStats.tokenData,
immutableData: {
issuer: 'Launchpad Intellectual Property, Inc.',
website: 'http://launchpadip.com/',
dateCreated: '2022-01-11'
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: {
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'
}
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,
tokenData
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 () => {
+26 -2
View File
@@ -350,8 +350,8 @@ describe('#PsfSlpIndexer', () => {
assert.property(result, 'mutableData')
assert.isObject(result.genesisData)
assert.isObject(result.immutableData)
assert.isObject(result.mutableData)
assert.isString(result.immutableData)
assert.isString(result.mutableData)
assert.property(result.genesisData, 'type')
assert.property(result.genesisData, 'ticker')
@@ -411,4 +411,28 @@ describe('#PsfSlpIndexer', () => {
}
})
})
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')
})
})
})
+2 -93
View File
@@ -4,7 +4,7 @@
const assert = require('chai').assert
const sinon = require('sinon')
const axios = require('axios')
// const axios = require('axios')
// Default to unit tests unless some other value for TEST is passed.
if (!process.env.TEST) process.env.TEST = 'unit'
@@ -14,7 +14,7 @@ const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
// Mock data used for unit tests
const mockData = require('./fixtures/slp/mock-utils')
// const mockData = require('./fixtures/slp/mock-utils')
// Default to unit tests unless some other value for TEST is passed.
if (!process.env.TEST) process.env.TEST = 'unit'
@@ -253,95 +253,4 @@ describe('#SLP NFT1', () => {
assert.isNumber(result.outputs)
})
})
describe('#listNFTGroupChildren', () => {
it('should throw an error for improper input', async () => {
try {
const groupId = 12345
await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(
err.message,
'groupId must be a string'
)
}
})
it('should handle API response error', async () => {
try {
const error = new Error()
error.response = {
error: 'NFT group does not exists'
}
sandbox.stub(axios, 'get').throws(error)
const groupId = 'non-exists'
await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
} catch (err) {
assert.include(
err.response,
{ error: 'NFT group does not exists' }
)
}
})
it('should return array of children GENESIS transactions IDs', async () => {
sandbox.stub(axios, 'get').resolves({ data: mockData.mockNftChildrenList })
const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
const result = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftChildren')
assert.isArray(result.nftChildren)
})
})
describe('#parentNFTGroup', () => {
it('should throw an error for improper input', async () => {
try {
const tokenId = 12345
await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(
err.message,
'tokenId must be a string'
)
}
})
it('should handle API response error', async () => {
try {
const error = new Error()
error.response = {
error: 'NFT child does not exists'
}
sandbox.stub(axios, 'get').throws(error)
const tokenId = 'non-exists'
await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
} catch (err) {
assert.include(
err.response,
{ error: 'NFT child does not exists' }
)
}
})
it('should return parent NFT group information', async () => {
sandbox.stub(axios, 'get').resolves({ data: mockData.mockNftGroup })
const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
const result = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftGroup')
assert.property(result.nftGroup, 'id')
assert.property(result.nftGroup, 'name')
assert.property(result.nftGroup, 'symbol')
assert.property(result.nftGroup, 'documentUri')
assert.property(result.nftGroup, 'versionType')
assert.property(result.nftGroup, 'initialTokenQty')
})
})
})
+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)
})
})
})