mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 09:02:01 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8117448fd3 | ||
|
|
c7b51a38ff | ||
|
|
07d490134a | ||
|
|
a8a4eb68a6 | ||
|
|
c05e2f14e2 | ||
|
|
3540d98910 | ||
|
|
d0129c01f3 | ||
|
|
ad40607e5b | ||
|
|
a4f0ee6e42 | ||
|
|
0b70ea108b | ||
|
|
cef9a3cf52 | ||
|
|
c216f6c075 | ||
|
|
b53f6bcfc6 | ||
|
|
cbc834cc1a | ||
|
|
1cb5fe069b | ||
|
|
425e2cbbc1 | ||
|
|
9e92f77523 | ||
|
|
dc85bb4cfe | ||
|
|
92960b6957 | ||
|
|
6f34ea286a | ||
|
|
26f39023c4 | ||
|
|
4a3baca72d | ||
|
|
fe37c3ca43 | ||
|
|
ff4ffab670 | ||
|
|
400e7b31a8 | ||
|
|
360fca0475 | ||
|
|
ef7056ee7a | ||
|
|
d221077786 | ||
|
|
046ab78c6a | ||
|
|
29e90915e7 | ||
|
|
3d85dbfb22 | ||
|
|
2c0fa9dfbd | ||
|
|
6ca1d729dc | ||
|
|
c095af8a29 | ||
|
|
89e4ea1635 | ||
|
|
85a34a8305 | ||
|
|
ef6cfe4409 | ||
|
|
a29d6ed26a | ||
|
|
fa58a0764c | ||
|
|
2c2fbeaa46 | ||
|
|
1e4cd5f99e | ||
|
|
52c1ebdafe | ||
|
|
2e56d66f48 | ||
|
|
9cf6c7e7a7 | ||
|
|
43043672c6 | ||
|
|
4f4b2c864d | ||
|
|
951fea8f9b | ||
|
|
6d75dceffb | ||
|
|
3a491c7aba | ||
|
|
f03c020e4f | ||
|
|
4aeb1148e5 | ||
|
|
f03ee13a1e | ||
|
|
cb98773d6a | ||
|
|
c593d68ee5 | ||
|
|
dc7d3a9339 | ||
|
|
6f4cc0f439 | ||
|
|
7aa0fa865e | ||
|
|
e8b6d7fb36 | ||
|
|
b13ef94ea3 | ||
|
|
912f899f4e | ||
|
|
9063d8da0b | ||
|
|
f4e2bd96f0 | ||
|
|
1c898a4ad3 | ||
|
|
c4db813f76 | ||
|
|
38b96b441f | ||
|
|
c94f78f510 | ||
|
|
6de2b712ec | ||
|
|
6f681fd7b0 | ||
|
|
9a82effb2b | ||
|
|
955a5e428e | ||
|
|
44c3bf44f9 | ||
|
|
31ce84abb0 | ||
|
|
40fa4b6bc1 | ||
|
|
8c5e111d60 | ||
|
|
8965dfc57f | ||
|
|
93904a632f | ||
|
|
851d4c404c | ||
|
|
44bac45c32 | ||
|
|
95ee8054be | ||
|
|
547b28cac8 | ||
|
|
40ba334a41 | ||
|
|
ffd8ccaf2b | ||
|
|
9a16c0c6d6 | ||
|
|
52a891fd78 | ||
|
|
c09df08429 | ||
|
|
4dc88c0af3 | ||
|
|
85caa9556b | ||
|
|
bbdc417eb2 | ||
|
|
cc3bc002e7 | ||
|
|
a75e1ab633 | ||
|
|
8c5f1e2274 | ||
|
|
c30e76c942 | ||
|
|
a575621c50 | ||
|
|
d3a0c5948a | ||
|
|
61bb509195 | ||
|
|
a18e549694 | ||
|
|
7d53f47d8a | ||
|
|
e8e615795d | ||
|
|
5759b096d5 | ||
|
|
5b300f68b2 | ||
|
|
3cce6f9754 | ||
|
|
3da04d9327 | ||
|
|
79091815a2 | ||
|
|
258144d26d | ||
|
|
4c4a45841b | ||
|
|
010afd10a3 | ||
|
|
82b4d2ab9e | ||
|
|
8eb6a21dc2 | ||
|
|
5f2a762d4f | ||
|
|
24e2c78d03 | ||
|
|
eab02fdbc9 | ||
|
|
f7bd9c75db | ||
|
|
2a96f3a4f5 | ||
|
|
3dc4a8ba6f | ||
|
|
65916f2c5c | ||
|
|
18188bfdce | ||
|
|
45acc2b0b7 | ||
|
|
31913c3fb7 | ||
|
|
2311643398 | ||
|
|
7501aa0238 | ||
|
|
92a89a543d | ||
|
|
cdf8171994 | ||
|
|
385baf0afd | ||
|
|
9a9aababd2 | ||
|
|
c228acd28a | ||
|
|
c1c140bdce | ||
|
|
54d9b80f26 | ||
|
|
961a190309 | ||
|
|
2d2a91dfde |
+2
-1
@@ -1,7 +1,8 @@
|
||||
Copyright 2021 Chris Troutner
|
||||
Copyright 2021 Permissionless Software Foundation contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
@@ -33,17 +33,13 @@ Here are two YouTube walk-through videos to help you get started:
|
||||
```
|
||||
const BCHJS = require("@psf/bch-js")
|
||||
let bchjs = new BCHJS() // Defaults to BCHN network.
|
||||
|
||||
// testnet
|
||||
bchjs = new BCHJS({ restURL: 'https://testnet3.fullstack.cash/v4/' })
|
||||
```
|
||||
|
||||
This library is intended to be paired with
|
||||
the [bch-api](https://github.com/Permissionless-Software-Foundation/bch-api) REST API, and the infrastructure provided by [FullStack.cash](https://fullstack.cash). The `restURL` property can be changed to work with different Bitcoin Cash networks:
|
||||
|
||||
- BCHN Mainnet REST API server: https://bchn.fullstack.cash/v4/
|
||||
- ABC Mainnet REST API server: https://abc.fullstack.cash/v4/
|
||||
- Testnet3 REST API server: https://testnet3.fullstack.cash/v4/
|
||||
- BCHN Mainnet REST API server: https://bchn.fullstack.cash/v5/
|
||||
- ABC Mainnet REST API server: https://abc.fullstack.cash/v5/
|
||||
- Check server status: https://metrics.fullstack.cash
|
||||
|
||||
### API Key
|
||||
@@ -57,7 +53,7 @@ rate limits when interacting with the back end server. See [this article](https:
|
||||
```
|
||||
const BCHJS = require("@psf/bch-js")
|
||||
let bchjs = new BCHJS({
|
||||
restURL: 'https://bchn.fullstack.cash/v3/',
|
||||
restURL: 'https://bchn.fullstack.cash/v5/',
|
||||
apiToken: 'eyJhbGciO...' // Your JWT token here.
|
||||
})
|
||||
```
|
||||
@@ -122,3 +118,5 @@ Copies of this repository are also published on [IPFS](https://ipfs.io).
|
||||
## License
|
||||
|
||||
[MIT](LICENSE.md)
|
||||
|
||||
test
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
Examples have been moved to this repository:
|
||||
Examples have been moved to this repository
|
||||
|
||||
https://github.com/Permissionless-Software-Foundation/bch-js-examples
|
||||
|
||||
Generated
+16286
-5819
File diff suppressed because it is too large
Load Diff
+56
-54
@@ -4,24 +4,27 @@
|
||||
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
|
||||
"author": "Chris Troutner <chris.troutner@gmail.com>",
|
||||
"contributors": [
|
||||
"Gabriel Cardona <gabriel@bitcoin.com>"
|
||||
"Gabriel Cardona <gabriel@bitcoin.com>",
|
||||
"Stoyan Zhekov <zh@zhware.net>"
|
||||
],
|
||||
"main": "src/bch-js",
|
||||
"scripts": {
|
||||
"test": "nyc mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
|
||||
"test:integration": "npm run test:integration:bchn",
|
||||
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
|
||||
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/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: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:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/chains/testnet/",
|
||||
"test:integration:local:abc": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
|
||||
"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 --timeout 30000 test/integration/",
|
||||
"test:temp": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
|
||||
"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.0.36:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
||||
"test:integration:decatur:abc": "export RESTURL=http://192.168.0.38: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/",
|
||||
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#Transaction' test/integration/",
|
||||
"test:temp4": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 -g '#decodeOpReturn' test/integration/",
|
||||
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v5/ && mocha --timeout 30000 -g '#DSProof' test/integration/chains/",
|
||||
"test:temp4": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#decodeOpReturn' test/integration/",
|
||||
"coverage": "nyc report --reporter=text-lcov | coveralls",
|
||||
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
|
||||
"docs": "./node_modules/.bin/apidoc -i src/ -o docs",
|
||||
@@ -34,53 +37,52 @@
|
||||
"url": "git+https://github.com/Permissionless-Software-Foundation/bch-js.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@psf/bip21": "^2.0.1",
|
||||
"@psf/bip32-utils": "^1.0.0",
|
||||
"@psf/bitcoincash-ops": "^2.0.0",
|
||||
"@psf/bitcoincashjs-lib": "^4.0.2",
|
||||
"@psf/coininfo": "^4.0.0",
|
||||
"@uppy/core": "^1.10.4",
|
||||
"@uppy/tus": "^1.5.12",
|
||||
"axios": "^0.21.1",
|
||||
"bc-bip68": "^1.0.5",
|
||||
"bchaddrjs-slp": "^0.2.5",
|
||||
"bigi": "^1.4.2",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"bip-schnorr": "^0.3.0",
|
||||
"bip38": "^2.0.2",
|
||||
"bip39": "^3.0.2",
|
||||
"bip66": "^1.1.5",
|
||||
"bitcoinjs-message": "^2.0.0",
|
||||
"bs58": "^4.0.1",
|
||||
"cashaddrjs": "^0.3.3",
|
||||
"ini": "^1.3.8",
|
||||
"randombytes": "^2.0.6",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"satoshi-bitcoin": "^1.0.4",
|
||||
"@psf/bip21": "2.0.1",
|
||||
"@chris.troutner/bip32-utils": "1.0.5",
|
||||
"@psf/bitcoincash-ops": "2.0.0",
|
||||
"@psf/bitcoincashjs-lib": "4.0.2",
|
||||
"@psf/coininfo": "4.0.0",
|
||||
"@uppy/core": "1.10.4",
|
||||
"@uppy/tus": "1.5.12",
|
||||
"axios": "^0.21.4",
|
||||
"bc-bip68": "1.0.5",
|
||||
"bchaddrjs-slp": "0.2.5",
|
||||
"bigi": "1.4.2",
|
||||
"bignumber.js": "9.0.0",
|
||||
"bip-schnorr": "0.3.0",
|
||||
"bip38": "2.0.2",
|
||||
"bip39": "3.0.2",
|
||||
"bip66": "1.1.5",
|
||||
"bitcoinjs-message": "2.0.0",
|
||||
"bs58": "4.0.1",
|
||||
"ecashaddrjs": "1.0.7",
|
||||
"ini": "1.3.8",
|
||||
"randombytes": "2.0.6",
|
||||
"safe-buffer": "5.1.2",
|
||||
"satoshi-bitcoin": "1.0.4",
|
||||
"slp-mdm": "0.0.6",
|
||||
"slp-parser": "0.0.4",
|
||||
"wif": "^2.0.6"
|
||||
"wif": "2.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apidoc": "^0.17.7",
|
||||
"assert": "^2.0.0",
|
||||
"chai": "^4.1.2",
|
||||
"coveralls": "^3.0.2",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-config-prettier": "^6.0.0",
|
||||
"eslint-config-standard": "^14.1.0",
|
||||
"eslint-plugin-node": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"eslint-plugin-standard": "^4.0.0",
|
||||
"husky": "^4.3.6",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"mocha": "^6.1.4",
|
||||
"node-mocks-http": "^1.7.0",
|
||||
"nyc": "^14.1.0",
|
||||
"prettier": "^1.18.2",
|
||||
"semantic-release": "^17.3.9",
|
||||
"sinon": "^7.3.2",
|
||||
"standard": "^16.0.3"
|
||||
"apidoc": "0.17.7",
|
||||
"assert": "2.0.0",
|
||||
"chai": "4.1.2",
|
||||
"coveralls": "3.0.2",
|
||||
"eslint": "7.17.0",
|
||||
"eslint-config-prettier": "7.1.0",
|
||||
"eslint-config-standard": "16.0.3",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-prettier": "3.3.1",
|
||||
"eslint-plugin-standard": "4.0.0",
|
||||
"husky": "^4.3.8",
|
||||
"lodash.clonedeep": "4.5.0",
|
||||
"mocha": "9.1.3",
|
||||
"node-mocks-http": "1.7.0",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "18.0.0",
|
||||
"sinon": "9.2.2",
|
||||
"standard": "^16.0.4"
|
||||
},
|
||||
"apidoc": {
|
||||
"title": "bch-js",
|
||||
|
||||
+78
-5
@@ -1,12 +1,12 @@
|
||||
// const axios = require("axios")
|
||||
const Bitcoin = require('@psf/bitcoincashjs-lib')
|
||||
const cashaddr = require('cashaddrjs')
|
||||
const cashaddr = require('ecashaddrjs')
|
||||
const coininfo = require('@psf/coininfo')
|
||||
|
||||
class Address {
|
||||
constructor (config) {
|
||||
const tmp = {}
|
||||
if (!config || !config.restURL) tmp.restURL = 'https://api.bchjs.cash/v4/'
|
||||
if (!config || !config.restURL) tmp.restURL = 'https://api.bchjs.cash/v5/'
|
||||
else tmp.restURL = config.restURL
|
||||
|
||||
this.restURL = tmp.restURL
|
||||
@@ -129,6 +129,63 @@ class Address {
|
||||
return cashAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.toEcashAddress() toEcashAddress()
|
||||
* @apiName toEcashAddress
|
||||
* @apiGroup Address
|
||||
* @apiDescription Convert legacy to cashAddress format
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // mainnet
|
||||
* bchjs.Address.toEcashAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2')
|
||||
* // ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
|
||||
*
|
||||
* // mainnet no prefix
|
||||
* bchjs.Address.toEcashAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2', false)
|
||||
* // qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
|
||||
*
|
||||
*/
|
||||
toEcashAddress (address, prefix = true) {
|
||||
const decoded = this._decode(address)
|
||||
|
||||
const ecashAddress = cashaddr.encode(
|
||||
'ecash',
|
||||
decoded.type,
|
||||
decoded.hash
|
||||
)
|
||||
|
||||
if (prefix) return ecashAddress
|
||||
return ecashAddress.split(':')[1]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.ecashtoCashAddress() ecashtoCashAddress()
|
||||
* @apiName ecashtoCashAddress
|
||||
* @apiGroup Address
|
||||
* @apiDescription Convert legacy to cashAddress format
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // mainnet
|
||||
* bchjs.Address.ecashtoCashAddress('ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da')
|
||||
* // bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2
|
||||
*
|
||||
* // mainnet no prefix
|
||||
* bchjs.Address.ecashtoCashAddress('ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da', false)
|
||||
* // qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2
|
||||
*/
|
||||
ecashtoCashAddress (address, prefix = true) {
|
||||
const decoded = this._decodeEcashAddress(address)
|
||||
|
||||
const cashAddress = cashaddr.encode(
|
||||
'bitcoincash',
|
||||
decoded.type,
|
||||
decoded.hash
|
||||
)
|
||||
|
||||
if (prefix) return cashAddress
|
||||
return cashAddress.split(':')[1]
|
||||
}
|
||||
|
||||
// Converts any address format to hash160
|
||||
toHash160 (address) {
|
||||
const legacyAddress = this.toLegacyAddress(address)
|
||||
@@ -166,7 +223,7 @@ class Address {
|
||||
* @api Address.hash160ToCash() hash160ToCash()
|
||||
* @apiName hash160ToCash
|
||||
* @apiGroup Address
|
||||
* @apiDescription Convert hash160 to cash address.
|
||||
* @apiDescription Convert hash160 to cash address. Accepts either hexadecimal or buffer.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* bchjs.Address.hash160ToCash("573d93b475be4f1925f3b74ed951201b0147eac1")
|
||||
@@ -271,6 +328,22 @@ class Address {
|
||||
throw new Error(`Invalid format : ${address}`)
|
||||
}
|
||||
|
||||
_decodeEcashAddress (address) {
|
||||
if (address.indexOf(':') !== -1) {
|
||||
const decoded = cashaddr.decode(address)
|
||||
decoded.format = 'cashaddr'
|
||||
return decoded
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = cashaddr.decode(`ecash:${address}`)
|
||||
decoded.format = 'cashaddr'
|
||||
return decoded
|
||||
} catch (error) {}
|
||||
|
||||
throw new Error(`Invalid format : ${address}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.isLegacyAddress() isLegacyAddress()
|
||||
* @apiName isLegacyAddress
|
||||
@@ -721,13 +794,13 @@ class Address {
|
||||
* @apiDescription Detect an addess from an OutputScript..
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* const script = bchjs.Script.encode([
|
||||
* const scriptBuffer = bchjs.Script.encode([
|
||||
* Buffer.from("BOX", "ascii"),
|
||||
* bchjs.Script.opcodes.OP_CAT,
|
||||
* Buffer.from("BITBOX", "ascii"),
|
||||
* bchjs.Script.opcodes.OP_EQUAL
|
||||
* ]);
|
||||
* const p2sh_hash160 = bchjs.Crypto.hash160(script);
|
||||
* const p2sh_hash160 = bchjs.Crypto.hash160(scriptBuffer);
|
||||
* const scriptPubKey = bchjs.Script.scriptHash.output.encode(p2sh_hash160);
|
||||
*
|
||||
* // mainnet address from output script
|
||||
|
||||
+5
-2
@@ -7,8 +7,8 @@
|
||||
*/
|
||||
|
||||
// bch-api mainnet.
|
||||
const DEFAULT_REST_API = 'https://api.fullstack.cash/v4/'
|
||||
// const DEFAULT_REST_API = "http://localhost:3000/v4/"
|
||||
const DEFAULT_REST_API = 'https://api.fullstack.cash/v5/'
|
||||
// const DEFAULT_REST_API = "http://localhost:3000/v5/"
|
||||
|
||||
// local deps
|
||||
const BitcoinCash = require('./bitcoincash')
|
||||
@@ -32,6 +32,7 @@ const IPFS = require('./ipfs')
|
||||
const Encryption = require('./encryption')
|
||||
const Utxo = require('./utxo')
|
||||
const Transaction = require('./transaction')
|
||||
const DSProof = require('./dsproof')
|
||||
|
||||
// Indexers
|
||||
const Ninsight = require('./ninsight')
|
||||
@@ -115,6 +116,8 @@ class BCHJS {
|
||||
this.IPFS = new IPFS()
|
||||
this.Utxo = new Utxo(libConfig)
|
||||
this.Transaction = new Transaction(libConfig)
|
||||
|
||||
this.DSProof = new DSProof(libConfig)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+58
-48
@@ -5,7 +5,7 @@
|
||||
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Blockchain {
|
||||
constructor (config) {
|
||||
@@ -29,7 +29,7 @@ class Blockchain {
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,7 +54,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBestBlockHash`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -68,8 +68,8 @@ class Blockchain {
|
||||
* @apiName getBlock
|
||||
* @apiGroup Blockchain
|
||||
* @apiDescription
|
||||
* If verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is true, returns an Object with information about block hash.
|
||||
*
|
||||
* If verbose is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is 1, returns an Object with information about block hash.
|
||||
* If verbose is 2, returns an Object with information about block hash and information about tx.
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
@@ -80,29 +80,39 @@ class Blockchain {
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // { hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
|
||||
* // confirmations: 528236,
|
||||
* // size: 216,
|
||||
* // height: 1000,
|
||||
* // version: 1,
|
||||
* // versionHex: '00000001',
|
||||
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
* // tx:
|
||||
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
|
||||
* // time: 1232346882,
|
||||
* // mediantime: 1232344831,
|
||||
* // nonce: 2595206198,
|
||||
* // bits: '1d00ffff',
|
||||
* // difficulty: 1,
|
||||
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
|
||||
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
|
||||
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6' }
|
||||
* // {
|
||||
* // hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
|
||||
* // confirmations: 528236,
|
||||
* // size: 216,
|
||||
* // height: 1000,
|
||||
* // version: 1,
|
||||
* // versionHex: '00000001',
|
||||
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
* // tx:
|
||||
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
|
||||
* // time: 1232346882,
|
||||
* // mediantime: 1232344831,
|
||||
* // nonce: 2595206198,
|
||||
* // bits: '1d00ffff',
|
||||
* // difficulty: 1,
|
||||
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
|
||||
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
|
||||
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6'
|
||||
* // }
|
||||
*/
|
||||
async getBlock (blockhash, verbose = true) {
|
||||
async getBlock (blockhash, verbosity = 1) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlock/${blockhash}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
// Input validation
|
||||
if (!blockhash || typeof blockhash !== 'string') {
|
||||
throw new Error('blockhash must be a string')
|
||||
}
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/getblock`,
|
||||
{
|
||||
blockhash,
|
||||
verbosity
|
||||
},
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -152,7 +162,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlockchainInfo`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -183,7 +193,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlockCount`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -216,7 +226,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlockHash/${height}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -263,7 +273,7 @@ class Blockchain {
|
||||
if (typeof hash === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getBlockHeader/${hash}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -277,7 +287,7 @@ class Blockchain {
|
||||
hashes: hash,
|
||||
verbose: verbose
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -324,7 +334,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getChainTips`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -356,7 +366,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getDifficulty`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -372,7 +382,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getMempoolAncestors/${txid}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -387,7 +397,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getMempoolDescendants/${txid}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -484,7 +494,7 @@ class Blockchain {
|
||||
if (typeof txid === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getMempoolEntry/${txid}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -495,7 +505,7 @@ class Blockchain {
|
||||
{
|
||||
txids: txid
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -535,7 +545,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getMempoolInfo`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -582,7 +592,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/getRawMempool?vebose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -626,7 +636,7 @@ class Blockchain {
|
||||
// Send the request to the REST API.
|
||||
// const response = await axios.get(
|
||||
// `${this.restURL}blockchain/getTxOut/${txid}/${n}?includeMempool=${includeMempool}`,
|
||||
// _this.axiosOptions
|
||||
// this.axiosOptions
|
||||
// )
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/getTxOut`,
|
||||
@@ -635,7 +645,7 @@ class Blockchain {
|
||||
vout: n,
|
||||
mempool: includeMempool
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -688,7 +698,7 @@ class Blockchain {
|
||||
const path = `${this.restURL}blockchain/getTxOutProof/${txids}`
|
||||
// if (blockhash) path = `${path}?blockhash=${blockhash}`
|
||||
|
||||
const response = await axios.get(path, _this.axiosOptions)
|
||||
const response = await axios.get(path, this.axiosOptions)
|
||||
return response.data
|
||||
|
||||
// Array of txids.
|
||||
@@ -699,7 +709,7 @@ class Blockchain {
|
||||
{
|
||||
txids: txids
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -716,7 +726,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/preciousBlock/${blockhash}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -729,7 +739,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}blockchain/pruneBlockchain/${height}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -742,7 +752,7 @@ class Blockchain {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -794,7 +804,7 @@ class Blockchain {
|
||||
if (typeof proof === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}blockchain/verifyTxOutProof/${proof}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
@@ -806,7 +816,7 @@ class Blockchain {
|
||||
{
|
||||
proofs: proof
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
|
||||
+4
-4
@@ -4,7 +4,7 @@
|
||||
|
||||
const axios = require('axios')
|
||||
|
||||
let _this // Global reference to the instance of this class.
|
||||
// let _this // Global reference to the instance of this class.
|
||||
|
||||
class Control {
|
||||
constructor (config) {
|
||||
@@ -28,7 +28,7 @@ class Control {
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +81,7 @@ class Control {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}control/getNetworkInfo`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -94,7 +94,7 @@ class Control {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}control/getMemoryInfo`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
|
||||
+3
-3
@@ -22,7 +22,7 @@ class Crypto {
|
||||
* // buffer from hex
|
||||
* let buffer = Buffer.from('03123464075c7a5fa6b8680afa2c962a02e7bf071c6b2395b0ac711d462cac9354', 'hex')
|
||||
* bchjs.Crypto.sha256(buffer)
|
||||
* // <Buffer 26 b0 78 0a 68 3a 1e 09 8e 9c b8 cf a1 b0 92 42 28 25 00 97>
|
||||
* // <Buffer 97 8c 09 dd 46 09 1d 19 22 fa 01 e9 f4 a9 75 b9 1a 37 1f 26 ba 83 99 de 27 d5 38 01 15 21 21 de>
|
||||
*
|
||||
* */
|
||||
// Translate address from any address format into a specific format.
|
||||
@@ -60,7 +60,7 @@ class Crypto {
|
||||
* @api Crypto.hash256() hash256()
|
||||
* @apiName hash256
|
||||
* @apiGroup Crypto
|
||||
* @apiDescription Utility for creating double sha256 hash digests of data.
|
||||
* @apiDescription Utility for creating double sha256 hash digests of buffer encoded data.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // buffer from hex
|
||||
@@ -86,7 +86,7 @@ class Crypto {
|
||||
* @api Crypto.hash160() hash160()
|
||||
* @apiName hash160
|
||||
* @apiGroup Crypto
|
||||
* @apiDescription Utility for creating ripemd160(sha256()) hash digests of data.
|
||||
* @apiDescription Utility for creating ripemd160(sha256()) hash digests of buffer encoded data.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // buffer from hex
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
|
||||
class DSProof {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
this.axios = axios
|
||||
|
||||
if (this.authToken) {
|
||||
// Add Basic Authentication token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: this.authToken
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api DSProof.getDSProof() getDSProof()
|
||||
* @apiName getDSProof
|
||||
* @apiGroup DSProof
|
||||
* @apiDescription Checks if a transaction generated a double-spend proof.
|
||||
*
|
||||
* If a double-spend is attempted, one of the transactions will generate a
|
||||
* 'double spend proof'. This call can be used to check if a transaction
|
||||
* generated such a proof.
|
||||
*
|
||||
* Merchants should wait 3-5 seconds after receiving notification of a
|
||||
* transaction before calling this endpoint, to see if the TXID generated a
|
||||
* proof. If this method returns no data, then the TX can be considered 'safe'
|
||||
* and not a double spend. If proof data is returned by this method, then
|
||||
* the transaction generated a proof and can be considered a 'double spend'.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
|
||||
* const result = await bchjs.DSProof.getDSProof(txid)
|
||||
* console.log(result);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* null
|
||||
*/
|
||||
async getDSProof (txid) {
|
||||
try {
|
||||
if (!txid) {
|
||||
throw new Error('txid is required')
|
||||
}
|
||||
if (txid.length !== 64) {
|
||||
throw new Error(`txid must be of length 64 (not ${txid.length})`)
|
||||
}
|
||||
const response = await _this.axios.get(
|
||||
`${this.restURL}dsproof/getdsproof/${txid}`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DSProof
|
||||
+16
-16
@@ -8,7 +8,7 @@ const axios = require('axios')
|
||||
// Local libraries.
|
||||
const Blockchain = require('./blockchain')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class ElectrumX {
|
||||
constructor (config) {
|
||||
@@ -34,7 +34,7 @@ class ElectrumX {
|
||||
|
||||
this.blockchain = new Blockchain(config)
|
||||
|
||||
_this = this
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +108,7 @@ class ElectrumX {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/utxos/${address}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
@@ -119,7 +119,7 @@ class ElectrumX {
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -142,7 +142,7 @@ class ElectrumX {
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.Electrumx.balance('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf');
|
||||
* console.log(utxo);
|
||||
* console.log(balance);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
@@ -159,7 +159,7 @@ class ElectrumX {
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.Electrumx.balance(['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v']);
|
||||
* console.log(utxo);
|
||||
* console.log(balance);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
@@ -192,7 +192,7 @@ class ElectrumX {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/balance/${address}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
@@ -203,7 +203,7 @@ class ElectrumX {
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -290,7 +290,7 @@ class ElectrumX {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/transactions/${address}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
@@ -302,7 +302,7 @@ class ElectrumX {
|
||||
addresses: address,
|
||||
usrObj // pass user data when making an internal call.
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -383,7 +383,7 @@ class ElectrumX {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/unconfirmed/${address}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
@@ -394,7 +394,7 @@ class ElectrumX {
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -452,7 +452,7 @@ class ElectrumX {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/block/headers/${height}?count=${count}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -543,7 +543,7 @@ class ElectrumX {
|
||||
if (typeof txid === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}electrumx/tx/data/${txid}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} else if (Array.isArray(txid)) {
|
||||
@@ -552,7 +552,7 @@ class ElectrumX {
|
||||
{
|
||||
txids: txid
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -592,7 +592,7 @@ class ElectrumX {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}electrumx/tx/broadcast`,
|
||||
{ txHex },
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
|
||||
+4
-2
@@ -35,11 +35,13 @@ class Encryption {
|
||||
// Search the blockchain for a public key associated with a BCH address.
|
||||
async getPubKey (addr) {
|
||||
try {
|
||||
if (!addr || typeof addr !== 'string') { throw new Error('Input must be a valid Bitcoin Cash address.') }
|
||||
if (!addr || typeof addr !== 'string') {
|
||||
throw new Error('Input must be a valid Bitcoin Cash address.')
|
||||
}
|
||||
|
||||
const response = await _this.axios.get(
|
||||
`${this.restURL}encryption/publickey/${addr}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Generating {
|
||||
constructor (config) {
|
||||
@@ -24,14 +24,14 @@ class Generating {
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
// _this = this
|
||||
}
|
||||
|
||||
async generateToAddress (blocks, address, maxtries = 1000000) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}generating/generateToAddress/${blocks}/${address}?maxtries=${maxtries}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
|
||||
+4
-2
@@ -1,6 +1,6 @@
|
||||
const Bitcoin = require('@psf/bitcoincashjs-lib')
|
||||
const coininfo = require('@psf/coininfo')
|
||||
const bip32utils = require('@psf/bip32-utils')
|
||||
const bip32utils = require('@chris.troutner/bip32-utils')
|
||||
const bchaddrjs = require('bchaddrjs-slp')
|
||||
|
||||
class HDNode {
|
||||
@@ -34,7 +34,9 @@ class HDNode {
|
||||
*/
|
||||
fromSeed (rootSeedBuffer, network = 'mainnet') {
|
||||
let bitcoincash
|
||||
if (network === 'bitcoincash' || network === 'mainnet') { bitcoincash = coininfo.bitcoincash.main } else bitcoincash = coininfo.bitcoincash.test
|
||||
if (network === 'bitcoincash' || network === 'mainnet') {
|
||||
bitcoincash = coininfo.bitcoincash.main
|
||||
} else bitcoincash = coininfo.bitcoincash.test
|
||||
|
||||
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
|
||||
return Bitcoin.HDNode.fromSeedBuffer(
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Mining {
|
||||
constructor (config) {
|
||||
@@ -24,14 +24,14 @@ class Mining {
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
// _this = this
|
||||
}
|
||||
|
||||
async getBlockTemplate (templateRequest) {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}mining/getBlockTemplate/${templateRequest}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -44,7 +44,7 @@ class Mining {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}mining/getMiningInfo`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -57,7 +57,7 @@ class Mining {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}mining/getNetworkHashps?nblocks=${nblocks}&height=${height}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -71,7 +71,7 @@ class Mining {
|
||||
if (parameters) path = `${path}?parameters=${parameters}`
|
||||
|
||||
try {
|
||||
const response = await axios.post(path, _this.axiosOptions)
|
||||
const response = await axios.post(path, this.axiosOptions)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
|
||||
+10
-10
@@ -5,7 +5,7 @@
|
||||
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Ninsight {
|
||||
constructor (config) {
|
||||
@@ -27,7 +27,7 @@ class Ninsight {
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,7 +75,7 @@ class Ninsight {
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
@@ -86,7 +86,7 @@ class Ninsight {
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -143,7 +143,7 @@ class Ninsight {
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -153,7 +153,7 @@ class Ninsight {
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -219,7 +219,7 @@ class Ninsight {
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -229,7 +229,7 @@ class Ninsight {
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -292,7 +292,7 @@ class Ninsight {
|
||||
{
|
||||
txids: [txid]
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -302,7 +302,7 @@ class Ninsight {
|
||||
{
|
||||
txids: txid
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
|
||||
+52
-13
@@ -1,10 +1,10 @@
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Price {
|
||||
constructor (config) {
|
||||
_this = this
|
||||
// _this = this
|
||||
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
@@ -58,7 +58,7 @@ class Price {
|
||||
* let current = await bchjs.Price.getUsd();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.err(err)
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
@@ -68,7 +68,7 @@ class Price {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/usd`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
@@ -94,7 +94,7 @@ class Price {
|
||||
* let current = await bchjs.Price.rates();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.err(err)
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
@@ -111,7 +111,7 @@ class Price {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/rates`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
@@ -127,8 +127,9 @@ class Price {
|
||||
* @apiName Price getBchaUsd()
|
||||
* @apiGroup Price
|
||||
* @apiDescription Return current price of BCHA in USD.
|
||||
* This endpoint gets the USD price of BCHA from the Coinex API. The price
|
||||
* comes from bch-api, so it has a better chance of working in Tor.
|
||||
* This endpoint gets the USD price of XEC from the Coinex API. The price
|
||||
* denominated in BCHA comes from bch-api, so it has a better chance of
|
||||
* working in Tor.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
*(async () => {
|
||||
@@ -136,17 +137,55 @@ class Price {
|
||||
* let current = await bchjs.Price.getBchaUsd();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.err(err)
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
* // 18.81
|
||||
* // 212.34
|
||||
*/
|
||||
async getBchaUsd () {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/bchausd`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
const bchaPrice = response.data.usd * 1000000
|
||||
// Convert XEC denomination to BCHA denomination
|
||||
|
||||
return bchaPrice
|
||||
} catch (err) {
|
||||
if (err.response && err.response.data) throw err.response.data
|
||||
else throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api price.getXecUsd() getXecUsd()
|
||||
* @apiName Price getXecUsd()
|
||||
* @apiGroup Price
|
||||
* @apiDescription Return current price of XEC in USD.
|
||||
* This endpoint gets the USD price of XEC 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.getXecUsd();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
* // 0.00021234
|
||||
*/
|
||||
async getXecUsd () {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/bchausd`,
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
@@ -171,7 +210,7 @@ class Price {
|
||||
* let current = await bchjs.Price.getBchUsd();
|
||||
* console.log(current);
|
||||
* } catch(err) {
|
||||
* console.err(err)
|
||||
* console.error(err)
|
||||
* }
|
||||
*})()
|
||||
*
|
||||
@@ -181,7 +220,7 @@ class Price {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/bchusd`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
|
||||
+51
-21
@@ -1,6 +1,6 @@
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class RawTransactions {
|
||||
constructor (config) {
|
||||
@@ -27,7 +27,7 @@ class RawTransactions {
|
||||
// Encapsulate dependencies
|
||||
this.axios = axios
|
||||
|
||||
_this = this
|
||||
// this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,7 +98,7 @@ class RawTransactions {
|
||||
if (typeof hex === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}rawtransactions/decodeRawTransaction/${hex}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -111,7 +111,7 @@ class RawTransactions {
|
||||
data: {
|
||||
hexes: hex
|
||||
},
|
||||
headers: _this.axiosOptions.headers
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await axios(options)
|
||||
|
||||
@@ -120,7 +120,8 @@ class RawTransactions {
|
||||
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.error) throw new Error(error.error)
|
||||
else if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
@@ -165,7 +166,7 @@ class RawTransactions {
|
||||
if (typeof script === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}rawtransactions/decodeScript/${script}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -176,7 +177,7 @@ class RawTransactions {
|
||||
data: {
|
||||
hexes: script
|
||||
},
|
||||
headers: _this.axiosOptions.headers
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await axios(options)
|
||||
|
||||
@@ -185,7 +186,8 @@ class RawTransactions {
|
||||
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.error) throw new Error(error.error)
|
||||
else if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
@@ -257,9 +259,13 @@ class RawTransactions {
|
||||
async getRawTransaction (txid, verbose = false, usrObj = null) {
|
||||
try {
|
||||
if (typeof txid === 'string') {
|
||||
// console.log(
|
||||
// 'getRawTransaction() this.axiosOptions: ',
|
||||
// this.axiosOptions
|
||||
// )
|
||||
const response = await axios.get(
|
||||
`${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=${verbose}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -272,7 +278,7 @@ class RawTransactions {
|
||||
verbose: verbose,
|
||||
usrObj // pass user data when making an internal call.
|
||||
},
|
||||
headers: _this.axiosOptions.headers
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await axios(options)
|
||||
|
||||
@@ -281,8 +287,14 @@ class RawTransactions {
|
||||
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.error) throw new Error(error.error)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
throw new Error(error.response.data.error)
|
||||
} else if (error.response && error.response.data) {
|
||||
throw error.response.data
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +319,9 @@ class RawTransactions {
|
||||
const inputTxid = vin.txid
|
||||
const inputVout = vin.vout
|
||||
|
||||
// TODO: Coinbase TXs have no input transaction. Figure out how to
|
||||
// handle this corner case.
|
||||
|
||||
// Get the TX details for the input, in order to retrieve the address of
|
||||
// the sender.
|
||||
const txDetailsParent = await this.getRawTransaction(inputTxid, true)
|
||||
@@ -326,8 +341,14 @@ class RawTransactions {
|
||||
|
||||
return retArray
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.error) throw new Error(error.error)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
throw new Error(error.response.data.error)
|
||||
} else if (error.response && error.response.data) {
|
||||
throw error.response.data
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +382,9 @@ class RawTransactions {
|
||||
async getTxData (txid) {
|
||||
try {
|
||||
if (typeof txid !== 'string') {
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
throw new Error(
|
||||
'Input to raw-transaction.js/getTxData() must be a string containg a TXID.'
|
||||
)
|
||||
}
|
||||
|
||||
// Get the TX details for the transaction under consideration.
|
||||
@@ -379,8 +402,14 @@ class RawTransactions {
|
||||
|
||||
return txDetails
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
if (error.error) throw new Error(error.error)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
throw new Error(error.response.data.error)
|
||||
} else if (error.response && error.response.data) {
|
||||
throw error.response.data
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,7 +451,7 @@ class RawTransactions {
|
||||
if (typeof hex === 'string') {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}rawtransactions/sendRawTransaction/${hex}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
if (response.data === '66: insufficient priority') {
|
||||
@@ -443,16 +472,17 @@ class RawTransactions {
|
||||
data: {
|
||||
hexes: hex
|
||||
},
|
||||
headers: _this.axiosOptions.headers
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await _this.axios(options)
|
||||
const response = await this.axios(options)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input hex must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.error) throw new Error(error.error)
|
||||
else if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
+100
-2
@@ -8,16 +8,21 @@
|
||||
(Parent) token.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const axios = require('axios')
|
||||
|
||||
// Local libraries.
|
||||
const Address = require('./address')
|
||||
|
||||
// const BigNumber = require('bignumber.js')
|
||||
const slpMdm = require('slp-mdm')
|
||||
|
||||
// let _this
|
||||
// const addy = new Address()
|
||||
let addy
|
||||
const TransactionBuilder = require('../transaction-builder')
|
||||
|
||||
class TokenType1 {
|
||||
class Nft1 {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
|
||||
@@ -25,6 +30,8 @@ class TokenType1 {
|
||||
|
||||
// Instantiate the transaction builder.
|
||||
TransactionBuilder.setAddress(addy)
|
||||
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -412,6 +419,97 @@ class TokenType1 {
|
||||
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 = TokenType1
|
||||
module.exports = Nft1
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ class SLP {
|
||||
this.Address = new Address(config)
|
||||
this.ECPair = ECPair
|
||||
this.TokenType1 = new TokenType1(config)
|
||||
this.NFT1 = new NFT1(this.restURL)
|
||||
this.NFT1 = new NFT1(config)
|
||||
this.Utils = new Utils(config)
|
||||
}
|
||||
}
|
||||
|
||||
+18
-12
@@ -125,13 +125,13 @@ class TokenType1 {
|
||||
// console.log('baseChange: ', baseChange)
|
||||
|
||||
// Check for potential burns
|
||||
const outputQty = new BigNumber(baseChange).plus(
|
||||
new BigNumber(baseQty)
|
||||
)
|
||||
const outputQty = new BigNumber(baseChange).plus(new BigNumber(baseQty))
|
||||
const inputQty = new BigNumber(totalTokens)
|
||||
const tokenOutputDelta = outputQty.minus(inputQty).toString() !== '0'
|
||||
if (tokenOutputDelta) {
|
||||
throw new Error('Token transaction inputs do not match outputs, cannot send transaction')
|
||||
throw new Error(
|
||||
'Token transaction inputs do not match outputs, cannot send transaction'
|
||||
)
|
||||
}
|
||||
|
||||
// Generate the OP_RETURN as a Buffer.
|
||||
@@ -152,7 +152,9 @@ class TokenType1 {
|
||||
const tokenSingleOutputError =
|
||||
noChangeOutputQty.minus(noChangeInputQty).toString() !== '0'
|
||||
if (tokenSingleOutputError) {
|
||||
throw new Error('Token transaction inputs do not match outputs, cannot send transaction')
|
||||
throw new Error(
|
||||
'Token transaction inputs do not match outputs, cannot send transaction'
|
||||
)
|
||||
}
|
||||
|
||||
// Generate the OP_RETURN as a Buffer.
|
||||
@@ -285,9 +287,15 @@ class TokenType1 {
|
||||
try {
|
||||
// TODO: Add input validation.
|
||||
|
||||
let baseQty = new BigNumber(configObj.initialQty).times(
|
||||
10 ** configObj.decimals
|
||||
)
|
||||
let baseQty
|
||||
if (configObj.decimals !== 0) {
|
||||
baseQty = new BigNumber(configObj.initialQty).times(
|
||||
10 ** configObj.decimals
|
||||
)
|
||||
} else {
|
||||
baseQty = new BigNumber(configObj.initialQty)
|
||||
}
|
||||
|
||||
baseQty = baseQty.absoluteValue()
|
||||
baseQty = Math.floor(baseQty)
|
||||
baseQty = baseQty.toString()
|
||||
@@ -378,9 +386,7 @@ class TokenType1 {
|
||||
|
||||
// Throw an error if the minting baton could not be found.
|
||||
if (!mintBatonUtxo) {
|
||||
throw new Error(
|
||||
'Minting baton could not be found in tokenUtxos array.'
|
||||
)
|
||||
throw new Error('Minting baton could not be found in tokenUtxos array.')
|
||||
}
|
||||
|
||||
const tokenId = mintBatonUtxo.tokenId
|
||||
@@ -454,7 +460,7 @@ class TokenType1 {
|
||||
const result = await _this.axios.post(
|
||||
`${this.restURL}slp/generatesendopreturn`,
|
||||
data,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
const slpSendObj = result.data
|
||||
|
||||
+49
-48
@@ -217,14 +217,14 @@ class Utils {
|
||||
try {
|
||||
let response
|
||||
if (method === 'get') {
|
||||
response = await _this.axios.get(path, _this.axiosOptions)
|
||||
response = await _this.axios.get(path, this.axiosOptions)
|
||||
} else {
|
||||
response = await _this.axios.post(
|
||||
path,
|
||||
{
|
||||
tokenIds: id
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
}
|
||||
return response.data
|
||||
@@ -315,7 +315,7 @@ class Utils {
|
||||
if (typeof address === 'string') {
|
||||
const path = `${this.restURL}slp/balancesForAddress/${address}`
|
||||
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
return response.data
|
||||
|
||||
// Array of addresses.
|
||||
@@ -328,7 +328,7 @@ class Utils {
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -380,7 +380,7 @@ class Utils {
|
||||
try {
|
||||
const path = `${this.restURL}slp/balancesForToken/${tokenId}`
|
||||
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
@@ -450,13 +450,14 @@ class Utils {
|
||||
else txids = txid
|
||||
|
||||
try {
|
||||
// console.log('validateTxid() this.axiosOptions: ', this.axiosOptions)
|
||||
const response = await _this.axios.post(
|
||||
path,
|
||||
{
|
||||
txids: txids,
|
||||
usrObj // pass user data when making an internal call.
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(
|
||||
// `validateTxid response.data: ${JSON.stringify(response.data, null, 2)}`
|
||||
@@ -520,7 +521,8 @@ class Utils {
|
||||
|
||||
const path = `${this.restURL}slp/validateTxid2/${txid}`
|
||||
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
// console.log('validateTxid2() this.axiosOptions: ', this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
@@ -589,7 +591,7 @@ class Utils {
|
||||
|
||||
// Retrieve the whitelist from the REST API if we haven't gotten it yet.
|
||||
if (this.whitelist.length === 0) {
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
this.whitelist = response.data
|
||||
@@ -666,13 +668,14 @@ class Utils {
|
||||
else txids = txid
|
||||
|
||||
try {
|
||||
// console.log('validateTxid3() this.axiosOptions: ', this.axiosOptions)
|
||||
const response = await _this.axios.post(
|
||||
path,
|
||||
{
|
||||
txids: txids,
|
||||
usrObj // pass user data when making an internal call.
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
@@ -680,6 +683,14 @@ class Utils {
|
||||
|
||||
return validatedTxids
|
||||
} catch (error) {
|
||||
// console.log('validateTxid3 error: ', error)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
throw new Error(error.response.data.error)
|
||||
}
|
||||
|
||||
// Not sure if this can be safely deprecated?
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
throw error
|
||||
}
|
||||
@@ -724,7 +735,7 @@ class Utils {
|
||||
try {
|
||||
const path = `${this.restURL}slp/tokenStats/${tokenId}`
|
||||
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -795,7 +806,7 @@ class Utils {
|
||||
try {
|
||||
const path = `${this.restURL}slp/transactions/${tokenId}/${address}`
|
||||
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -835,7 +846,7 @@ class Utils {
|
||||
try {
|
||||
const path = `${this.restURL}slp/burnTotal/${transactionId}`
|
||||
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
@@ -879,7 +890,7 @@ class Utils {
|
||||
// console.log(`this.restURL: ${this.restURL}`)
|
||||
const path = `${this.restURL}slp/txDetails/${txid}`
|
||||
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
@@ -910,7 +921,7 @@ class Utils {
|
||||
* const txid =
|
||||
* "266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1"
|
||||
*
|
||||
* const data = await slp.Utils.decodeOpReturn2(txid)
|
||||
* const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
|
||||
*
|
||||
* console.log(`Decoded OP_RETURN data: ${JSON.stringify(data,null,2)}`)
|
||||
* } catch (error) {
|
||||
@@ -932,7 +943,6 @@ class Utils {
|
||||
*/
|
||||
// Reimplementation of decodeOpReturn() using slp-parser.
|
||||
async decodeOpReturn (txid, cache = null, usrObj = null) {
|
||||
console.log(`Entering decodeOpReturn with txid ${txid}.`)
|
||||
// The cache object is an in-memory cache (JS Object) that can be passed
|
||||
// into this function. It helps if multiple vouts from the same TXID are
|
||||
// being evaluated. In that case, it can significantly reduce the number
|
||||
@@ -957,19 +967,18 @@ class Utils {
|
||||
throw new Error('txid string must be included.')
|
||||
}
|
||||
|
||||
console.log(`axiosOptions: ${JSON.stringify(_this.axiosOptions, null, 2)}`)
|
||||
|
||||
// CT: 2/24/21 Deprected GET in favor of POST, to pass IP address.
|
||||
// Retrieve the transaction object from the full node.
|
||||
const path = `${this.restURL}rawtransactions/getRawTransaction`
|
||||
const response = await _this.axios.post(
|
||||
// console.log('decodeOpReturn() this.axiosOptions: ', this.axiosOptions)
|
||||
const response = await this.axios.post(
|
||||
path,
|
||||
{
|
||||
verbose: true,
|
||||
txids: [txid],
|
||||
usrObj // pass user data when making an internal call.
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
const txDetails = response.data[0]
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
@@ -1016,20 +1025,18 @@ class Utils {
|
||||
|
||||
if (cache) cache[txid] = tokenData
|
||||
|
||||
console.log('Exiting decodeOpReturn()')
|
||||
|
||||
return tokenData
|
||||
} catch (error) {
|
||||
// Used for debugging
|
||||
console.log('decodeOpReturn error: ', error)
|
||||
console.log(`decodeOpReturn error.message: ${error.message}`)
|
||||
if (error.response && error.response.data) {
|
||||
console.log(
|
||||
`decodeOpReturn error.response.data: ${JSON.stringify(
|
||||
error.response.data
|
||||
)}`
|
||||
)
|
||||
}
|
||||
// console.log('decodeOpReturn error: ', error)
|
||||
// console.log(`decodeOpReturn error.message: ${error.message}`)
|
||||
// if (error.response && error.response.data) {
|
||||
// console.log(
|
||||
// `decodeOpReturn error.response.data: ${JSON.stringify(
|
||||
// error.response.data
|
||||
// )}`
|
||||
// )
|
||||
// }
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -1103,7 +1110,6 @@ class Utils {
|
||||
* }
|
||||
*/
|
||||
async tokenUtxoDetails (utxos, usrObj = null) {
|
||||
console.log('Entering tokenUtxoDetails()')
|
||||
try {
|
||||
// Throw error if input is not an array.
|
||||
if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
|
||||
@@ -1162,11 +1168,9 @@ class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Exiting tokenUtxoDetails()')
|
||||
|
||||
return outAry
|
||||
} catch (error) {
|
||||
console.log('Error in tokenUtxoDetails()')
|
||||
// console.log('Error in tokenUtxoDetails()')
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
throw error
|
||||
}
|
||||
@@ -1291,7 +1295,6 @@ class Utils {
|
||||
// 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) {
|
||||
console.log('Entering _hydrateUtxo()')
|
||||
try {
|
||||
const decodeOpReturnCache = {}
|
||||
|
||||
@@ -1511,10 +1514,8 @@ class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Exiting _hydrateUtxo()')
|
||||
return outAry
|
||||
} catch (error) {
|
||||
console.log('Error in _hydrateUtxo()')
|
||||
// console.log('_hydrateUtxo error: ', error)
|
||||
throw error
|
||||
}
|
||||
@@ -1557,7 +1558,6 @@ class Utils {
|
||||
* true
|
||||
*/
|
||||
async waterfallValidateTxid (txid, usrObj = null) {
|
||||
console.log('Entering waterfallValidateTxid()')
|
||||
try {
|
||||
// console.log('txid: ', txid)
|
||||
|
||||
@@ -1647,13 +1647,16 @@ class Utils {
|
||||
return isValid
|
||||
}
|
||||
|
||||
console.log('Exiting waterfallValidateTxid()')
|
||||
|
||||
// If isValid is still null, return that value, signaling that the txid
|
||||
// could not be validated.
|
||||
return isValid
|
||||
} catch (error) {
|
||||
console.log('Error in waterfallValidateTxid()')
|
||||
// This case handles rate limit errors.
|
||||
if (error.response && error.response.data && error.response.data.error) {
|
||||
throw new Error(error.response.data.error)
|
||||
}
|
||||
|
||||
// console.log('Error in waterfallValidateTxid()')
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
throw error
|
||||
}
|
||||
@@ -1844,21 +1847,19 @@ class Utils {
|
||||
// Throw error if input is not an array.
|
||||
if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
|
||||
|
||||
console.log(
|
||||
`axiosOptions: ${JSON.stringify(_this.axiosOptions, null, 2)}`
|
||||
)
|
||||
|
||||
const response = await _this.axios.post(
|
||||
`${this.restURL}slp/hydrateUtxos`,
|
||||
{
|
||||
utxos: utxos,
|
||||
usrObj
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} catch (error) {
|
||||
console.log('Error in hydrateUtxos(): ', error)
|
||||
|
||||
if (error.response && error.response.data) {
|
||||
throw new Error(JSON.stringify(error.response.data, null, 2))
|
||||
}
|
||||
@@ -1892,7 +1893,7 @@ class Utils {
|
||||
{
|
||||
utxos: utxos
|
||||
},
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
@@ -1932,7 +1933,7 @@ class Utils {
|
||||
const path = `${this.restURL}slp/status`
|
||||
|
||||
try {
|
||||
const response = await _this.axios.get(path, _this.axiosOptions)
|
||||
const response = await _this.axios.get(path, this.axiosOptions)
|
||||
// console.log(
|
||||
// `getStatus response.data: ${JSON.stringify(response.data, null, 2)}`
|
||||
// )
|
||||
|
||||
+395
-12
@@ -2,14 +2,18 @@
|
||||
High-level functions for working with Transactions
|
||||
*/
|
||||
|
||||
const BigNumber = require('bignumber.js')
|
||||
|
||||
const RawTransaction = require('./raw-transactions')
|
||||
const SlpUtils = require('./slp/utils')
|
||||
const Blockchain = require('./blockchain')
|
||||
|
||||
class Transaction {
|
||||
constructor (config) {
|
||||
// Encapsulate dependencies
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.blockchain = new Blockchain(config)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,10 +38,16 @@ class Transaction {
|
||||
* }
|
||||
* })()
|
||||
*/
|
||||
|
||||
// CT 10/31/21: TODO: this function should be refactored to use get2(), but
|
||||
// add waterfall validation of the TX and its inputs.
|
||||
|
||||
async get (txid) {
|
||||
try {
|
||||
if (typeof txid !== 'string') {
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
throw new Error(
|
||||
'Input to Transaction.get() must be a string containing a TXID.'
|
||||
)
|
||||
}
|
||||
|
||||
const txDetails = await this.rawTransaction.getTxData(txid)
|
||||
@@ -73,9 +83,18 @@ class Transaction {
|
||||
// Add the token quantity to each output.
|
||||
for (let i = 0; i < outTokenData.amounts.length; i++) {
|
||||
const rawQty = outTokenData.amounts[i]
|
||||
const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
|
||||
// const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
|
||||
|
||||
txDetails.vout[i + 1].tokenQty = realQty
|
||||
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||
// floating point number.
|
||||
let realQty = new BigNumber(rawQty).dividedBy(
|
||||
10 ** parseInt(txDetails.tokenDecimals)
|
||||
)
|
||||
realQty = realQty.toString()
|
||||
// realQty = parseFloat(realQty)
|
||||
|
||||
txDetails.vout[i + 1].tokenQtyStr = realQty
|
||||
txDetails.vout[i + 1].tokenQty = parseFloat(realQty)
|
||||
}
|
||||
|
||||
// Add tokenQty = null to any outputs that don't have a value.
|
||||
@@ -90,6 +109,7 @@ class Transaction {
|
||||
// Loop through each input and retrieve the token data.
|
||||
for (let i = 0; i < txDetails.vin.length; i++) {
|
||||
const thisVin = txDetails.vin[i]
|
||||
// console.log(`thisVin: ${JSON.stringify(thisVin, null, 2)}`)
|
||||
|
||||
try {
|
||||
// If decodeOpReturn() throws an error, then this input is not
|
||||
@@ -99,18 +119,66 @@ class Transaction {
|
||||
// `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
|
||||
// )
|
||||
|
||||
// Get the appropriate vout token amount. This may throw an error,
|
||||
// which means this Vin is not actually a token UTXO, it was just
|
||||
// associated with a previous token TX.
|
||||
const tokenQty = inTokenData.amounts[thisVin.vout - 1]
|
||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||
let tokenQty = 0
|
||||
|
||||
// Validate the input. Mark qty as null if not valid.
|
||||
const vinIsValid = await this.slpUtils.waterfallValidateTxid(
|
||||
thisVin.txid
|
||||
)
|
||||
|
||||
if (!vinIsValid) {
|
||||
// If the input is not a valid, then set qty as null.
|
||||
tokenQty = null
|
||||
} else if (inTokenData.txType === 'SEND') {
|
||||
// Get the appropriate vout token amount. This may throw an error,
|
||||
// which means this Vin is not actually a token UTXO, it was just
|
||||
// associated with a previous token TX.
|
||||
tokenQty = inTokenData.amounts[thisVin.vout - 1]
|
||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||
|
||||
//
|
||||
} else if (inTokenData.txType === 'GENESIS') {
|
||||
// Only vout[1] of a Genesis transaction represents the tokens.
|
||||
// Any other outputs in that transaction are normal BCH UTXOs.
|
||||
if (thisVin.vout === 1) {
|
||||
tokenQty = inTokenData.qty
|
||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||
}
|
||||
} else if (inTokenData.txType === 'MINT') {
|
||||
// vout=1 (second output) recieves the newly minted tokens.
|
||||
if (thisVin.vout === 1) {
|
||||
tokenQty = inTokenData.qty
|
||||
} else {
|
||||
tokenQty = null
|
||||
}
|
||||
|
||||
//
|
||||
} else {
|
||||
console.log(
|
||||
'Unexpected code path in Transaction.get(). What is the txType?'
|
||||
)
|
||||
console.log(inTokenData)
|
||||
throw new Error('Unexpected code path')
|
||||
}
|
||||
|
||||
if (tokenQty) {
|
||||
const realQty =
|
||||
Number(tokenQty) / Math.pow(10, txDetails.tokenDecimals)
|
||||
// const realQty =
|
||||
// Number(tokenQty) / Math.pow(10, txDetails.tokenDecimals)
|
||||
|
||||
thisVin.tokenQty = realQty
|
||||
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||
// floating point number.
|
||||
let realQty = new BigNumber(tokenQty).dividedBy(
|
||||
10 ** parseInt(txDetails.tokenDecimals)
|
||||
)
|
||||
realQty = realQty.toString()
|
||||
// realQty = parseFloat(realQty)
|
||||
|
||||
thisVin.tokenQtyStr = realQty
|
||||
thisVin.tokenQty = parseFloat(realQty)
|
||||
// txDetails.vin[i].tokenQty = tokenQty
|
||||
|
||||
// Add token ID to input
|
||||
thisVin.tokenId = inTokenData.tokenId
|
||||
} else {
|
||||
thisVin.tokenQty = null
|
||||
}
|
||||
@@ -126,9 +194,17 @@ class Transaction {
|
||||
// Finally, validate the SLP TX.
|
||||
// this.slpUtils.waterfallValidateTxid(txid, usrObj)
|
||||
txDetails.isValidSLPTx = await this.slpUtils.waterfallValidateTxid(txid)
|
||||
|
||||
// TODO: Convert the block hash to a block height. Add block height
|
||||
// value to the transaction.
|
||||
} catch (err) {
|
||||
// console.log('Error: ', err)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (err.response && err.response.data && err.response.data.error) {
|
||||
throw new Error(err.response.data.error)
|
||||
}
|
||||
|
||||
// If decoding the op_return fails, then it's not an SLP transaction,
|
||||
// and the non-hyrated TX details can be returned.
|
||||
return txDetails
|
||||
@@ -136,10 +212,317 @@ class Transaction {
|
||||
|
||||
return txDetails
|
||||
} catch (err) {
|
||||
console.error('Error in transactions.js/get()')
|
||||
// console.error('Error in transactions.js/get(): ', err)
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (err.response && err.response.data && err.response.data.error) {
|
||||
throw new Error(err.response.data.error)
|
||||
}
|
||||
|
||||
if (err.error) throw new Error(err.error)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction.get3() get3()
|
||||
* @apiName get3
|
||||
* @apiGroup Transaction
|
||||
* @apiDescription
|
||||
* Returns an object of transaction data, including addresses for input UTXOs.
|
||||
* If it is a SLP token transaction, the token information for inputs and
|
||||
* outputs will also be included.
|
||||
*
|
||||
* This is an API heavy call. This function will only work with a single txid.
|
||||
* It does not yet support an array of TXIDs.
|
||||
*
|
||||
* This is the same as get(), except it omits DAG validation of the TXID.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.Transaction.get2("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||
* console.log(txData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*/
|
||||
async get3 (txid) {
|
||||
try {
|
||||
if (typeof txid !== 'string') {
|
||||
throw new Error(
|
||||
'Input to Transaction.get() must be a string containing a TXID.'
|
||||
)
|
||||
}
|
||||
|
||||
// Get TX data
|
||||
const txDetails = await this.rawTransaction.getTxData(txid)
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
// Get the block height the transaction was mined in.
|
||||
const blockHeader = await this.blockchain.getBlockHeader(
|
||||
txDetails.blockhash
|
||||
)
|
||||
txDetails.blockheight = blockHeader.height
|
||||
// console.log(`blockHeader: ${JSON.stringify(blockHeader, null, 2)}`)
|
||||
|
||||
// Set default as not an SLP tx
|
||||
txDetails.isSlpTx = false
|
||||
|
||||
// Get Token Data
|
||||
const txTokenData = await this.getTokenInfo(txid)
|
||||
// console.log(`txTokenData: ${JSON.stringify(txTokenData, null, 2)}`)
|
||||
|
||||
// If not a token, return the tx data. Processing is complete.
|
||||
if (!txTokenData) return txDetails
|
||||
|
||||
// Mark TX as an SLP tx. This does not mean it's valid, it just means
|
||||
// the OP_RETURN passes a basic check.
|
||||
txDetails.isSlpTx = true
|
||||
|
||||
// Get Genesis data
|
||||
const genesisData = await this.getTokenInfo(txTokenData.tokenId)
|
||||
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
|
||||
|
||||
// Add token information to the tx details object.
|
||||
txDetails.tokenTxType = txTokenData.txType
|
||||
txDetails.tokenId = txTokenData.tokenId
|
||||
txDetails.tokenTicker = genesisData.ticker
|
||||
txDetails.tokenName = genesisData.name
|
||||
txDetails.tokenDecimals = genesisData.decimals
|
||||
txDetails.tokenUri = genesisData.documentUri
|
||||
txDetails.tokenDocHash = genesisData.documentHash
|
||||
// console.log(`txDetails before processing input and outputs: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
// Process TX Outputs
|
||||
// Add the token quantity to each output.
|
||||
// 'i' starts at 1, because vout[0] is the OP_RETURN
|
||||
for (let i = 0; i < txDetails.vout.length; i++) {
|
||||
const thisVout = txDetails.vout[i]
|
||||
if (txTokenData.txType === 'SEND') {
|
||||
// console.log(
|
||||
// `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
|
||||
// )
|
||||
|
||||
// First output is OP_RETURN, so tokenQty is null.
|
||||
if (i === 0) {
|
||||
thisVout.tokenQty = null
|
||||
thisVout.tokenQtyStr = null
|
||||
continue
|
||||
}
|
||||
|
||||
// Non SLP outputs.
|
||||
if (i > txTokenData.amounts.length) {
|
||||
thisVout.tokenQty = null
|
||||
thisVout.tokenQtyStr = null
|
||||
continue
|
||||
}
|
||||
|
||||
const rawQty = txTokenData.amounts[i - 1]
|
||||
|
||||
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||
// floating point number.
|
||||
let realQty = new BigNumber(rawQty).dividedBy(
|
||||
10 ** parseInt(txDetails.tokenDecimals)
|
||||
)
|
||||
realQty = realQty.toString()
|
||||
// realQty = parseFloat(realQty)
|
||||
|
||||
txDetails.vout[i].tokenQtyStr = realQty
|
||||
txDetails.vout[i].tokenQty = parseFloat(realQty)
|
||||
|
||||
// console.log(
|
||||
// `thisVout ${i}: ${JSON.stringify(txDetails.vout[i], null, 2)}`
|
||||
// )
|
||||
} else if (
|
||||
txTokenData.txType === 'GENESIS' ||
|
||||
txTokenData.txType === 'MINT'
|
||||
) {
|
||||
// console.log(
|
||||
// `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
|
||||
// )
|
||||
|
||||
let tokenQty = 0 // Default value
|
||||
|
||||
// Only vout[1] of a Genesis or Mint transaction represents the tokens.
|
||||
// Any other outputs in that transaction are normal BCH UTXOs.
|
||||
if (i === 1) {
|
||||
tokenQty = txTokenData.qty
|
||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||
|
||||
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||
// floating point number.
|
||||
let realQty = new BigNumber(tokenQty).dividedBy(
|
||||
10 ** parseInt(txDetails.tokenDecimals)
|
||||
)
|
||||
realQty = realQty.toString()
|
||||
// realQty = parseFloat(realQty)
|
||||
|
||||
thisVout.tokenQtyStr = realQty
|
||||
thisVout.tokenQty = parseFloat(realQty)
|
||||
// console.log(`thisVout[${i}]: ${JSON.stringify(thisVout, null, 2)}`)
|
||||
} else if (i === txTokenData.mintBatonVout) {
|
||||
// Optional Mint baton
|
||||
thisVout.tokenQtyStr = '0'
|
||||
thisVout.tokenQty = 0
|
||||
thisVout.isMintBaton = true
|
||||
} else {
|
||||
thisVout.tokenQtyStr = '0'
|
||||
thisVout.tokenQty = 0
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unknown SLP TX type for TX')
|
||||
}
|
||||
}
|
||||
|
||||
// Process TX inputs
|
||||
for (let i = 0; i < txDetails.vin.length; i++) {
|
||||
const thisVin = txDetails.vin[i]
|
||||
// console.log(`thisVin[${i}]: ${JSON.stringify(thisVin, null, 2)}`)
|
||||
|
||||
const vinTokenData = await this.getTokenInfo(thisVin.txid)
|
||||
// console.log(
|
||||
// `vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
|
||||
// )
|
||||
|
||||
// Corner case: Ensure the token ID is the same.
|
||||
const vinTokenIdIsTheSame = vinTokenData.tokenId === txDetails.tokenId
|
||||
|
||||
// If the input is not a token input, or if the tokenID is not the same,
|
||||
// then mark the token output as null.
|
||||
if (!vinTokenData || !vinTokenIdIsTheSame) {
|
||||
thisVin.tokenQty = 0
|
||||
thisVin.tokenQtyStr = '0'
|
||||
thisVin.tokenId = null
|
||||
continue
|
||||
}
|
||||
|
||||
if (vinTokenData.txType === 'SEND') {
|
||||
// console.log(
|
||||
// `SEND vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
|
||||
// )
|
||||
|
||||
const tokenQty = vinTokenData.amounts[thisVin.vout - 1]
|
||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||
|
||||
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||
// floating point number.
|
||||
let realQty = new BigNumber(tokenQty).dividedBy(
|
||||
10 ** parseInt(txDetails.tokenDecimals)
|
||||
)
|
||||
realQty = realQty.toString()
|
||||
// realQty = parseFloat(realQty)
|
||||
|
||||
thisVin.tokenQtyStr = realQty
|
||||
thisVin.tokenQty = parseFloat(realQty)
|
||||
thisVin.tokenId = vinTokenData.tokenId
|
||||
} else if (vinTokenData.txType === 'MINT') {
|
||||
// console.log(
|
||||
// `MINT vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
|
||||
// )
|
||||
|
||||
let tokenQty = 0 // Default value
|
||||
|
||||
// Only vout[1] of a Genesis transaction represents the tokens.
|
||||
// Any other outputs in that transaction are normal BCH UTXOs.
|
||||
if (thisVin.vout === 1) {
|
||||
tokenQty = vinTokenData.qty
|
||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||
|
||||
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||
// floating point number.
|
||||
let realQty = new BigNumber(tokenQty).dividedBy(
|
||||
10 ** parseInt(txDetails.tokenDecimals)
|
||||
)
|
||||
realQty = realQty.toString()
|
||||
// realQty = parseFloat(realQty)
|
||||
|
||||
thisVin.tokenQtyStr = realQty
|
||||
thisVin.tokenQty = parseFloat(realQty)
|
||||
thisVin.tokenId = vinTokenData.tokenId
|
||||
} else if (thisVin.vout === vinTokenData.mintBatonVout) {
|
||||
// Optional Mint baton
|
||||
thisVin.tokenQtyStr = '0'
|
||||
thisVin.tokenQty = 0
|
||||
thisVin.tokenId = vinTokenData.tokenId
|
||||
thisVin.isMintBaton = true
|
||||
} else {
|
||||
thisVin.tokenQtyStr = '0'
|
||||
thisVin.tokenQty = 0
|
||||
thisVin.tokenId = null
|
||||
}
|
||||
} else if (vinTokenData.txType === 'GENESIS') {
|
||||
// console.log(
|
||||
// `GENESIS vinTokenData ${i}: ${JSON.stringify(
|
||||
// vinTokenData,
|
||||
// null,
|
||||
// 2
|
||||
// )}`
|
||||
// )
|
||||
|
||||
let tokenQty = 0 // Default value
|
||||
|
||||
// Only vout[1] of a Genesis transaction represents the tokens.
|
||||
// Any other outputs in that transaction are normal BCH UTXOs.
|
||||
if (thisVin.vout === 1) {
|
||||
tokenQty = vinTokenData.qty
|
||||
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
|
||||
|
||||
// Calculate the real quantity using a BigNumber, then convert it to a
|
||||
// floating point number.
|
||||
let realQty = new BigNumber(tokenQty).dividedBy(
|
||||
10 ** parseInt(txDetails.tokenDecimals)
|
||||
)
|
||||
realQty = realQty.toString()
|
||||
// realQty = parseFloat(realQty)
|
||||
|
||||
thisVin.tokenQtyStr = realQty
|
||||
thisVin.tokenQty = parseFloat(realQty)
|
||||
thisVin.tokenId = vinTokenData.tokenId
|
||||
} else if (thisVin.vout === vinTokenData.mintBatonVout) {
|
||||
// Optional Mint baton
|
||||
thisVin.tokenQtyStr = '0'
|
||||
thisVin.tokenQty = 0
|
||||
thisVin.tokenId = vinTokenData.tokenId
|
||||
thisVin.isMintBaton = true
|
||||
} else {
|
||||
thisVin.tokenQtyStr = '0'
|
||||
thisVin.tokenQty = 0
|
||||
thisVin.tokenId = null
|
||||
}
|
||||
} else {
|
||||
console.log(
|
||||
`Unknown vinTokenData: ${JSON.stringify(vinTokenData, null, 2)}`
|
||||
)
|
||||
throw new Error('Unknown token type in input')
|
||||
}
|
||||
}
|
||||
|
||||
return txDetails
|
||||
} catch (err) {
|
||||
console.error('Error in get3()')
|
||||
|
||||
// This case handles rate limit errors.
|
||||
if (err.response && err.response.data && err.response.data.error) {
|
||||
throw new Error(err.response.data.error)
|
||||
}
|
||||
|
||||
if (err.error) throw new Error(err.error)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// A wrapper for decodeOpReturn(). Returns false if txid is not an SLP tx.
|
||||
// Returns the token data if the txid is an SLP tx.
|
||||
async getTokenInfo (txid) {
|
||||
try {
|
||||
const tokenData = await this.slpUtils.decodeOpReturn(txid)
|
||||
return tokenData
|
||||
} catch (err) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Transaction
|
||||
|
||||
+8
-8
@@ -1,6 +1,6 @@
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class Util {
|
||||
constructor (config) {
|
||||
@@ -24,11 +24,11 @@ class Util {
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api util.floor8() floor8()
|
||||
* @api Util.floor8() floor8()
|
||||
* @apiName floor8
|
||||
* @apiGroup Util
|
||||
* @apiDescription Round a number down to 8 decimal places.
|
||||
@@ -63,7 +63,7 @@ class Util {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api util.floor2() floor2()
|
||||
* @api Util.floor2() floor2()
|
||||
* @apiName floor2
|
||||
* @apiGroup Util
|
||||
* @apiDescription Round a number down to 2 decimal places.
|
||||
@@ -98,7 +98,7 @@ class Util {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api util.chunk20() chunk20()
|
||||
* @api Util.chunk20() chunk20()
|
||||
* @apiName chunk20
|
||||
* @apiGroup Util
|
||||
* @apiDescription chunk up an array into multiple arrays of 20 elements each.
|
||||
@@ -151,7 +151,7 @@ class Util {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api util.sleep() sleep()
|
||||
* @api Util.sleep() sleep()
|
||||
* @apiName sleep
|
||||
* @apiGroup Util
|
||||
* @apiDescription Promise-based delay.
|
||||
@@ -175,7 +175,7 @@ class Util {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api util.validateAddress() validateAddress()
|
||||
* @api Util.validateAddress() validateAddress()
|
||||
* @apiName Validate Address.
|
||||
* @apiGroup Util
|
||||
* @apiDescription Return information about the given bitcoin address.
|
||||
@@ -225,7 +225,7 @@ class Util {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}util/validateAddress/${address}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
|
||||
@@ -183,6 +183,10 @@ class UTXO {
|
||||
*/
|
||||
async get (address, useWhitelist = false) {
|
||||
try {
|
||||
if (!address) {
|
||||
throw new Error('Address must be an array or a string')
|
||||
}
|
||||
|
||||
// Convert address to an array if it is a string.
|
||||
if (typeof address === 'string') address = [address]
|
||||
|
||||
@@ -276,6 +280,8 @@ class UTXO {
|
||||
return retAry
|
||||
} catch (err) {
|
||||
console.error('Error in bchjs.utxo.get()')
|
||||
|
||||
if (err.error) throw new Error(err.error)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
const assert = require('chai').assert
|
||||
|
||||
// const RESTURL = `https://abc.fullstack.cash/v4/`
|
||||
const RESTURL = 'https://bchn.fullstack.cash/v4/'
|
||||
// const RESTURL = `http://localhost:3000/v4/`
|
||||
// const RESTURL = `https://abc.fullstack.cash/v5/`
|
||||
const RESTURL = 'https://bchn.fullstack.cash/v5/'
|
||||
// const RESTURL = `http://localhost:3000/v5/`
|
||||
|
||||
const BCHJS = require('../../../src/bch-js')
|
||||
const bchjs = new BCHJS({ restURL: RESTURL })
|
||||
|
||||
@@ -13,9 +13,9 @@ const PRO_PASS = 'testpassword'
|
||||
|
||||
const BCHJS = require('../../../src/bch-js')
|
||||
const bchjs = new BCHJS({
|
||||
// restURL: `https://bchn.fullstack.cash/v4/`,
|
||||
restURL: 'https://abc.fullstack.cash/v4/',
|
||||
// restURL: `http://localhost:3000/v4/`,
|
||||
// restURL: `https://bchn.fullstack.cash/v5/`,
|
||||
restURL: 'https://abc.fullstack.cash/v5/',
|
||||
// restURL: `http://localhost:3000/v5/`,
|
||||
authPass: PRO_PASS
|
||||
})
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ const JWT_TOKEN =
|
||||
|
||||
const BCHJS = require('../../../src/bch-js')
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'https://api.fullstack.cash/v4/',
|
||||
// restURL: `http://localhost:3000/v4/`,
|
||||
restURL: 'https://api.fullstack.cash/v5/',
|
||||
// restURL: `http://localhost:3000/v5/`,
|
||||
apiToken: JWT_TOKEN
|
||||
})
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ const JWT_TOKEN =
|
||||
|
||||
const BCHJS = require('../../../src/bch-js')
|
||||
const bchjs = new BCHJS({
|
||||
// restURL: `https://bchn.fullstack.cash/v4/`,
|
||||
restURL: 'https:/abc.fullstack.cash/v4/',
|
||||
// restURL: `http://localhost:3000/v4/`,
|
||||
// restURL: `https://bchn.fullstack.cash/v5/`,
|
||||
restURL: 'https:/abc.fullstack.cash/v5/',
|
||||
// restURL: `http://localhost:3000/v5/`,
|
||||
apiToken: JWT_TOKEN
|
||||
})
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('#full node rate limits', () => {
|
||||
|
||||
it('should throw error for more than 100 RPM to fullnode', async () => {
|
||||
try {
|
||||
console.log('This test usually doesn\'t pass, because of latency.')
|
||||
console.log("This test usually doesn't pass, because of latency.")
|
||||
for (let i = 0; i < 100; i++) await bchjs.Control.getNetworkInfo()
|
||||
} catch (err) {
|
||||
// console.log(`validating after 10th call`)
|
||||
|
||||
@@ -14,9 +14,9 @@ const JWT_TOKEN =
|
||||
|
||||
const BCHJS = require('../../../src/bch-js')
|
||||
const bchjs = new BCHJS({
|
||||
// restURL: `https://bchn.fullstack.cash/v4/`,
|
||||
restURL: 'https://abc.fullstack.cash/v4/',
|
||||
// restURL: `http://localhost:3000/v4/`,
|
||||
// restURL: `https://bchn.fullstack.cash/v5/`,
|
||||
restURL: 'https://abc.fullstack.cash/v5/',
|
||||
// restURL: `http://localhost:3000/v5/`,
|
||||
apiToken: JWT_TOKEN
|
||||
})
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ util.inspect.defaultOptions = {
|
||||
|
||||
describe('#blockchain', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#getBestBlockHash', () => {
|
||||
@@ -276,6 +276,43 @@ describe('#blockchain', () => {
|
||||
assert.equal(result, null)
|
||||
})
|
||||
})
|
||||
describe('#getBlock', () => {
|
||||
it('should get block information with default verbosity', async () => {
|
||||
const blockhash =
|
||||
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
|
||||
const result = await bchjs.Blockchain.getBlock(blockhash)
|
||||
|
||||
assert.hasAllKeys(result, [
|
||||
'hash',
|
||||
'confirmations',
|
||||
'size',
|
||||
'height',
|
||||
'version',
|
||||
'versionHex',
|
||||
'merkleroot',
|
||||
'tx',
|
||||
'time',
|
||||
'mediantime',
|
||||
'nonce',
|
||||
'bits',
|
||||
'difficulty',
|
||||
'chainwork',
|
||||
'nTx',
|
||||
'previousblockhash',
|
||||
'nextblockhash'
|
||||
])
|
||||
assert.isArray(result.tx)
|
||||
})
|
||||
|
||||
it('should get block information with verbosity 0', async () => {
|
||||
const blockhash =
|
||||
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
|
||||
const verbosity = 0
|
||||
const result = await bchjs.Blockchain.getBlock(blockhash, verbosity)
|
||||
|
||||
assert.isString(result)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
|
||||
@@ -20,7 +20,7 @@ util.inspect.defaultOptions = {
|
||||
|
||||
describe('#rawtransaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('#SLP', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
|
||||
bchjs = new BCHJS({ restURL: process.env.RESTURL })
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('#UTXO', () => {
|
||||
beforeEach(async () => {
|
||||
// sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
|
||||
TODO
|
||||
*/
|
||||
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 3
|
||||
}
|
||||
|
||||
describe('#rawtransaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
})
|
||||
|
||||
/*
|
||||
Testing sentRawTransaction isn't really possible with an integration test,
|
||||
as the endpoint really needs an e2e test to be properly tested. The tests
|
||||
below expect error messages returned from the server, but at least test
|
||||
that the server is responding on those endpoints, and responds consistently.
|
||||
*/
|
||||
describe('sendRawTransaction', () => {
|
||||
it('should send a single transaction hex', async () => {
|
||||
try {
|
||||
const hex =
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
|
||||
|
||||
await bchjs.RawTransactions.sendRawTransaction(hex)
|
||||
// console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ['error'])
|
||||
assert.include(err.error, 'Missing inputs')
|
||||
}
|
||||
})
|
||||
|
||||
it('should send an array of tx hexes', async () => {
|
||||
try {
|
||||
const hexes = [
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
|
||||
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
|
||||
]
|
||||
|
||||
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
|
||||
console.log(`result ${JSON.stringify(result, null, 2)}`)
|
||||
} catch (err) {
|
||||
// console.log(`err: ${util.inspect(err)}`)
|
||||
|
||||
assert.hasAllKeys(err, ['error'])
|
||||
assert.include(err.error, 'Missing inputs')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
Integration tests for the bchjs covering SLP tokens.
|
||||
These tests are specific to the ABC chain.
|
||||
*/
|
||||
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
let bchjs
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
const util = require('util')
|
||||
util.inspect.defaultOptions = {
|
||||
showHidden: true,
|
||||
colors: true,
|
||||
depth: 1
|
||||
}
|
||||
|
||||
describe('#SLP', () => {
|
||||
// before(() => {
|
||||
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
|
||||
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
|
||||
// })
|
||||
|
||||
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('#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 = [
|
||||
// Malformed SLP tx
|
||||
{
|
||||
note: 'Malformed SLP tx',
|
||||
tx_hash:
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Normal TX (non-SLP)
|
||||
{
|
||||
note: 'Normal TX (non-SLP)',
|
||||
tx_hash:
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
tx_pos: 0,
|
||||
value: 400000
|
||||
},
|
||||
// Valid PSF SLP tx
|
||||
{
|
||||
note: 'Valid PSF SLP tx',
|
||||
tx_hash:
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Valid SLP token not in whitelist
|
||||
{
|
||||
note: 'Valid SLP token not in whitelist',
|
||||
tx_hash:
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Token send on BCHN network.
|
||||
{
|
||||
note: 'Token send on BCHN network',
|
||||
tx_hash:
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Token send on ABC network.
|
||||
{
|
||||
note: 'Token send on ABC network',
|
||||
tx_hash:
|
||||
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
// Known invalid SLP token send of PSF tokens.
|
||||
{
|
||||
note: 'Known invalid SLP token send of PSF tokens',
|
||||
tx_hash:
|
||||
'2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
}
|
||||
]
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
|
||||
// Malformed SLP tx
|
||||
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
|
||||
assert.equal(data[0].isValid, false)
|
||||
|
||||
// Normal TX (non-SLP)
|
||||
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
|
||||
assert.equal(data[1].isValid, false)
|
||||
|
||||
// Valid PSF SLP tx
|
||||
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
|
||||
assert.equal(data[2].isValid, true)
|
||||
|
||||
// Valid SLP token not in whitelist
|
||||
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
|
||||
assert.equal(data[3].isValid, true)
|
||||
|
||||
// Token send on BCHN network
|
||||
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
|
||||
assert.equal(data[4].isValid, true)
|
||||
|
||||
// Token send on ABC network
|
||||
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
|
||||
assert.equal(data[5].isValid, null)
|
||||
|
||||
// Known invalid SLP token send of PSF tokens
|
||||
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
|
||||
assert.equal(data[6].isValid, false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#validateTxid3', () => {
|
||||
it('should invalidate a known invalid TXID', async () => {
|
||||
const txid =
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.property(result[0], 'txid')
|
||||
assert.equal(result[0].txid, txid)
|
||||
|
||||
assert.property(result[0], 'valid')
|
||||
assert.equal(result[0].valid, null)
|
||||
})
|
||||
|
||||
it('should validate a known valid TXID', async () => {
|
||||
const txid =
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.property(result[0], 'txid')
|
||||
assert.equal(result[0].txid, txid)
|
||||
|
||||
assert.property(result[0], 'valid')
|
||||
assert.equal(result[0].valid, true)
|
||||
})
|
||||
|
||||
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
|
||||
const txids = [
|
||||
// Malformed SLP tx
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
// Normal TX (non-SLP)
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
// Valid PSF SLP tx
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
// Valid SLP token not in whitelist
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
// Unprocessed SLP TX
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.equal(result[0].txid, txids[0])
|
||||
assert.equal(result[0].valid, null)
|
||||
|
||||
assert.equal(result[1].txid, txids[1])
|
||||
assert.equal(result[1].valid, null)
|
||||
|
||||
assert.equal(result[2].txid, txids[2])
|
||||
assert.equal(result[2].valid, true)
|
||||
|
||||
// True in validateTxid() (no whitelist SLPDB) but null in validateTxid3()
|
||||
assert.equal(result[3].txid, txids[3])
|
||||
assert.equal(result[3].valid, true)
|
||||
|
||||
// Note: This should change from null to true once SLPDB finishes indexing.
|
||||
assert.equal(result[4].txid, txids[4])
|
||||
// True when no whitelist SLPDB is used.
|
||||
assert.equal(result[4].valid, true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#validateTxid', () => {
|
||||
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
|
||||
const txids = [
|
||||
// Malformed SLP tx
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
// Normal TX (non-SLP)
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
// Valid PSF SLP tx
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
// Valid SLP token not in whitelist
|
||||
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
|
||||
// Token send on BCHN network.
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
|
||||
// Token send on ABC network.
|
||||
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
|
||||
assert.equal(result[0].txid, txids[0])
|
||||
assert.equal(result[0].valid, null)
|
||||
|
||||
assert.equal(result[1].txid, txids[1])
|
||||
assert.equal(result[1].valid, null)
|
||||
|
||||
assert.equal(result[2].txid, txids[2])
|
||||
assert.equal(result[2].valid, true)
|
||||
|
||||
// True in validateTxid() but null in validateTxid3()
|
||||
assert.equal(result[3].txid, txids[3])
|
||||
assert.equal(result[3].valid, true)
|
||||
|
||||
assert.equal(result[4].txid, txids[4])
|
||||
assert.equal(result[4].valid, true)
|
||||
|
||||
assert.equal(result[5].txid, txids[5])
|
||||
assert.equal(result[5].valid, null)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Integration tests for the utxo.js library.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe('#UTXO', () => {
|
||||
beforeEach(async () => {
|
||||
// sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should handle an array of addresses', async () => {
|
||||
const addr = [
|
||||
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
||||
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.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)
|
||||
})
|
||||
|
||||
it('should handle NFTs and minting batons', async () => {
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
assert.isArray(result[0].slpUtxos.type1.mintBatons)
|
||||
assert.isArray(result[0].slpUtxos.type1.tokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
|
||||
assert.isArray(result[0].slpUtxos.nft.groupTokens)
|
||||
assert.isArray(result[0].slpUtxos.nft.tokens)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#findBiggestUtxo', () => {
|
||||
it('should sort UTXOs from Electrumx', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
|
||||
it('should sort UTXOs from Utxos.get()', async () => {
|
||||
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
|
||||
|
||||
const utxos = await bchjs.Utxo.get(addr)
|
||||
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
|
||||
|
||||
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'satoshis')
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Integration tests for bchjs dsproof library.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe('#DSProof', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
|
||||
})
|
||||
|
||||
describe('#getDSProof', () => {
|
||||
it('should get TX info from the full node', async () => {
|
||||
const txid =
|
||||
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
|
||||
const result = await bchjs.DSProof.getDSProof(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result, null)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -264,6 +264,31 @@ describe('#SLP', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('#nft1', () => {
|
||||
describe('#listNFTGroupChildren', () => {
|
||||
it('should return array of children GENESIS transactions IDs', async () => {
|
||||
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 return parent NFT group information', async () => {
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
@@ -18,7 +18,7 @@ util.inspect.defaultOptions = {
|
||||
|
||||
describe('#util', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#validateAddress', () => {
|
||||
@@ -81,7 +81,8 @@ describe('#util', () => {
|
||||
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const dataMock = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
|
||||
const dataMock =
|
||||
'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
|
||||
const data = []
|
||||
for (let i = 0; i < 25; i++) data.push(dataMock)
|
||||
|
||||
@@ -12,11 +12,11 @@ const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: 'https://testnet3.fullstack.cash/v4/'
|
||||
: 'https://testnet3.fullstack.cash/v5/'
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
@@ -29,7 +29,7 @@ util.inspect.defaultOptions = {
|
||||
|
||||
describe('#blockchain', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#getBestBlockHash', () => {
|
||||
|
||||
@@ -7,14 +7,14 @@ const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: 'https://testnet3.fullstack.cash/v4/'
|
||||
: 'https://testnet3.fullstack.cash/v5/'
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
describe('#control', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#getNetworkInfo', () => {
|
||||
|
||||
@@ -5,7 +5,7 @@ const util = require('util')
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: 'https://testnet3.fullstack.cash/v4/'
|
||||
: 'https://testnet3.fullstack.cash/v5/'
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
@@ -16,7 +16,7 @@ describe('#ElectrumX', () => {
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
@@ -68,7 +68,9 @@ describe('#ElectrumX', () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++) { addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2') }
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
|
||||
}
|
||||
|
||||
// const result = await bchjs.Electrumx.utxo(addr)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
@@ -121,7 +123,9 @@ describe('#ElectrumX', () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++) { addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2') }
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
|
||||
}
|
||||
|
||||
const result = await bchjs.Electrumx.balance(addr)
|
||||
|
||||
@@ -175,7 +179,9 @@ describe('#ElectrumX', () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++) { addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2') }
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
|
||||
}
|
||||
|
||||
const result = await bchjs.Electrumx.transactions(addr)
|
||||
|
||||
@@ -236,7 +242,9 @@ describe('#ElectrumX', () => {
|
||||
it('should throw error on array size rate limit', async () => {
|
||||
try {
|
||||
const addr = []
|
||||
for (let i = 0; i < 25; i++) { addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2') }
|
||||
for (let i = 0; i < 25; i++) {
|
||||
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
|
||||
}
|
||||
|
||||
await bchjs.Electrumx.unconfirmed(addr)
|
||||
// console.log(`result: ${util.inspect(result)}`)
|
||||
@@ -355,7 +363,7 @@ describe('#ElectrumX', () => {
|
||||
assert.property(err, 'success')
|
||||
assert.equal(err.success, false)
|
||||
assert.include(
|
||||
err.error,
|
||||
err.error.error,
|
||||
'the transaction was rejected by network rules'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS({ ninsightURL: 'https://trest.bitcoin.com/v2' })
|
||||
|
||||
describe('#Ninsight', () => {
|
||||
let sandbox
|
||||
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#utxo', () => {
|
||||
it('should GET utxos for a single address', async () => {
|
||||
const addr = 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr'
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result[0], 'utxos')
|
||||
assert.property(result[0], 'legacyAddress')
|
||||
assert.property(result[0], 'cashAddress')
|
||||
assert.property(result[0], 'slpAddress')
|
||||
assert.property(result[0], 'scriptPubKey')
|
||||
assert.property(result[0], 'asm')
|
||||
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0].utxos[0], 'txid')
|
||||
assert.property(result[0].utxos[0], 'vout')
|
||||
assert.property(result[0].utxos[0], 'amount')
|
||||
assert.property(result[0].utxos[0], 'satoshis')
|
||||
assert.property(result[0].utxos[0], 'height')
|
||||
assert.property(result[0].utxos[0], 'confirmations')
|
||||
})
|
||||
|
||||
it('should POST utxo details for an array of addresses', async () => {
|
||||
const addr = [
|
||||
'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
|
||||
'bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul'
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0], 'utxos')
|
||||
assert.property(result[0], 'legacyAddress')
|
||||
assert.property(result[0], 'cashAddress')
|
||||
assert.property(result[0], 'slpAddress')
|
||||
assert.property(result[0], 'scriptPubKey')
|
||||
assert.property(result[0], 'asm')
|
||||
|
||||
// assert.hasAnyKeys(result[0][0], [
|
||||
// "txid",
|
||||
// "vout",
|
||||
// "value",
|
||||
// "height",
|
||||
// "confirmations"
|
||||
// ])
|
||||
})
|
||||
})
|
||||
describe('#transactions', () => {
|
||||
it('should POST transactions history for a single address', async () => {
|
||||
const addr = 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr'
|
||||
|
||||
const result = await bchjs.Ninsight.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'cashAddress')
|
||||
assert.property(result[0], 'legacyAddress')
|
||||
assert.property(result[0], 'txs')
|
||||
assert.isArray(result[0].txs)
|
||||
assert.property(result[0].txs[0], 'txid')
|
||||
assert.property(result[0].txs[0], 'vin')
|
||||
assert.property(result[0].txs[0], 'vout')
|
||||
})
|
||||
it('should POST transactions history for an array of addresses', async () => {
|
||||
const addr = [
|
||||
'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
|
||||
'bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul'
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'cashAddress')
|
||||
assert.property(result[0], 'legacyAddress')
|
||||
assert.property(result[0], 'txs')
|
||||
assert.isArray(result[0].txs)
|
||||
assert.property(result[0].txs[0], 'txid')
|
||||
assert.property(result[0].txs[0], 'vin')
|
||||
assert.property(result[0].txs[0], 'vout')
|
||||
})
|
||||
})
|
||||
describe('#txDetails', () => {
|
||||
it('should POST transactions details for a single address', async () => {
|
||||
const txid =
|
||||
'76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea'
|
||||
|
||||
const result = await bchjs.Ninsight.txDetails(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'txid')
|
||||
assert.property(result[0], 'version')
|
||||
assert.property(result[0], 'locktime')
|
||||
assert.property(result[0], 'vin')
|
||||
assert.property(result[0], 'vout')
|
||||
assert.property(result[0], 'blockhash')
|
||||
assert.property(result[0], 'blockheight')
|
||||
assert.property(result[0], 'confirmations')
|
||||
assert.property(result[0], 'time')
|
||||
assert.property(result[0], 'blocktime')
|
||||
assert.property(result[0], 'valueOut')
|
||||
assert.property(result[0], 'size')
|
||||
})
|
||||
it('should POST transactions details for an array of addresses', async () => {
|
||||
const txid = [
|
||||
'f191d1062789d7071da6f2168ba306869a829294f6b1c09d03492db4ca3a8e77',
|
||||
'76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea'
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.txDetails(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'txid')
|
||||
assert.property(result[0], 'vin')
|
||||
assert.property(result[0], 'vout')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -10,11 +10,11 @@ const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: 'https://testnet3.fullstack.cash/v4/'
|
||||
: 'https://testnet3.fullstack.cash/v5/'
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
@@ -27,7 +27,7 @@ util.inspect.defaultOptions = {
|
||||
|
||||
describe('#rawtransaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#decodeRawTransaction', () => {
|
||||
|
||||
@@ -7,11 +7,11 @@ const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: 'https://testnet3.fullstack.cash/v4/'
|
||||
: 'https://testnet3.fullstack.cash/v5/'
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
@@ -25,7 +25,7 @@ util.inspect.defaultOptions = {
|
||||
describe('#SLP', () => {
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#util', () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
export RESTURL=https://free-test.fullstack.cash/v4/
|
||||
export RESTURL=https://free-test.fullstack.cash/v5/
|
||||
export NETWORK=testnet
|
||||
export IS_USING_FREE_TIER=true
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ const assert = chai.assert
|
||||
|
||||
const RESTURL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: 'https://testnet3.fullstack.cash/v4/'
|
||||
: 'https://testnet3.fullstack.cash/v5/'
|
||||
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
|
||||
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
|
||||
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
|
||||
|
||||
// Inspect utility used for debugging.
|
||||
@@ -25,7 +25,7 @@ util.inspect.defaultOptions = {
|
||||
|
||||
describe('#util', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#validateAddress', () => {
|
||||
|
||||
@@ -9,7 +9,7 @@ const bchjs = new BCHJS()
|
||||
|
||||
describe('#control', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#getNetworkInfo', () => {
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('#ElectrumX', () => {
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
@@ -359,10 +359,12 @@ describe('#ElectrumX', () => {
|
||||
try {
|
||||
await bchjs.Electrumx.broadcast(txHex)
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
|
||||
assert.property(err, 'success')
|
||||
assert.equal(err.success, false)
|
||||
assert.include(
|
||||
err.error,
|
||||
err.error.error,
|
||||
'the transaction was rejected by network rules'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ const bchjs = new BCHJS()
|
||||
|
||||
describe('#Encryption', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#getPubKey', () => {
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const bchjs = new BCHJS({ ninsightURL: 'https://rest.bitcoin.com/v2' })
|
||||
|
||||
describe('#Ninsight', () => {
|
||||
let sandbox
|
||||
|
||||
beforeEach(async () => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#utxo', () => {
|
||||
it('should GET utxos for a single address', async () => {
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result[0], 'utxos')
|
||||
assert.property(result[0], 'legacyAddress')
|
||||
assert.property(result[0], 'cashAddress')
|
||||
assert.property(result[0], 'slpAddress')
|
||||
assert.property(result[0], 'scriptPubKey')
|
||||
assert.property(result[0], 'asm')
|
||||
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0].utxos[0], 'txid')
|
||||
assert.property(result[0].utxos[0], 'vout')
|
||||
assert.property(result[0].utxos[0], 'amount')
|
||||
assert.property(result[0].utxos[0], 'satoshis')
|
||||
assert.property(result[0].utxos[0], 'height')
|
||||
assert.property(result[0].utxos[0], 'confirmations')
|
||||
})
|
||||
|
||||
it('should POST utxo details for an array of addresses', async () => {
|
||||
const addr = [
|
||||
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7',
|
||||
'bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr'
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.isArray(result[0].utxos)
|
||||
|
||||
assert.property(result[0], 'utxos')
|
||||
assert.property(result[0], 'legacyAddress')
|
||||
assert.property(result[0], 'cashAddress')
|
||||
assert.property(result[0], 'slpAddress')
|
||||
assert.property(result[0], 'scriptPubKey')
|
||||
assert.property(result[0], 'asm')
|
||||
})
|
||||
})
|
||||
describe('#transactions', () => {
|
||||
it('should POST transactions history for a single address', async () => {
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Ninsight.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'cashAddress')
|
||||
assert.property(result[0], 'legacyAddress')
|
||||
assert.property(result[0], 'txs')
|
||||
assert.isArray(result[0].txs)
|
||||
assert.property(result[0].txs[0], 'txid')
|
||||
assert.property(result[0].txs[0], 'vin')
|
||||
assert.property(result[0].txs[0], 'vout')
|
||||
})
|
||||
it('should POST transactions history for an array of addresses', async () => {
|
||||
const addr = [
|
||||
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7',
|
||||
'bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr'
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.transactions(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'cashAddress')
|
||||
assert.property(result[0], 'legacyAddress')
|
||||
assert.property(result[0], 'txs')
|
||||
assert.isArray(result[0].txs)
|
||||
assert.property(result[0].txs[0], 'txid')
|
||||
assert.property(result[0].txs[0], 'vin')
|
||||
assert.property(result[0].txs[0], 'vout')
|
||||
})
|
||||
})
|
||||
describe('#txDetails', () => {
|
||||
it('should POST transactions details for a single address', async () => {
|
||||
const txid = 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33'
|
||||
|
||||
const result = await bchjs.Ninsight.txDetails(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'txid')
|
||||
assert.property(result[0], 'version')
|
||||
assert.property(result[0], 'locktime')
|
||||
assert.property(result[0], 'vin')
|
||||
assert.property(result[0], 'vout')
|
||||
assert.property(result[0], 'blockhash')
|
||||
assert.property(result[0], 'blockheight')
|
||||
assert.property(result[0], 'confirmations')
|
||||
assert.property(result[0], 'time')
|
||||
assert.property(result[0], 'blocktime')
|
||||
assert.property(result[0], 'valueOut')
|
||||
assert.property(result[0], 'size')
|
||||
})
|
||||
it('should POST transactions details for an array of addresses', async () => {
|
||||
const txid = [
|
||||
'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.txDetails(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'txid')
|
||||
assert.property(result[0], 'vin')
|
||||
assert.property(result[0], 'vout')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -4,7 +4,7 @@ const bchjs = new BCHJS()
|
||||
|
||||
describe('#price', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#current', () => {
|
||||
|
||||
@@ -19,7 +19,7 @@ util.inspect.defaultOptions = {
|
||||
|
||||
describe('#rawtransaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
|
||||
})
|
||||
|
||||
describe('#decodeRawTransaction', () => {
|
||||
@@ -191,8 +191,7 @@ describe('#rawtransaction', () => {
|
||||
console.log(`result: ${util.inspect(result)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.hasAnyKeys(err, ['error'])
|
||||
assert.include(err.error, 'Array too large')
|
||||
assert.include(err.message, 'Array too large')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -251,9 +250,13 @@ describe('#rawtransaction', () => {
|
||||
describe('#_getInputAddrs', () => {
|
||||
it('should return an array of input addresses', async () => {
|
||||
// const txid = '32233db13f2ae6d82b6262f335643dccf09fc0bcfcef4bc3fbe023355f02e112'
|
||||
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
const txid =
|
||||
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
const txDetails = await bchjs.RawTransactions.getRawTransaction(txid, true)
|
||||
const txDetails = await bchjs.RawTransactions.getRawTransaction(
|
||||
txid,
|
||||
true
|
||||
)
|
||||
|
||||
const result = await bchjs.RawTransactions._getInputAddrs(txDetails)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
@@ -268,7 +271,8 @@ describe('#rawtransaction', () => {
|
||||
|
||||
describe('#getTxData', () => {
|
||||
it('should return tx data with input addresses', async () => {
|
||||
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
const txid =
|
||||
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
@@ -56,5 +56,26 @@ describe('#Transaction', () => {
|
||||
assert.property(result, 'isValidSLPTx')
|
||||
assert.equal(result.isValidSLPTx, true)
|
||||
})
|
||||
|
||||
// it('should get problematic transaction', async () => {
|
||||
// const txid = 'a55515de32577e296c512840bcaabed5823bb773fb4f8fd8e5197cc96cbc54d1'
|
||||
//
|
||||
// const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
// })
|
||||
|
||||
// TX a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe has
|
||||
// an input TXID (bd84bc1dd5ecd976165892306992401272f6bedeb37d7b2cdbf74fc4a55967a6)
|
||||
// that is also a valid SLP tx, but is unrelated. Both TXs pass DAG validation,
|
||||
// but for separate tokens.
|
||||
it('should get problematic transaction', async () => {
|
||||
const txid = 'a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// The token ID should equal the txid for this Vin.
|
||||
assert.equal(result.vin[2].txid, result.vin[2].tokenId)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+28
-7
@@ -61,16 +61,40 @@ describe('#Blockchain', () => {
|
||||
|
||||
it('should get block by hash', done => {
|
||||
const resolved = new Promise(resolve => resolve({ data: data }))
|
||||
sandbox.stub(axios, 'get').returns(resolved)
|
||||
sandbox.stub(axios, 'post').returns(resolved)
|
||||
|
||||
bchjs.Blockchain.getBlock(
|
||||
const blockhash =
|
||||
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
|
||||
)
|
||||
bchjs.Blockchain.getBlock(blockhash)
|
||||
.then(result => {
|
||||
assert.deepStrictEqual(data, result)
|
||||
})
|
||||
.then(done, done)
|
||||
})
|
||||
it('should throw error if blockhash is not provided', async () => {
|
||||
try {
|
||||
await bchjs.Blockchain.getBlock()
|
||||
assert2.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert2.include(err.message, 'blockhash must be a string')
|
||||
}
|
||||
})
|
||||
it('should handle response error', async () => {
|
||||
try {
|
||||
const error = new Error()
|
||||
error.response = {
|
||||
data: 'Test Error'
|
||||
}
|
||||
sandbox.stub(axios, 'post').throws(error)
|
||||
|
||||
const blockhash =
|
||||
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
|
||||
await bchjs.Blockchain.getBlock(blockhash)
|
||||
assert2.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
assert2.include(err, 'Test Error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getBlockchainInfo', () => {
|
||||
@@ -355,10 +379,7 @@ describe('#Blockchain', () => {
|
||||
try {
|
||||
await bchjs.Blockchain.getTxOut('badtxid')
|
||||
} catch (err) {
|
||||
assert2.include(
|
||||
err.message,
|
||||
'txid needs to be a proper transaction ID'
|
||||
)
|
||||
assert2.include(err.message, 'txid needs to be a proper transaction ID')
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
|
||||
// Unit under test (uut)
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const mockData = require('./fixtures/dsproof-mock')
|
||||
|
||||
let bchjs
|
||||
const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
|
||||
describe('#DSProof', () => {
|
||||
let sandbox
|
||||
beforeEach(() => {
|
||||
bchjs = new BCHJS()
|
||||
sandbox = sinon.createSandbox()
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#getDSProof', () => {
|
||||
it('should throw error if input is not provided', async () => {
|
||||
try {
|
||||
await bchjs.DSProof.getDSProof()
|
||||
assert.equal(false, true, 'unexpected error')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'txid is required')
|
||||
}
|
||||
})
|
||||
it('should throw error txid is invalid', async () => {
|
||||
try {
|
||||
await bchjs.DSProof.getDSProof('txid')
|
||||
assert.equal(false, true, 'unexpected error')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'txid must be of length 64')
|
||||
}
|
||||
})
|
||||
it('should handle error', async () => {
|
||||
try {
|
||||
sandbox.stub(bchjs.DSProof.axios, 'get').throws(new Error('test error'))
|
||||
await bchjs.DSProof.getDSProof(txid)
|
||||
|
||||
assert.equal(false, true, 'unexpected error')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
it('should get double spend proof', async () => {
|
||||
try {
|
||||
sandbox
|
||||
.stub(bchjs.DSProof.axios, 'get')
|
||||
.resolves({ data: mockData.dsproof })
|
||||
const result = await bchjs.DSProof.getDSProof(txid)
|
||||
|
||||
assert.property(result, 'dspid')
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'outpoint')
|
||||
assert.property(result, 'path')
|
||||
assert.property(result, 'descendants')
|
||||
|
||||
assert.property(result.outpoint, 'txid')
|
||||
assert.property(result.outpoint, 'vout')
|
||||
} catch (error) {
|
||||
assert.equal(false, true, 'unexpected error')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Mock data used for unit testing.
|
||||
*/
|
||||
const dsproof = {
|
||||
dspid: '6234fff2a9dbcaa50e2c50d74e1d725a2bed1757d7a05a2db7a82df659554397',
|
||||
txid: 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e',
|
||||
outpoint: {
|
||||
txid: '17ebef34f7e9a0692317dd6fbb43ffccff45b7d688a2a754dbccfc5e6d93ce3e',
|
||||
vout: 1
|
||||
},
|
||||
path: ['ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'],
|
||||
descendants: [
|
||||
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
|
||||
]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
dsproof
|
||||
}
|
||||
@@ -119,6 +119,33 @@ const mockTokens = [
|
||||
}
|
||||
]
|
||||
|
||||
const mockNftChildrenList = {
|
||||
nftChildren: [
|
||||
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9',
|
||||
'928ce61fe1006b1325a0ba0dce700bf83986a6f0691ba26e121c9ac035d12a55'
|
||||
]
|
||||
}
|
||||
|
||||
const mockNftGroup = {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
const balancesForAddress = [
|
||||
{
|
||||
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
@@ -1388,6 +1415,8 @@ module.exports = {
|
||||
mockList,
|
||||
mockToken,
|
||||
mockTokens,
|
||||
mockNftChildrenList,
|
||||
mockNftGroup,
|
||||
balancesForAddress,
|
||||
balancesForAddresses,
|
||||
mockRawTx,
|
||||
|
||||
@@ -175,10 +175,621 @@ const mockOpReturnData03 = {
|
||||
amounts: ['1000000000', '99883400000000']
|
||||
}
|
||||
|
||||
const genesisTestInputTx = {
|
||||
txid: '874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e',
|
||||
hash: '874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e',
|
||||
version: 1,
|
||||
size: 480,
|
||||
locktime: 543408,
|
||||
vin: [
|
||||
{
|
||||
txid: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
|
||||
vout: 1,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'30440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f55[ALL|FORKID] 028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11',
|
||||
hex:
|
||||
'4730440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f554121028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qp2jesd06k8ycj4wvkpl9lcwaemtr04f5yphjsa07v',
|
||||
value: 0.00000546
|
||||
},
|
||||
{
|
||||
txid: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
|
||||
vout: 3,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519[ALL|FORKID] 02cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bc',
|
||||
hex:
|
||||
'483045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519412102cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bc'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qppj3euc36x5u6twr5cxrrea2rca53vsfu3dxwr86j',
|
||||
value: 0.00172192
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 1145980243 323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35 00000000004c4b40 00000000004c4b40',
|
||||
hex:
|
||||
'6a04534c500001010453454e4420323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d350800000000004c4b400800000000004c4b40',
|
||||
type: 'nulldata'
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 0a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e0 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a9140a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e088ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qq98fnuup7eldhtzc067ang76mss29pguqh7qv9eac']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 2,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 d4548261e1be0de7e50b7511597799ec4af2b173 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914d4548261e1be0de7e50b7511597799ec4af2b17388ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qr29fqnpuxlqmel9pd63zkthn8ky4u43wv0v7pg5mn']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00171165,
|
||||
n: 3,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 d4548261e1be0de7e50b7511597799ec4af2b173 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914d4548261e1be0de7e50b7511597799ec4af2b17388ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qr29fqnpuxlqmel9pd63zkthn8ky4u43wv0v7pg5mn']
|
||||
}
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'0100000002359dab0d9c8ddc788b14c0b51493d195c9fbd9f2203d091663350bae351e3a32010000006a4730440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f554121028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11feffffff359dab0d9c8ddc788b14c0b51493d195c9fbd9f2203d091663350bae351e3a32030000006b483045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519412102cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bcfeffffff040000000000000000406a04534c500001010453454e4420323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d350800000000004c4b400800000000004c4b4022020000000000001976a9140a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e088ac22020000000000001976a914d4548261e1be0de7e50b7511597799ec4af2b17388ac9d9c0200000000001976a914d4548261e1be0de7e50b7511597799ec4af2b17388acb04a0800',
|
||||
blockhash: '000000000000000000292a9c6150fce48e2edd8df346948494fe6249e6e7f63b',
|
||||
confirmations: 163095,
|
||||
time: 1534271330,
|
||||
blocktime: 1534271330
|
||||
}
|
||||
|
||||
const mintTestInputTx = {
|
||||
txid: '4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa',
|
||||
hash: '4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa',
|
||||
version: 1,
|
||||
size: 585,
|
||||
locktime: 543613,
|
||||
vin: [
|
||||
{
|
||||
txid: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
vout: 1,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3045022100e3d5f9a48f9aa1cf7e9ed1992e0281e7300b734ba0e4bd5bb9265b2be60bd71002200ccde53ce7ea9da9df3e834f234f3bea65c2e58c96f8436f93333fd8946a1db2[ALL|FORKID] 02056220984cc2cf5261a27d4f66d31c9ef601a688ca1a5ab81e55b6f0d311be74',
|
||||
hex:
|
||||
'483045022100e3d5f9a48f9aa1cf7e9ed1992e0281e7300b734ba0e4bd5bb9265b2be60bd71002200ccde53ce7ea9da9df3e834f234f3bea65c2e58c96f8436f93333fd8946a1db2412102056220984cc2cf5261a27d4f66d31c9ef601a688ca1a5ab81e55b6f0d311be74'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qpaf9wltgmpjlg2vxwwu7zdw5y4z7m277ckxn8cufl',
|
||||
value: 0.00000546,
|
||||
tokenQtyStr: '43545.34534',
|
||||
tokenQty: 43545.34534
|
||||
},
|
||||
{
|
||||
txid: 'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34',
|
||||
vout: 1,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3045022100bb90d52be9b568f643fdb1a302e9f063be27a9f914e2a6b192cbabf2cbdeaf9302203c275fc8d1b82390bc1726390d361c5266056bddd54dec23efa207c79eacea4a[ALL|FORKID] 024146cfcd0c02e99d6451dc48ad0f97114aeda18fe55c52a10bfc0490f314e6a2',
|
||||
hex:
|
||||
'483045022100bb90d52be9b568f643fdb1a302e9f063be27a9f914e2a6b192cbabf2cbdeaf9302203c275fc8d1b82390bc1726390d361c5266056bddd54dec23efa207c79eacea4a4121024146cfcd0c02e99d6451dc48ad0f97114aeda18fe55c52a10bfc0490f314e6a2'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qqf89zzwd0fqc3npkks997r5l7dpfjf9kgx2rqu500',
|
||||
value: 0.00000546,
|
||||
tokenQtyStr: '2.34123',
|
||||
tokenQty: 2.34123
|
||||
},
|
||||
{
|
||||
txid: 'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34',
|
||||
vout: 3,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3044022029ab770c249f467b40bb90410429f6d657f9be299baa19a6838d04d972436b450220143660297e8e836bc16d317649fad8c310bdadb4fb61d4e9f6ba11203abe5dae[ALL|FORKID] 02eed4ac9dda3405d9b1ccbbf09f8056f3e7c615924274bd5643238b543d0a1d56',
|
||||
hex:
|
||||
'473044022029ab770c249f467b40bb90410429f6d657f9be299baa19a6838d04d972436b450220143660297e8e836bc16d317649fad8c310bdadb4fb61d4e9f6ba11203abe5dae412102eed4ac9dda3405d9b1ccbbf09f8056f3e7c615924274bd5643238b543d0a1d56'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qqt30r33k0jx3sxe34tmupaujpaljnglmvqgrrfp2x',
|
||||
value: 0.00054848,
|
||||
tokenQtyStr: '2.34123',
|
||||
tokenQty: 2.34123
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 1145980243 938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8 0000000103907f11',
|
||||
hex:
|
||||
'6a04534c500001010453454e4420938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8080000000103907f11',
|
||||
type: 'nulldata'
|
||||
},
|
||||
tokenQty: null
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 059775ff94f65c04e8a6847e74eb9809d8cd779b OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914059775ff94f65c04e8a6847e74eb9809d8cd779b88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qqzewa0ljnm9cp8g56z8ua8tnqya3nthnvhv5hpu8y']
|
||||
},
|
||||
tokenQtyStr: '43547.68657',
|
||||
tokenQty: 43547.68657
|
||||
},
|
||||
{
|
||||
value: 0.00054808,
|
||||
n: 2,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 0e330dc9009e1e07831f5a22d4ade8977ab674c8 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a9140e330dc9009e1e07831f5a22d4ade8977ab674c888ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qq8rxrwfqz0pupurradz949dazth4dn5eqs3mhrucv']
|
||||
},
|
||||
tokenQty: null
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'0100000003f8cda53b43b0ea49991a9bc6c74eb901d2a8b964bc7b8987d76789618ec18c93010000006b483045022100e3d5f9a48f9aa1cf7e9ed1992e0281e7300b734ba0e4bd5bb9265b2be60bd71002200ccde53ce7ea9da9df3e834f234f3bea65c2e58c96f8436f93333fd8946a1db2412102056220984cc2cf5261a27d4f66d31c9ef601a688ca1a5ab81e55b6f0d311be74feffffff34dbf62a45295ae11c2a45f44368218ec64498ac3f7e1434c1993515f53c9dee010000006b483045022100bb90d52be9b568f643fdb1a302e9f063be27a9f914e2a6b192cbabf2cbdeaf9302203c275fc8d1b82390bc1726390d361c5266056bddd54dec23efa207c79eacea4a4121024146cfcd0c02e99d6451dc48ad0f97114aeda18fe55c52a10bfc0490f314e6a2feffffff34dbf62a45295ae11c2a45f44368218ec64498ac3f7e1434c1993515f53c9dee030000006a473044022029ab770c249f467b40bb90410429f6d657f9be299baa19a6838d04d972436b450220143660297e8e836bc16d317649fad8c310bdadb4fb61d4e9f6ba11203abe5dae412102eed4ac9dda3405d9b1ccbbf09f8056f3e7c615924274bd5643238b543d0a1d56feffffff030000000000000000376a04534c500001010453454e4420938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8080000000103907f1122020000000000001976a914059775ff94f65c04e8a6847e74eb9809d8cd779b88ac18d60000000000001976a9140e330dc9009e1e07831f5a22d4ade8977ab674c888ac7d4b0800',
|
||||
blockhash: '00000000000000000140f0d813052da59c811a936494a8d8b2d60b215d19e3dc',
|
||||
confirmations: 167007,
|
||||
time: 1534391953,
|
||||
blocktime: 1534391953,
|
||||
isValidSLPTx: true,
|
||||
tokenTxType: 'SEND',
|
||||
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
tokenTicker: 'Bubb2',
|
||||
tokenName: 'the new bubbles!',
|
||||
tokenDecimals: 5,
|
||||
tokenUri: '',
|
||||
tokenDocHash: ''
|
||||
}
|
||||
|
||||
const genesisTestOpReturnData01 = {
|
||||
tokenType: 1,
|
||||
txType: 'SEND',
|
||||
tokenId: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
|
||||
amounts: ['5000000', '5000000']
|
||||
}
|
||||
|
||||
const genesisTestOpReturnData02 = {
|
||||
tokenType: 1,
|
||||
txType: 'GENESIS',
|
||||
ticker: '',
|
||||
name: '',
|
||||
tokenId: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
|
||||
documentUri: '',
|
||||
documentHash: '',
|
||||
decimals: 0,
|
||||
mintBatonVout: 2,
|
||||
qty: '10000000'
|
||||
}
|
||||
|
||||
const mintTestOpReturnData01 = {
|
||||
tokenType: 1,
|
||||
txType: 'SEND',
|
||||
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
amounts: ['4354768657']
|
||||
}
|
||||
|
||||
const mintTestOpReturnData02 = {
|
||||
tokenType: 1,
|
||||
txType: 'GENESIS',
|
||||
ticker: 'Bubb2',
|
||||
name: 'the new bubbles!',
|
||||
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
documentUri: '',
|
||||
documentHash: '',
|
||||
decimals: 5,
|
||||
mintBatonVout: 2,
|
||||
qty: '4354534534'
|
||||
}
|
||||
|
||||
const mintTestOpReturnData03 = {
|
||||
tokenType: 1,
|
||||
txType: 'MINT',
|
||||
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
mintBatonVout: 2,
|
||||
qty: '234123'
|
||||
}
|
||||
|
||||
const sendTestInputTx01 = {
|
||||
txid: '6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a',
|
||||
hash: '6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a',
|
||||
version: 1,
|
||||
size: 627,
|
||||
locktime: 543956,
|
||||
vin: [
|
||||
{
|
||||
txid: 'b36b0c7485ad569b98cc9b9614dc68a5208495f22ec3b00effcf963b135a5215',
|
||||
vout: 1,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3045022100934ca9732bf6c1b146f09e6198142a79a80788b576c6b94e2052df5bcdc86ad902205ef6205192200ea93f6c97f966b838ee785529b694eb61e576e3452470613847[ALL|FORKID] 02b23484341cf36ec00184e045d54e6ffca97da7a8f8a6fd719cead192132306b1',
|
||||
hex:
|
||||
'483045022100934ca9732bf6c1b146f09e6198142a79a80788b576c6b94e2052df5bcdc86ad902205ef6205192200ea93f6c97f966b838ee785529b694eb61e576e3452470613847412102b23484341cf36ec00184e045d54e6ffca97da7a8f8a6fd719cead192132306b1'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qqxj7p2jatt8h3tcpadyxw8a2mr7myqk2qm57u4rdf',
|
||||
value: 0.00000546
|
||||
},
|
||||
{
|
||||
txid: 'b36b0c7485ad569b98cc9b9614dc68a5208495f22ec3b00effcf963b135a5215',
|
||||
vout: 3,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3044022004d4a95658ef140561ae3a936fb01511778a5376a499bcd6f47b64074a6ca4430220273a4bf553ca17c7e10f393582f980e17a6a98ada867633faad9dbcc595dfd23[ALL|FORKID] 0324fe3b895cfa09523e8ec529eb15c681e3a20f96df85175c54a952b68ac018df',
|
||||
hex:
|
||||
'473044022004d4a95658ef140561ae3a936fb01511778a5376a499bcd6f47b64074a6ca4430220273a4bf553ca17c7e10f393582f980e17a6a98ada867633faad9dbcc595dfd2341210324fe3b895cfa09523e8ec529eb15c681e3a20f96df85175c54a952b68ac018df'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qrcdncgpnv3s8lekcamv4yekp8sghnk37qtsxdyzla',
|
||||
value: 0.00153976
|
||||
},
|
||||
{
|
||||
txid: '95d460512143b636bbc5780d8b27b04fca3bfd2f22003ab48da594e2bab9cfc1',
|
||||
vout: 2,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3044022079f239216f8d806a084221e841227e55d23a1b90aaf5b5ab54d6f865fbeacf8b022053568f10afc00459af083e4c77d0b85229eb8973a1de406aaa699557f05c7fed[ALL|FORKID] 02035f98ff79d6f3a8ec5a2738c103bbb7796e94fcaffdfaae8f5b7a7fae5d93e3',
|
||||
hex:
|
||||
'473044022079f239216f8d806a084221e841227e55d23a1b90aaf5b5ab54d6f865fbeacf8b022053568f10afc00459af083e4c77d0b85229eb8973a1de406aaa699557f05c7fed412102035f98ff79d6f3a8ec5a2738c103bbb7796e94fcaffdfaae8f5b7a7fae5d93e3'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qrcukahsm53lsywcdarehh73vqazpdu8zv547nym9g',
|
||||
value: 0.00000546
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 1145980243 550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35 00005af3107a4000 004657febe8d8000',
|
||||
hex:
|
||||
'6a04534c500001010453454e4420550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b350800005af3107a400008004657febe8d8000',
|
||||
type: 'nulldata'
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 268dadd7c7becc11782ad3d956342442d8eae92a OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914268dadd7c7becc11782ad3d956342442d8eae92a88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qqngmtwhc7lvcytc9tfaj435y3pd36hf9gtdq8nsuu']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 2,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 9fb45b370f9ff38a775ee95cabcad7615817924e OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a9149fb45b370f9ff38a775ee95cabcad7615817924e88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qz0mgkehp70l8znhtm54e2726as4s9ujfc9s2szctr']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00153347,
|
||||
n: 3,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 9fb45b370f9ff38a775ee95cabcad7615817924e OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a9149fb45b370f9ff38a775ee95cabcad7615817924e88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qz0mgkehp70l8znhtm54e2726as4s9ujfc9s2szctr']
|
||||
}
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'010000000315525a133b96cfff0eb0c32ef2958420a568dc14969bcc989b56ad85740c6bb3010000006b483045022100934ca9732bf6c1b146f09e6198142a79a80788b576c6b94e2052df5bcdc86ad902205ef6205192200ea93f6c97f966b838ee785529b694eb61e576e3452470613847412102b23484341cf36ec00184e045d54e6ffca97da7a8f8a6fd719cead192132306b1feffffff15525a133b96cfff0eb0c32ef2958420a568dc14969bcc989b56ad85740c6bb3030000006a473044022004d4a95658ef140561ae3a936fb01511778a5376a499bcd6f47b64074a6ca4430220273a4bf553ca17c7e10f393582f980e17a6a98ada867633faad9dbcc595dfd2341210324fe3b895cfa09523e8ec529eb15c681e3a20f96df85175c54a952b68ac018dffeffffffc1cfb9bae294a58db43a00222ffd3bca4fb0278b0d78c5bb36b643215160d495020000006a473044022079f239216f8d806a084221e841227e55d23a1b90aaf5b5ab54d6f865fbeacf8b022053568f10afc00459af083e4c77d0b85229eb8973a1de406aaa699557f05c7fed412102035f98ff79d6f3a8ec5a2738c103bbb7796e94fcaffdfaae8f5b7a7fae5d93e3feffffff040000000000000000406a04534c500001010453454e4420550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b350800005af3107a400008004657febe8d800022020000000000001976a914268dadd7c7becc11782ad3d956342442d8eae92a88ac22020000000000001976a9149fb45b370f9ff38a775ee95cabcad7615817924e88ac03570200000000001976a9149fb45b370f9ff38a775ee95cabcad7615817924e88acd44c0800',
|
||||
blockhash: '000000000000000001882548bbd9a41bc53a039634f2afa4a12d653434347d04',
|
||||
confirmations: 166670,
|
||||
time: 1534593905,
|
||||
blocktime: 1534593905
|
||||
}
|
||||
|
||||
const sendTestOpReturnData01 = {
|
||||
tokenType: 1,
|
||||
txType: 'SEND',
|
||||
tokenId: '550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35',
|
||||
amounts: ['100000000000000', '19800000000000000']
|
||||
}
|
||||
|
||||
const sendTestOpReturnData02 = {
|
||||
tokenType: 1,
|
||||
txType: 'GENESIS',
|
||||
ticker: 'USDT',
|
||||
name: 'Tether Ltd. US dollar backed tokens',
|
||||
tokenId: '550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35',
|
||||
documentUri:
|
||||
'https://tether.to/wp-content/uploads/2016/06/TetherWhitePaper.pdf',
|
||||
documentHash:
|
||||
'�DQ�\u001e�3�\u0006p���pM�\u0001\u0017�pW(;\u0003,��wy19\u0016',
|
||||
decimals: 8,
|
||||
mintBatonVout: 2,
|
||||
qty: '10000000000000000'
|
||||
}
|
||||
|
||||
const sendTestOpReturnData03 = {
|
||||
tokenType: 1,
|
||||
txType: 'MINT',
|
||||
tokenId: '550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35',
|
||||
mintBatonVout: 2,
|
||||
qty: '10000000000000000'
|
||||
}
|
||||
|
||||
const sendTestOpReturnData04 = {
|
||||
tokenType: 1,
|
||||
txType: 'SEND',
|
||||
tokenId: '550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35',
|
||||
amounts: ['100000000000000', '9900000000000000']
|
||||
}
|
||||
|
||||
const mintTestInputTx02 = {
|
||||
txid: 'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34',
|
||||
hash: 'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34',
|
||||
version: 1,
|
||||
size: 473,
|
||||
locktime: 543613,
|
||||
vin: [
|
||||
{
|
||||
txid: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
vout: 3,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3045022100ac43a871e0f84787001db745e4801b789fd9cd7efa247727ebb5c0c7b66d597302205316328b6501759e9a15ec65202219a433172d153f9f1b68f65f3a63494ba6f0[ALL|FORKID] 0345a5f5dd11c987b346c18c245f0df6d305369984b9b3c7ba465c29875466e374',
|
||||
hex:
|
||||
'483045022100ac43a871e0f84787001db745e4801b789fd9cd7efa247727ebb5c0c7b66d597302205316328b6501759e9a15ec65202219a433172d153f9f1b68f65f3a63494ba6f041210345a5f5dd11c987b346c18c245f0df6d305369984b9b3c7ba465c29875466e374'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qqj0durd4qzdddvl2u6sen8n8h6mljtywugqctt9km',
|
||||
value: 0.00056266
|
||||
},
|
||||
{
|
||||
txid: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
vout: 2,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'304402202ce2d9d63bc6ab7765d267540fd3429bf7a8d1b50e975391fff3a4506289461a02205daf3f761a9b02d141f22136a6fe019062182c9da1b1d3a5245ff745f1236e45[ALL|FORKID] 02c00b05bc633a48e074bc54dd195b19a470ad6252ac8d306c82a2bd729c1c4c69',
|
||||
hex:
|
||||
'47304402202ce2d9d63bc6ab7765d267540fd3429bf7a8d1b50e975391fff3a4506289461a02205daf3f761a9b02d141f22136a6fe019062182c9da1b1d3a5245ff745f1236e45412102c00b05bc633a48e074bc54dd195b19a470ad6252ac8d306c82a2bd729c1c4c69'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qzf72qccdgjrpjpewq8dazlze6jyxzku8q3cauna4t',
|
||||
value: 0.00000546
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 1414416717 938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8 2 000000000003928b',
|
||||
hex:
|
||||
'6a04534c50000101044d494e5420938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8010208000000000003928b',
|
||||
type: 'nulldata'
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 1272884e6bd20c4661b5a052f874ff9a14c925b2 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a9141272884e6bd20c4661b5a052f874ff9a14c925b288ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qqf89zzwd0fqc3npkks997r5l7dpfjf9kgx2rqu500']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 2,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 d1eaebf8d1face5bd866e36bd94f43e2ffceef71 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914d1eaebf8d1face5bd866e36bd94f43e2ffceef7188ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qrg746lc68avuk7cvm3khk20g030lnh0wy5h2k2fqr']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00054848,
|
||||
n: 3,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 17178e31b3e468c0d98d57be07bc907bf94d1fdb OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a91417178e31b3e468c0d98d57be07bc907bf94d1fdb88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qqt30r33k0jx3sxe34tmupaujpaljnglmvqgrrfp2x']
|
||||
}
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'0100000002f8cda53b43b0ea49991a9bc6c74eb901d2a8b964bc7b8987d76789618ec18c93030000006b483045022100ac43a871e0f84787001db745e4801b789fd9cd7efa247727ebb5c0c7b66d597302205316328b6501759e9a15ec65202219a433172d153f9f1b68f65f3a63494ba6f041210345a5f5dd11c987b346c18c245f0df6d305369984b9b3c7ba465c29875466e374fefffffff8cda53b43b0ea49991a9bc6c74eb901d2a8b964bc7b8987d76789618ec18c93020000006a47304402202ce2d9d63bc6ab7765d267540fd3429bf7a8d1b50e975391fff3a4506289461a02205daf3f761a9b02d141f22136a6fe019062182c9da1b1d3a5245ff745f1236e45412102c00b05bc633a48e074bc54dd195b19a470ad6252ac8d306c82a2bd729c1c4c69feffffff040000000000000000396a04534c50000101044d494e5420938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8010208000000000003928b22020000000000001976a9141272884e6bd20c4661b5a052f874ff9a14c925b288ac22020000000000001976a914d1eaebf8d1face5bd866e36bd94f43e2ffceef7188ac40d60000000000001976a91417178e31b3e468c0d98d57be07bc907bf94d1fdb88ac7d4b0800',
|
||||
blockhash: '00000000000000000140f0d813052da59c811a936494a8d8b2d60b215d19e3dc',
|
||||
confirmations: 168436,
|
||||
time: 1534391953,
|
||||
blocktime: 1534391953
|
||||
}
|
||||
|
||||
const mintTestOpReturnData04 = {
|
||||
tokenType: 1,
|
||||
txType: 'MINT',
|
||||
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
mintBatonVout: 2,
|
||||
qty: '234123'
|
||||
}
|
||||
|
||||
const mintTestOpReturnData05 = {
|
||||
tokenType: 1,
|
||||
txType: 'GENESIS',
|
||||
ticker: 'Bubb2',
|
||||
name: 'the new bubbles!',
|
||||
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
|
||||
documentUri: '',
|
||||
documentHash: '',
|
||||
decimals: 5,
|
||||
mintBatonVout: 2,
|
||||
qty: '4354534534'
|
||||
}
|
||||
|
||||
const genesisTestInputTx02 = {
|
||||
txid: '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf',
|
||||
hash: '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf',
|
||||
version: 1,
|
||||
size: 444,
|
||||
locktime: 571211,
|
||||
vin: [
|
||||
{
|
||||
txid: 'f3e056874846b6f491a1ccac3da81a3411c61d8466835446a52efa68c0d11804',
|
||||
vout: 0,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3044022022fb67bd9b35810db3fa04da4014cc93063c5ed15e8febeca09d32c03397068502206fbb86498919ca8a9be6940c904c944cd923691391a4a7d92e0c841605d46b2e[ALL|FORKID] 03fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bf',
|
||||
hex:
|
||||
'473044022022fb67bd9b35810db3fa04da4014cc93063c5ed15e8febeca09d32c03397068502206fbb86498919ca8a9be6940c904c944cd923691391a4a7d92e0c841605d46b2e412103fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bf'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qz6rcxjw34yuy5fngu7fngnyy55t0v802cmsmjvsp2',
|
||||
value: 0.00001111
|
||||
},
|
||||
{
|
||||
txid: '87bf0efc1630f5179b73496f9f885b141cb4c8366432a91ba678298432b197ea',
|
||||
vout: 0,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3045022100d5fbab461c19c7b56245287eb15510c86872ceb405ede7322f5266efa6cc32ea022074b557bcef9a129fb31cb2e7c2ada9fe51d5cf924ae55d1d2a29368ca1f15ef2[ALL|FORKID] 03fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bf',
|
||||
hex:
|
||||
'483045022100d5fbab461c19c7b56245287eb15510c86872ceb405ede7322f5266efa6cc32ea022074b557bcef9a129fb31cb2e7c2ada9fe51d5cf924ae55d1d2a29368ca1f15ef2412103fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bf'
|
||||
},
|
||||
sequence: 4294967294,
|
||||
address: 'bitcoincash:qz6rcxjw34yuy5fngu7fngnyy55t0v802cmsmjvsp2',
|
||||
value: 0.007
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 47454e45534953 5350494345 5370696365 7370696365736c7040676d61696c2e636f6d 0 8 0 016345785d8a0000',
|
||||
hex:
|
||||
'6a04534c500001010747454e45534953055350494345055370696365127370696365736c7040676d61696c2e636f6d4c0001084c0008016345785d8a0000',
|
||||
type: 'nulldata'
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 70e5c384ae563b4fd6cfc5f7f8df43ea427d9e10 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a91470e5c384ae563b4fd6cfc5f7f8df43ea427d9e1088ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qpcwtsuy4etrkn7kelzl07xlg04yylv7zqru2cqwvm']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.0070012,
|
||||
n: 2,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 df305a87d98cd64e1bee825d2167a16445ecdfdd OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914df305a87d98cd64e1bee825d2167a16445ecdfdd88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qr0nqk58mxxdvnsma6p96gt859jytmxlm5t0v7de4q']
|
||||
}
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'01000000020418d1c068fa2ea546548366841dc611341aa83daccca191f4b646488756e0f3000000006a473044022022fb67bd9b35810db3fa04da4014cc93063c5ed15e8febeca09d32c03397068502206fbb86498919ca8a9be6940c904c944cd923691391a4a7d92e0c841605d46b2e412103fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bffeffffffea97b132842978a61ba9326436c8b41c145b889f6f49739b17f53016fc0ebf87000000006b483045022100d5fbab461c19c7b56245287eb15510c86872ceb405ede7322f5266efa6cc32ea022074b557bcef9a129fb31cb2e7c2ada9fe51d5cf924ae55d1d2a29368ca1f15ef2412103fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bffeffffff0300000000000000003e6a04534c500001010747454e45534953055350494345055370696365127370696365736c7040676d61696c2e636f6d4c0001084c0008016345785d8a000022020000000000001976a91470e5c384ae563b4fd6cfc5f7f8df43ea427d9e1088acd8ae0a00000000001976a914df305a87d98cd64e1bee825d2167a16445ecdfdd88ac4bb70800',
|
||||
blockhash: '000000000000000002278c188c52b536d4ed84a1970d8ff59043e082cef7748a',
|
||||
confirmations: 141226,
|
||||
time: 1551042875,
|
||||
blocktime: 1551042875
|
||||
}
|
||||
|
||||
const genesisTestOpReturn03 = {
|
||||
tokenType: 1,
|
||||
txType: 'GENESIS',
|
||||
ticker: 'SPICE',
|
||||
name: 'Spice',
|
||||
tokenId: '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf',
|
||||
documentUri: 'spiceslp@gmail.com',
|
||||
documentHash: '',
|
||||
decimals: 8,
|
||||
mintBatonVout: 0,
|
||||
qty: '100000000000000000'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
nonSlpTxDetails,
|
||||
slpTxDetails,
|
||||
mockOpReturnData01,
|
||||
mockOpReturnData02,
|
||||
mockOpReturnData03
|
||||
mockOpReturnData03,
|
||||
genesisTestInputTx,
|
||||
mintTestInputTx,
|
||||
genesisTestOpReturnData01,
|
||||
genesisTestOpReturnData02,
|
||||
mintTestOpReturnData01,
|
||||
mintTestOpReturnData02,
|
||||
mintTestOpReturnData03,
|
||||
sendTestInputTx01,
|
||||
sendTestOpReturnData01,
|
||||
sendTestOpReturnData02,
|
||||
sendTestOpReturnData03,
|
||||
sendTestOpReturnData04,
|
||||
mintTestInputTx02,
|
||||
mintTestOpReturnData04,
|
||||
mintTestOpReturnData05,
|
||||
genesisTestInputTx02,
|
||||
genesisTestOpReturn03
|
||||
}
|
||||
|
||||
@@ -171,10 +171,15 @@ describe('#RawTransactions', () => {
|
||||
describe('#getTxData', () => {
|
||||
it('should return tx data with input addresses', async () => {
|
||||
// Mock dependencies
|
||||
sandbox.stub(bchjs.RawTransactions, 'getRawTransaction').resolves(mockData.mockTx)
|
||||
sandbox.stub(bchjs.RawTransactions, '_getInputAddrs').resolves(mockData.mockGetInputAddrsOutput)
|
||||
sandbox
|
||||
.stub(bchjs.RawTransactions, 'getRawTransaction')
|
||||
.resolves(mockData.mockTx)
|
||||
sandbox
|
||||
.stub(bchjs.RawTransactions, '_getInputAddrs')
|
||||
.resolves(mockData.mockGetInputAddrsOutput)
|
||||
|
||||
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
const txid =
|
||||
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxData(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
@@ -190,7 +195,10 @@ describe('#RawTransactions', () => {
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
assert2.include(err.message, 'Input must be a string or array of strings')
|
||||
assert2.include(
|
||||
err.message,
|
||||
'Input to raw-transaction.js/getTxData() must be a string containg a TXID.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -201,7 +209,8 @@ describe('#RawTransactions', () => {
|
||||
.stub(bchjs.RawTransactions, 'getRawTransaction')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
const txid =
|
||||
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
|
||||
|
||||
await bchjs.RawTransactions.getTxData(txid)
|
||||
|
||||
|
||||
+93
-2
@@ -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,4 +253,95 @@ 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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+20
-18
@@ -10,7 +10,7 @@ let uut
|
||||
|
||||
const REST_URL = process.env.RESTURL
|
||||
? process.env.RESTURL
|
||||
: 'https://bchn.fullstack.cash/v4/'
|
||||
: 'https://bchn.fullstack.cash/v5/'
|
||||
|
||||
// Mock data used for unit tests
|
||||
const mockDataLib = require('./fixtures/slp/mock-utils')
|
||||
@@ -2281,23 +2281,25 @@ describe('#SLP Utils', () => {
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('catches and throws an error', async () => {
|
||||
try {
|
||||
const txid = 'fakeTxid'
|
||||
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(uut.Utils, 'validateTxid3')
|
||||
.rejects(new Error('fake error'))
|
||||
|
||||
await await uut.Utils.waterfallValidateTxid(txid)
|
||||
|
||||
assert.fail('Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'fake error')
|
||||
}
|
||||
})
|
||||
// CT 08-09-21 Changed behavior. validateTxid3() is now wrapped in a
|
||||
// try/catch statement that exits quietly if there is an error.
|
||||
// it('catches and throws an error', async () => {
|
||||
// try {
|
||||
// const txid = 'fakeTxid'
|
||||
//
|
||||
// // Force an error
|
||||
// sandbox
|
||||
// .stub(uut.Utils, 'validateTxid3')
|
||||
// .rejects(new Error('fake error'))
|
||||
//
|
||||
// await await uut.Utils.waterfallValidateTxid(txid)
|
||||
//
|
||||
// assert.fail('Unexpected result')
|
||||
// } catch (err) {
|
||||
// // console.log(`err.message: ${err.message}`)
|
||||
// assert.include(err.message, 'fake error')
|
||||
// }
|
||||
// })
|
||||
})
|
||||
|
||||
describe('#getWhitelist', () => {
|
||||
|
||||
@@ -5,15 +5,21 @@
|
||||
// Public npm libraries
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
const cloneDeep = require('lodash.clonedeep')
|
||||
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
const mockData = require('./fixtures/transaction-mock.js')
|
||||
const mockDataLib = require('./fixtures/transaction-mock.js')
|
||||
|
||||
describe('#TransactionLib', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
let sandbox, mockData
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
mockData = cloneDeep(mockDataLib)
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#get', () => {
|
||||
@@ -25,7 +31,7 @@ describe('#TransactionLib', () => {
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Input must be a string or array of strings.'
|
||||
'Input to Transaction.get() must be a string containing a TXID.'
|
||||
)
|
||||
}
|
||||
})
|
||||
@@ -115,5 +121,588 @@ describe('#TransactionLib', () => {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
// This test case was created in response to a bug. When the input TX
|
||||
// was a Genesis SLP transaction, the inputs of the transaction were not
|
||||
// being hydrated properly.
|
||||
it('should get input details when input is a genesis tx', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.genesisTestInputTx)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||
.onCall(0)
|
||||
.resolves(mockData.genesisTestOpReturnData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.genesisTestOpReturnData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.genesisTestOpReturnData02)
|
||||
.onCall(3)
|
||||
.resolves(mockData.genesisTestOpReturnData02)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
|
||||
.resolves(true)
|
||||
|
||||
const txid =
|
||||
'874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert that added properties exist.
|
||||
assert.property(result.vout[0], 'tokenQty')
|
||||
assert.equal(result.vout[0].tokenQty, null)
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'value')
|
||||
assert.property(result.vin[0], 'tokenQty')
|
||||
assert.property(result, 'isValidSLPTx')
|
||||
assert.equal(result.isValidSLPTx, true)
|
||||
|
||||
// Assert inputs values unique to a Genesis input have the proper values.
|
||||
assert.equal(result.vin[0].tokenQty, 10000000)
|
||||
assert.equal(result.vin[1].tokenQty, null)
|
||||
})
|
||||
|
||||
it('should get input details when input is a mint tx', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.mintTestInputTx)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||
.onCall(0)
|
||||
.resolves(mockData.mintTestOpReturnData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.mintTestOpReturnData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.mintTestOpReturnData02)
|
||||
.onCall(3)
|
||||
.resolves(mockData.mintTestOpReturnData03)
|
||||
.onCall(4)
|
||||
.resolves(mockData.mintTestOpReturnData03)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
|
||||
.resolves(true)
|
||||
|
||||
const txid =
|
||||
'4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert that added properties exist.
|
||||
assert.property(result.vout[0], 'tokenQty')
|
||||
assert.equal(result.vout[0].tokenQty, null)
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'value')
|
||||
assert.property(result.vin[0], 'tokenQty')
|
||||
assert.property(result, 'isValidSLPTx')
|
||||
assert.equal(result.isValidSLPTx, true)
|
||||
|
||||
// Assert inputs values unique to a Mint input have the proper values.
|
||||
assert.equal(result.vin[0].tokenQty, 43545.34534)
|
||||
assert.equal(result.vin[1].tokenQty, 2.34123)
|
||||
assert.equal(result.vin[2].tokenQty, null)
|
||||
})
|
||||
|
||||
// This test case was generated from the problematic transaction that
|
||||
// used inputs in a 'non-standard' way.
|
||||
it('should correctly assign quantities to mixed mint inputs', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.sendTestInputTx01)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||
.onCall(0)
|
||||
.resolves(mockData.sendTestOpReturnData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.sendTestOpReturnData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.sendTestOpReturnData03)
|
||||
.onCall(3)
|
||||
.resolves(mockData.sendTestOpReturnData03)
|
||||
.onCall(4)
|
||||
.resolves(mockData.sendTestOpReturnData04)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
|
||||
.resolves(true)
|
||||
|
||||
const txid =
|
||||
'6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert that added properties exist.
|
||||
assert.property(result.vout[0], 'tokenQty')
|
||||
assert.equal(result.vout[0].tokenQty, null)
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'value')
|
||||
assert.property(result.vin[0], 'tokenQty')
|
||||
assert.property(result, 'isValidSLPTx')
|
||||
assert.equal(result.isValidSLPTx, true)
|
||||
|
||||
// Assert inputs values unique to a Mint input have the proper values.
|
||||
assert.equal(result.vin[0].tokenQty, 100000000)
|
||||
assert.equal(result.vin[1].tokenQty, null)
|
||||
assert.equal(result.vin[2].tokenQty, 99000000)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#get3', () => {
|
||||
it('should throw an error if txid is not specified', async () => {
|
||||
try {
|
||||
await bchjs.Transaction.get3()
|
||||
|
||||
assert.fail('Unexpected code path!')
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Input to Transaction.get() must be a string containing a TXID.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should get details about a non-SLP transaction', async () => {
|
||||
const txid =
|
||||
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
|
||||
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.nonSlpTxDetails)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
|
||||
.resolves({ height: 602405 })
|
||||
sandbox.stub(bchjs.Transaction, 'getTokenInfo').resolves(false)
|
||||
|
||||
const result = await bchjs.Transaction.get3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[0].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert that added properties exist.
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'value')
|
||||
|
||||
// Assert blockheight is added
|
||||
assert.equal(result.blockheight, 602405)
|
||||
assert.equal(result.isSlpTx, false)
|
||||
})
|
||||
|
||||
it('should get details about a SLP SEND tx with SEND input', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.slpTxDetails)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
|
||||
.resolves({ height: 603424 })
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||
.onCall(0)
|
||||
.resolves(mockData.mockOpReturnData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.mockOpReturnData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.mockOpReturnData03)
|
||||
.onCall(3)
|
||||
.rejects(new Error('No OP_RETURN'))
|
||||
|
||||
const txid =
|
||||
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
|
||||
|
||||
const result = await bchjs.Transaction.get3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert outputs have expected properties
|
||||
assert.equal(result.vout[0].tokenQty, null)
|
||||
assert.equal(result.vout[0].tokenQtyStr, null)
|
||||
assert.equal(result.vout[1].tokenQty, 1)
|
||||
assert.equal(result.vout[1].tokenQtyStr, '1')
|
||||
assert.equal(result.vout[2].tokenQty, 998833)
|
||||
assert.equal(result.vout[2].tokenQtyStr, '998833')
|
||||
assert.equal(result.vout[3].tokenQty, null)
|
||||
assert.equal(result.vout[3].tokenQtyStr, null)
|
||||
|
||||
// Assert that inputs have expected properties
|
||||
assert.equal(result.vin[0].tokenQtyStr, '998834')
|
||||
assert.equal(result.vin[0].tokenQty, 998834)
|
||||
assert.equal(
|
||||
result.vin[0].tokenId,
|
||||
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
|
||||
)
|
||||
assert.equal(result.vin[1].tokenQtyStr, '0')
|
||||
assert.equal(result.vin[1].tokenQty, 0)
|
||||
assert.equal(result.vin[1].tokenId, null)
|
||||
|
||||
// Assert blockheight is added
|
||||
assert.equal(result.blockheight, 603424)
|
||||
assert.equal(result.isSlpTx, true)
|
||||
})
|
||||
|
||||
it('should catch and throw error on network error', async () => {
|
||||
try {
|
||||
const txid =
|
||||
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
|
||||
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
await bchjs.Transaction.get3(txid)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
// This test case was created in response to a bug. When the input TX
|
||||
// was a Genesis SLP transaction, the inputs of the transaction were not
|
||||
// being hydrated properly.
|
||||
it('should get details about a SLP SEND tx with GENSIS input', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.genesisTestInputTx)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
|
||||
.resolves({ height: 543409 })
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||
.onCall(0)
|
||||
.resolves(mockData.genesisTestOpReturnData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.genesisTestOpReturnData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.genesisTestOpReturnData02)
|
||||
.onCall(3)
|
||||
.resolves(mockData.genesisTestOpReturnData02)
|
||||
|
||||
const txid =
|
||||
'874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e'
|
||||
|
||||
const result = await bchjs.Transaction.get3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert outputs have expected properties and values
|
||||
assert.equal(result.vout[0].tokenQty, null)
|
||||
assert.equal(result.vout[0].tokenQtyStr, null)
|
||||
assert.equal(result.vout[1].tokenQty, 5000000)
|
||||
assert.equal(result.vout[1].tokenQtyStr, '5000000')
|
||||
assert.equal(result.vout[2].tokenQty, 5000000)
|
||||
assert.equal(result.vout[2].tokenQtyStr, '5000000')
|
||||
assert.equal(result.vout[3].tokenQty, null)
|
||||
assert.equal(result.vout[3].tokenQtyStr, null)
|
||||
|
||||
// Assert inputs have expected properties and values
|
||||
assert.equal(result.vin[0].tokenQty, 10000000)
|
||||
assert.equal(result.vin[0].tokenQtyStr, '10000000')
|
||||
assert.equal(
|
||||
result.vin[0].tokenId,
|
||||
'323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35'
|
||||
)
|
||||
assert.equal(result.vin[1].tokenQty, 0)
|
||||
assert.equal(result.vin[1].tokenQtyStr, '0')
|
||||
assert.equal(result.vin[1].tokenId, null)
|
||||
|
||||
// Assert blockheight is added
|
||||
assert.equal(result.blockheight, 543409)
|
||||
assert.equal(result.isSlpTx, true)
|
||||
})
|
||||
|
||||
it('should get details about a SLP SEND tx with MINT (and GENESIS) input', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.mintTestInputTx)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
|
||||
.resolves({ height: 543614 })
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||
.onCall(0)
|
||||
.resolves(mockData.mintTestOpReturnData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.mintTestOpReturnData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.mintTestOpReturnData02)
|
||||
.onCall(3)
|
||||
.resolves(mockData.mintTestOpReturnData03)
|
||||
.onCall(4)
|
||||
.resolves(mockData.mintTestOpReturnData03)
|
||||
|
||||
const txid =
|
||||
'4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa'
|
||||
|
||||
const result = await bchjs.Transaction.get3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert expected output properties and values exist.
|
||||
assert.equal(result.vout[0].tokenQty, null)
|
||||
assert.equal(result.vout[1].tokenQty, 43547.68657)
|
||||
assert.equal(result.vout[1].tokenQtyStr, '43547.68657')
|
||||
assert.equal(result.vout[2].tokenQty, null)
|
||||
|
||||
// Assert expected input properties and values exist.
|
||||
assert.equal(result.vin[0].tokenQty, 43545.34534)
|
||||
assert.equal(result.vin[0].tokenQtyStr, '43545.34534')
|
||||
assert.equal(
|
||||
result.vin[0].tokenId,
|
||||
'938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8'
|
||||
)
|
||||
assert.equal(result.vin[1].tokenQty, 2.34123)
|
||||
assert.equal(result.vin[1].tokenQtyStr, '2.34123')
|
||||
assert.equal(
|
||||
result.vin[1].tokenId,
|
||||
'938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8'
|
||||
)
|
||||
assert.equal(result.vin[2].tokenQty, 0)
|
||||
assert.equal(result.vin[2].tokenQtyStr, '0')
|
||||
assert.equal(result.vin[2].tokenId, null)
|
||||
|
||||
// Assert blockheight is added
|
||||
assert.equal(result.blockheight, 543614)
|
||||
assert.equal(result.isSlpTx, true)
|
||||
})
|
||||
|
||||
// This test case was generated from the problematic transaction that
|
||||
// used inputs in a 'non-standard' way.
|
||||
it('should get details about a SLP SEND tx with MINT (and SEND) input', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.sendTestInputTx01)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
|
||||
.resolves({ height: 543957 })
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||
.onCall(0)
|
||||
.resolves(mockData.sendTestOpReturnData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.sendTestOpReturnData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.sendTestOpReturnData03)
|
||||
.onCall(3)
|
||||
.resolves(mockData.sendTestOpReturnData03)
|
||||
.onCall(4)
|
||||
.resolves(mockData.sendTestOpReturnData04)
|
||||
|
||||
const txid =
|
||||
'6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a'
|
||||
|
||||
const result = await bchjs.Transaction.get3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert the outputs have expected properties and values.
|
||||
assert.equal(result.vout[0].tokenQty, null)
|
||||
assert.equal(result.vout[1].tokenQty, 1000000)
|
||||
assert.equal(result.vout[1].tokenQtyStr, '1000000')
|
||||
assert.equal(result.vout[2].tokenQty, 198000000)
|
||||
assert.equal(result.vout[2].tokenQtyStr, '198000000')
|
||||
assert.equal(result.vout[3].tokenQty, null)
|
||||
|
||||
// Assert the inputs have expected properties and values.
|
||||
assert.equal(result.vin[0].tokenQty, 100000000)
|
||||
assert.equal(result.vin[0].tokenQtyStr, '100000000')
|
||||
assert.equal(
|
||||
result.vin[0].tokenId,
|
||||
'550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35'
|
||||
)
|
||||
assert.equal(result.vin[1].tokenQty, 0)
|
||||
assert.equal(result.vin[1].tokenQtyStr, 0)
|
||||
assert.equal(result.vin[1].tokenId, null)
|
||||
assert.equal(result.vin[2].tokenQty, 99000000)
|
||||
assert.equal(result.vin[2].tokenQtyStr, '99000000')
|
||||
assert.equal(
|
||||
result.vin[2].tokenId,
|
||||
'550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35'
|
||||
)
|
||||
|
||||
// Assert blockheight is added
|
||||
assert.equal(result.blockheight, 543957)
|
||||
assert.equal(result.isSlpTx, true)
|
||||
})
|
||||
|
||||
// This was a problematic TX
|
||||
it('should process MINT TX with GENESIS input', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.mintTestInputTx02)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
|
||||
.resolves({ height: 543614 })
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
|
||||
.onCall(0)
|
||||
.resolves(mockData.mintTestOpReturnData04)
|
||||
.onCall(1)
|
||||
.resolves(mockData.mintTestOpReturnData05)
|
||||
.onCall(2)
|
||||
.resolves(mockData.mintTestOpReturnData05)
|
||||
.onCall(3)
|
||||
.resolves(mockData.mintTestOpReturnData05)
|
||||
.onCall(4)
|
||||
.resolves(mockData.mintTestOpReturnData05)
|
||||
.onCall(5)
|
||||
.resolves(mockData.mintTestOpReturnData05)
|
||||
|
||||
const txid =
|
||||
'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34'
|
||||
|
||||
const result = await bchjs.Transaction.get3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert outputs have expected properties and values
|
||||
assert.equal(result.vout[0].tokenQty, 0)
|
||||
assert.equal(result.vout[0].tokenQty, '0')
|
||||
assert.equal(result.vout[1].tokenQty, 2.34123)
|
||||
assert.equal(result.vout[1].tokenQty, '2.34123')
|
||||
assert.equal(result.vout[2].tokenQty, 0)
|
||||
assert.equal(result.vout[2].tokenQty, '0')
|
||||
assert.equal(result.vout[2].isMintBaton, true)
|
||||
assert.equal(result.vout[3].tokenQty, 0)
|
||||
assert.equal(result.vout[3].tokenQty, '0')
|
||||
|
||||
// Assert inputs have expected properties and values
|
||||
assert.equal(result.vin[0].tokenQty, 0)
|
||||
assert.equal(result.vin[0].tokenQtyStr, '0')
|
||||
assert.equal(result.vin[0].tokenId, null)
|
||||
assert.equal(result.vin[1].tokenQty, 0)
|
||||
assert.equal(result.vin[1].tokenQtyStr, '0')
|
||||
assert.equal(
|
||||
result.vin[1].tokenId,
|
||||
'938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8'
|
||||
)
|
||||
assert.equal(result.vin[1].isMintBaton, true)
|
||||
|
||||
// Assert added TX data exists.
|
||||
assert.equal(result.blockheight, 543614)
|
||||
assert.equal(result.isSlpTx, true)
|
||||
})
|
||||
|
||||
// It should process a GENESIS tx
|
||||
it('should process a GENESIS tx', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
|
||||
.resolves(mockData.genesisTestInputTx02)
|
||||
sandbox
|
||||
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
|
||||
.resolves({ height: 571212 })
|
||||
sandbox
|
||||
.stub(bchjs.Transaction, 'getTokenInfo')
|
||||
.onCall(0)
|
||||
.resolves(mockData.genesisTestOpReturn03)
|
||||
.onCall(1)
|
||||
.resolves(mockData.genesisTestOpReturn03)
|
||||
.onCall(2)
|
||||
.resolves(false)
|
||||
.onCall(3)
|
||||
.resolves(false)
|
||||
.onCall(4)
|
||||
.resolves(false)
|
||||
|
||||
const txid =
|
||||
'4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf'
|
||||
|
||||
const result = await bchjs.Transaction.get3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that there are stanardized properties.
|
||||
assert.property(result, 'txid')
|
||||
assert.property(result, 'vin')
|
||||
assert.property(result, 'vout')
|
||||
assert.property(result.vout[0], 'value')
|
||||
assert.property(result.vout[1].scriptPubKey, 'addresses')
|
||||
|
||||
// Assert output have expected properties and values
|
||||
assert.equal(result.vout[0].tokenQty, 0)
|
||||
assert.equal(result.vout[0].tokenQtyStr, '0')
|
||||
assert.equal(result.vout[0].isMintBaton, true)
|
||||
assert.equal(result.vout[1].tokenQty, 1000000000)
|
||||
assert.equal(result.vout[1].tokenQtyStr, '1000000000')
|
||||
assert.equal(result.vout[2].tokenQty, 0)
|
||||
assert.equal(result.vout[2].tokenQtyStr, '0')
|
||||
|
||||
// Assert input have expected properties and values
|
||||
assert.equal(result.vin[0].tokenQty, 0)
|
||||
assert.equal(result.vin[0].tokenQtyStr, '0')
|
||||
assert.equal(result.vin[0].tokenId, null)
|
||||
assert.equal(result.vin[1].tokenQty, 0)
|
||||
assert.equal(result.vin[1].tokenQtyStr, '0')
|
||||
assert.equal(result.vin[1].tokenId, null)
|
||||
|
||||
// Assert added TX data exists.
|
||||
assert.equal(result.blockheight, 571212)
|
||||
assert.equal(result.isSlpTx, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user