Compare commits

...
14 Commits
Author SHA1 Message Date
Chris Troutner 9d0840c2fe Merge pull request #131 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetailsWL): Adding usrObj input variable
2021-03-08 17:37:31 -08:00
Chris Troutner 74dc3e7e1d Merge branch 'master' into ct-unstable 2021-03-08 17:33:25 -08:00
Chris Troutner 171786a470 fix(tokenUtxoDetailsWL): Adding usrObj input variable 2021-03-08 17:32:33 -08:00
Chris Troutner 5cbd685060 Merge pull request #130 from Permissionless-Software-Foundation/dh-bch-price
feat(price): Created getBchUsd() for bch-js
2021-03-05 07:51:32 -08:00
Daniel Gonzalez ef05aff7a3 feat(price): Created getBchUsd() for bch-js 2021-03-04 15:14:46 -04:00
Chris Troutner 3f01fdba7c Merge pull request #129 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Stringifying bch-api object when passing error
2021-03-02 07:49:14 -08:00
Chris Troutner 232747280e Merge branch 'master' into ct-unstable 2021-03-02 07:47:03 -08:00
Chris Troutner e96da1615b fix(hydrateUtxos): Stringifying bch-api object when passing error 2021-03-02 07:46:51 -08:00
Chris Troutner 9538501f01 Merge pull request #128 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Adding better error handler
2021-03-02 07:31:43 -08:00
Chris Troutner 0a4fee5716 fix(hydrateUtxos): Adding better error handler 2021-03-02 07:29:41 -08:00
Chris Troutner cec79be4fa Merge pull request #127 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Adding usrObj input for delaying UTXO processing
2021-03-01 08:21:23 -08:00
Chris Troutner c839f0b891 fix(integration tests): Fixing broken ABC integration test 2021-03-01 08:19:16 -08:00
Chris Troutner 24d629ebe1 fix(hydrateUtxos): Adding usrObj input for delaying UTXO processing 2021-03-01 07:48:08 -08:00
Chris Troutner f729918385 fix(_hydrateUtxo): Adding user-configurable artifical delay 2021-03-01 06:55:35 -08:00
9 changed files with 256 additions and 18111 deletions
+53 -18067
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -17,6 +17,7 @@
"test:integration:local:bchn": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/chains/testnet",
"test:integration:decatur:bchn": "export RESTURL=http://192.168.0.36:3000/v4/ && mocha --timeout 30000 test/integration/",
"test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v4/ && mocha -g '#hydrateUtxosWL' --timeout 30000 test/integration/",
"test:temp": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 -g '#hydrateUtxos' test/integration/",
"test:temp2": "mocha --timeout=30000 -g '#_hydrateUtxo' test/unit/",
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#hydrateUtxos' test/integration/",
+35
View File
@@ -156,6 +156,41 @@ class Price {
else throw err
}
}
/**
* @api price.getBchUsd() getBchUsd()
* @apiName Price getBchUsd()
* @apiGroup Price
* @apiDescription Return current price of BCH in USD.
* This endpoint gets the USD price of BCH from the Coinex API. The price
* comes from bch-api, so it has a better chance of working in Tor.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getBchUsd();
* console.log(current);
* } catch(err) {
* console.err(err)
* }
*})()
*
* // 512.81
*/
async getBchUsd () {
try {
const response = await this.axios.get(
`${this.restURL}price/bchusd`,
_this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.usd
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
}
module.exports = Price
+56 -20
View File
@@ -5,10 +5,8 @@ const axios = require('axios')
const slpParser = require('slp-parser')
const BigNumber = require('bignumber.js')
// const Script = require("../script")
// const scriptLib = new Script()
// const BigNumber = require("bignumber.js")
// Local libraries
const Util = require('../util')
let _this
@@ -39,6 +37,8 @@ class Utils {
_this = this
this.whitelist = []
this.util = new Util(config)
}
/**
@@ -1049,14 +1049,18 @@ class Utils {
* that txid and validity has not been confirmed, or a 429 rate-limit error
* was enountered during the processing of the request.
*
* An optional second input object, `usrObj`, allows the user to inject an
* artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
* a number, the call will delay by that number of milliseconds between
* processing UTXOs.
*
* This is an API-heavy call. If you get a lot of `null` values, then slow down
* the calls by adding artifical delays, or request info on fewer UTXOs at a
* the calls by using the usrObj.utxoDelay property, or request info on fewer
* UTXOs at a
* time. `null` indicates that the UTXO can *not* be safely spent, because
* a judgement as to weather it is a token UTXO has not been made. Spending it
* could burn tokens. It's safest to ignore UTXOs with a value of `null`.
*
* A second, optional input, `usrObj`, is used by bch-api for managing rate
* limits. It can be safely ignored when writing apps using this call.
*
* @apiExample Example usage:
*
@@ -1064,7 +1068,8 @@ class Utils {
* try {
* const utxos = await bchjs.Electrumx.utxo(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
*
* const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
* // Delay 100mS between processing UTXOs, to prevent rate-limit errors.
* const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos, { utxoDelay: 100 })
*
* console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
* } catch (error) {
@@ -1174,8 +1179,20 @@ class Utils {
* but much more performant handling of SLP tokens. Some wallet apps prefer
* the scaling performance over the breadth of supported tokens.
*
* An optional second input object, `usrObj`, allows the user to inject an
* artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
* a number, the call will delay by that number of milliseconds between
* processing UTXOs.
*
* This is an API-heavy call. If you get a lot of `null` values, then slow down
* the calls by using the usrObj.utxoDelay property, or request info on fewer
* UTXOs at a
* time. `null` indicates that the UTXO can *not* be safely spent, because
* a judgement as to weather it is a token UTXO has not been made. Spending it
* could burn tokens. It's safest to ignore UTXOs with a value of `null`.
*
*/
async tokenUtxoDetailsWL (utxos) {
async tokenUtxoDetailsWL (utxos, usrObj = null) {
try {
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
@@ -1211,7 +1228,7 @@ class Utils {
}
// Hydrate each UTXO with data from SLP OP_REUTRNs.
const outAry = await this._hydrateUtxo(utxos)
const outAry = await this._hydrateUtxo(utxos, usrObj)
// console.log(`outAry: ${JSON.stringify(outAry, null, 2)}`)
// *After* each UTXO has been hydrated with SLP data,
@@ -1227,7 +1244,7 @@ class Utils {
// information.
// Validate against the whitelist SLPDB.
const whitelistResult = await this.validateTxid3(utxo.txid)
const whitelistResult = await this.validateTxid3(utxo.txid, usrObj)
// console.log(
// `whitelist-SLPDB for ${txid}: ${JSON.stringify(
// whitelistResult,
@@ -1266,6 +1283,9 @@ class Utils {
// transaction. However, that is a rare occurence since the cache of
// decodeOpReturn() will minimize API calls for this case. This behavior
// could be changed, but right now it's a corner case of a corner case.
//
// If the usrObj has a utxoDelay property, then it will delay the loop for
// each UTXO by that many milliseconds.
async _hydrateUtxo (utxos, usrObj = null) {
try {
const decodeOpReturnCache = {}
@@ -1279,6 +1299,12 @@ class Utils {
for (let i = 0; i < utxos.length; i++) {
const utxo = utxos[i]
// If the user passes in a delay, then wait.
if (usrObj && usrObj.utxoDelay && !isNaN(Number(usrObj.utxoDelay))) {
const delayMs = Number(usrObj.utxoDelay)
await this.util.sleep(delayMs)
}
// Get raw transaction data from the full node and attempt to decode
// the OP_RETURN data.
// If there is no OP_RETURN, mark the UTXO as false.
@@ -1645,11 +1671,17 @@ class Utils {
* or a 429 rate-limit error was enountered during the processing of the
* request.
*
* An optional second input object, `usrObj`, allows the user to inject an
* artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
* a number, the call will delay by that number of milliseconds between
* processing UTXOs.
*
* This is an API-heavy call. If you get a lot of `null` values, then slow down
* the calls or request info on fewer UTXOs at a time.
* `null` indicates that the UTXO can not be safely spent, because a judgement
* as to weather it is a token UTXO has not been made. Spending it could burn
* tokens. It's safest to ignore UTXOs with a value of `null`.
* the calls by using the usrObj.utxoDelay property, or request info on fewer
* UTXOs at a
* time. `null` indicates that the UTXO can *not* be safely spent, because
* a judgement as to weather it is a token UTXO has not been made. Spending it
* could burn tokens. It's safest to ignore UTXOs with a value of `null`.
*
* @apiExample Example usage:
*
@@ -1661,7 +1693,8 @@ class Utils {
* "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk"
* ])
*
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
* // Wait 100mS between processing UTXOs, to prevent rate limit errors.
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos, { utxoDelay: 100 })
*
* console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
* } catch (error) {
@@ -1795,7 +1828,7 @@ class Utils {
*/
// Same as tokenUtxoDetails(), but reduces API calls by having bch-api server
// do the heavy lifting.
async hydrateUtxos (utxos) {
async hydrateUtxos (utxos, usrObj) {
try {
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
@@ -1803,15 +1836,18 @@ class Utils {
const response = await _this.axios.post(
`${this.restURL}slp/hydrateUtxos`,
{
utxos: utxos
utxos: utxos,
usrObj
},
_this.axiosOptions
)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
if (error.response && error.response.data) {
throw new Error(JSON.stringify(error.response.data, null, 2))
}
throw error
}
}
+25
View File
@@ -31,6 +31,31 @@ describe('#SLP', () => {
})
describe('#util', () => {
describe('#decodeOpReturn', () => {
it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [
+8
View File
@@ -33,6 +33,14 @@ describe('#price', () => {
assert.isNumber(result)
})
})
describe('#getBchUsd', () => {
it('should get the USD price of BCH', async () => {
const result = await bchjs.Price.getBchUsd()
console.log(result)
assert.isNumber(result)
})
})
describe('#rates', () => {
it('should get the price of BCH in several currencies', async () => {
+1 -24
View File
@@ -228,29 +228,6 @@ describe('#SLP', () => {
assert.include(err.message, 'amount string size not 8 bytes')
}
})
it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
describe('#tokenUtxoDetails', () => {
@@ -662,7 +639,7 @@ describe('#SLP', () => {
})
})
describe('#hydrateUtxos', () => {
describe('#hydrateUtxos-', () => {
it('should hydrate UTXOs', async () => {
const utxos = [
{
+22
View File
@@ -67,4 +67,26 @@ describe('#price', () => {
assert.isNumber(result)
})
})
describe('#getBchUsd', () => {
it('should get the USD price of BCH', async () => {
sandbox.stub(bchjs.Price.axios, 'get').resolves({ data: { usd: 510.39 } })
const result = await bchjs.Price.getBchUsd()
// console.log(result)
assert.isNumber(result)
})
it('should handle error', async () => {
try {
sandbox.stub(bchjs.Price.axios, 'get').throws(new Error('test error'))
await bchjs.Price.getBchUsd()
// console.log(result)
assert.equal(false, true, 'unexpected error')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
})
+55
View File
@@ -775,6 +775,61 @@ describe('#SLP Utils', () => {
assert.include(err.response.data.error, 'Too many requests')
}
})
it('should add delay if delay is specified', async () => {
// Mock the call to REST API
// Stub the calls to decodeOpReturn.
sandbox
.stub(uut.Utils, 'decodeOpReturn')
.onCall(0)
.resolves({
tokenType: 1,
txType: 'SEND',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['200000000', '99887500000000']
})
.onCall(1)
.resolves({
tokenType: 1,
txType: 'GENESIS',
ticker: 'TOK-CH',
name: 'TokyoCash',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
documentUri: '',
documentHash: '',
decimals: 8,
mintBatonVout: 0,
qty: '2100000000000000'
})
// sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
const utxos = [
{
txid:
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
const usrObj = {
utxoDelay: 100
}
await uut.Utils._hydrateUtxo(utxos, usrObj)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// TODO: This test should realy assert that the test took at least 100mS
// to complete. However, as-is, it exercises the code path, so not
// throwing an error can be considered a pass.
assert.equal(true, true)
})
})
describe('#tokenUtxoDetails', () => {