mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 00:52:01 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f369411ccb | ||
|
|
0504cf7b8d | ||
|
|
88ae47cade | ||
|
|
30d58bc337 | ||
|
|
651ae3f6ca | ||
|
|
04cd90f0f4 | ||
|
|
552ee31b79 | ||
|
|
affa9619bb | ||
|
|
39bff131af | ||
|
|
a84fecfe41 | ||
|
|
0736c9f2a1 | ||
|
|
aa898b31e7 | ||
|
|
29c3381cc5 | ||
|
|
f7ba6bb3ae | ||
|
|
62056e1ac4 | ||
|
|
a60e2fa2da | ||
|
|
37fe679800 | ||
|
|
4250d841c5 | ||
|
|
31079afc35 | ||
|
|
62b44d5058 | ||
|
|
150295cb43 | ||
|
|
5de2f71ea5 | ||
|
|
50a1d60229 | ||
|
|
f3e25a0338 | ||
|
|
640c582724 | ||
|
|
b472fa2673 | ||
|
|
ea83335fd2 | ||
|
|
3050819784 | ||
|
|
4ca9b1c00b | ||
|
|
bbc1a45a9e | ||
|
|
469d08e2b1 | ||
|
|
dcadad189b | ||
|
|
02f81ee203 | ||
|
|
972bf604d1 | ||
|
|
82f084edec | ||
|
|
4764463fc5 | ||
|
|
857458db57 | ||
|
|
5f8f05d194 | ||
|
|
e455d9efec | ||
|
|
ad4e05c289 | ||
|
|
0c1eda8e5a | ||
|
|
f15bc7e454 | ||
|
|
e9113d864e | ||
|
|
277c9ce0fd | ||
|
|
e0c07d1542 | ||
|
|
56bc942e87 | ||
|
|
34b9cefb65 | ||
|
|
6a35091eec | ||
|
|
02e3ff4dcd | ||
|
|
64f8adea25 | ||
|
|
bcf817f5c7 | ||
|
|
541377e548 | ||
|
|
84b44b299a | ||
|
|
b6456df1f4 | ||
|
|
b1826c04cb | ||
|
|
71755964c8 | ||
|
|
bd1c3b622d | ||
|
|
bdced13230 | ||
|
|
c925bfce46 | ||
|
|
056a47f6b2 | ||
|
|
51038681d3 | ||
|
|
1a2f144baf | ||
|
|
6d70568082 | ||
|
|
0b1cf57852 | ||
|
|
48f7f75c9c | ||
|
|
0bc96fded5 | ||
|
|
9869211572 | ||
|
|
1056c3724c | ||
|
|
6b838ef9cd | ||
|
|
f09573554d | ||
|
|
4a62ae64de | ||
|
|
78de9e390e | ||
|
|
7d8d0c2d69 | ||
|
|
656b0f6809 | ||
|
|
48808aec14 | ||
|
|
d50f4a0d50 | ||
|
|
f09f96d365 | ||
|
|
7f4745f83c | ||
|
|
dd5de1424c | ||
|
|
30604591bd | ||
|
|
aeb1efe23e | ||
|
|
6e43f795d7 | ||
|
|
e07a5b9da0 | ||
|
|
5ada76eace | ||
|
|
e605f6f386 | ||
|
|
eb9d0bea2b | ||
|
|
f185f5de16 | ||
|
|
d795174916 | ||
|
|
1294bc2149 | ||
|
|
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 | ||
|
|
fbac18d456 | ||
|
|
62ae67ceab | ||
|
|
8e64d854cf | ||
|
|
5fe8e515ff | ||
|
|
f11297339b | ||
|
|
61d52c8ebe | ||
|
|
d89bbe0bf6 | ||
|
|
2a1c2500e6 | ||
|
|
8879a5170a | ||
|
|
b29cb7c12b | ||
|
|
69fa10d3a6 | ||
|
|
d16431d165 | ||
|
|
faa2972460 | ||
|
|
5d3ea2b559 | ||
|
|
40fa6440f4 | ||
|
|
ce951afddb | ||
|
|
d2e847d3fb | ||
|
|
fe697455c0 | ||
|
|
25832d52fd |
@@ -4,6 +4,7 @@ wallet-info.txt
|
||||
wallet.json
|
||||
integration-test-sweet.sh
|
||||
integration-test-ss.sh
|
||||
integration-test-bitfinex.sh
|
||||
|
||||
docs/
|
||||
coverage/
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"srcDir": "",
|
||||
"destDir": "",
|
||||
"files": "**/*.js",
|
||||
"command": "npm run lint"
|
||||
}
|
||||
]
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright 2021 Chris Troutner
|
||||
Copyright 2022 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:
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
[](https://www.npmjs.com/package/@psf/bch-js)
|
||||
[](https://npmjs.org/package/@psf/bch-js)
|
||||
[](https://github.com/Permissionless-Software-Foundation/bch-js/blob/master/LICENSE.md)
|
||||
[](https://github.com/feross/standard)
|
||||
[](https://github.com/feross/standard) [](https://gitter.im/Permissionless-Software-Foundation/bch-js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[bch-js](https://www.npmjs.com/package/@psf/bch-js) is a JavaScript npm library for creating web and mobile apps that can interact with the Bitcoin Cash (BCH) blockchains. It can be used for free, but requires an account on [FullStack.cash](https://fullstack.cash) for increased rate limits. Learn more from [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) about Full Stack Bitcoin Cash development.
|
||||
[bch-js](https://www.npmjs.com/package/@psf/bch-js) is a JavaScript npm library for creating web and mobile apps that can interact with the Bitcoin Cash (BCH) and eCash (XEC) blockchains. bch-js contains a toolbox of handy tools, and an easy API for talking with [bch-api REST API](https://github.com/Permissionless-Software-Foundation/bch-api). [FullStack.cash](https://fullstack.cash) offers paid cloud access to bch-api. You can run your own infrastructure by following documentation on [CashStack.info](https://cashstack.info).
|
||||
|
||||
### Quick Start Videos:
|
||||
|
||||
@@ -23,6 +23,7 @@ Here are two YouTube walk-through videos to help you get started:
|
||||
- [FullStack.cash](https://fullstack.cash) - cloud-based infrastructure for application developers.
|
||||
- [FullStack.cash Account](https://fullstack.cash/login) - Get your API key to unlock increased rate limits.
|
||||
- [Permissionless Software Foundation](https://psfoundation.cash) - The organization that maintains this library.
|
||||
- [CashStack.info](https://cashstack.info) - bch-js is part of the Cash Stack, a JavaScript framework for writing web 2 and web 3 business applications.
|
||||
|
||||
### Quick Notes
|
||||
|
||||
@@ -33,23 +34,19 @@ 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
|
||||
### API Key (JWT Token)
|
||||
|
||||
The [bch-api](https://github.com/Permissionless-Software-Foundation/bch-api) REST API hosted by [FullStack.cash](https://fullstack.cash) uses JWT tokens to pay for increased
|
||||
rate limits when interacting with the back end server. See [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) if you want to understand the system-as-a-whole. The JWT token can be fed to bch-js _implicitly_ or _explicitly_.
|
||||
rate limits when interacting with the back end server. See [this article](https://cashstack.info) if you want to understand the system-as-a-whole. The JWT token can be fed to bch-js _implicitly_ or _explicitly_.
|
||||
|
||||
- Implicitly: bch-js will detect your JWT token if you set the `BCHJSTOKEN` environment variable.
|
||||
- Explicitly: You can directly feed in the JWT token with the `apiToken` property when instantiating the library. Here is an example:
|
||||
@@ -57,43 +54,30 @@ 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.
|
||||
})
|
||||
```
|
||||
|
||||
### Gatsby
|
||||
### Gatsby & Web Apps
|
||||
|
||||
bch-js is included in this [gatsby-ipfs-template](https://github.com/Permissionless-Software-Foundation/gatsby-ipfs-template) for building uncensorable web apps that can interact with the blockchain. When building a Gatsby (or other front-end app that uses Webpack), you'll need to add these lines to your `gatsby-node.js` file, as per [this issue](https://github.com/gatsbyjs/gatsby/issues/564):
|
||||
[minimal-slp-wallet](https://www.npmjs.com/package/minimal-slp-wallet) is a minimal wallet 'engine' that incorporates bch-js. It's compiled with Browserify for front end apps.
|
||||
|
||||
```
|
||||
exports.onCreateWebpackConfig = ({ actions }) => {
|
||||
actions.setWebpackConfig({
|
||||
node: {
|
||||
fs: 'empty'
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
[gatsby-theme-bch-wallet](https://github.com/Permissionless-Software-Foundation/gatsby-theme-bch-wallet) is a Gatsby Theme and [bch-wallet-starter](https://github.com/Permissionless-Software-Foundation/bch-wallet-starter) is a Gatsby Starter for building web wallets using minimal-slp-wallet.
|
||||
|
||||
This is because the new IPFS class in bch-js uses the fs library for uploading files, which is not supported by Gatsby.
|
||||
|
||||
We also provide [minimal-slp-wallet-web](https://www.npmjs.com/package/minimal-slp-wallet-web) as a basic Bitcoin Cash wallet with SLP support, for front end projects. bch-js is encapsulated inside the instantiation of the library Class.
|
||||
[This gist](https://gist.github.com/christroutner/6cb9d1b615f3f9363af79723157bc434) shows how to include minimal-slp-wallet into a basic web page without using a framework.
|
||||
|
||||
## Features
|
||||
|
||||
- [ECMAScript 2017 standard JavaScript](https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_-_ECMAScript_2017) used instead of TypeScript. Works
|
||||
natively with node.js v10 or higher.
|
||||
|
||||
- Full SLP tokens support: bch-js has full support for all SLP token functionality, including send, mint, and genesis transactions. It also fully support all aspects of [non-fugible tokans (NFTs)](https://www.youtube.com/watch?v=vvlpYUx6HRs).
|
||||
- Full SLP tokens support: bch-js has full support for all SLP token functionality, including send, mint, and genesis transactions. It also fully supports all aspects of [non-fugible tokans (NFTs)](https://www.youtube.com/watch?v=vvlpYUx6HRs).
|
||||
|
||||
- [Semantic Release](https://github.com/semantic-release/semantic-release) for
|
||||
continuous delivery using semantic versioning.
|
||||
|
||||
- [Greenkeeper](https://greenkeeper.io/) automatic dependency management for
|
||||
automatically maintaining the latest, most secure dependencies.
|
||||
|
||||
- [IPFS uploads](https://ipfs.io) of all files and dependencies, to backup
|
||||
- [IPFS](https://ipfs.io) and [Radicle](https://radicle.xyz) uploads of all files and dependencies, to backup
|
||||
dependencies in case they are ever inaccessible from GitHub or npm.
|
||||
|
||||
## Documentation:
|
||||
@@ -113,11 +97,25 @@ live in the same repository. To generate the documentation:
|
||||
Have questions? Need help? Join our community support
|
||||
[Telegram channel](https://t.me/bch_js_toolkit)
|
||||
|
||||
## IPFS Releases
|
||||
## Donate
|
||||
|
||||
This open source software is developed and maintained by the [Permissionless Software Foundation](https://psfoundation.cash). If this library provides value to you, please consider making a donation to support the PSF developers:
|
||||
|
||||
<div align="center">
|
||||
<img src="./img/donation-qr.png" />
|
||||
<p>bitcoincash:qqsrke9lh257tqen99dkyy2emh4uty0vky9y0z0lsr</p>
|
||||
</div>
|
||||
|
||||
|
||||
## IPFS & Radicle Releases
|
||||
|
||||
Copies of this repository are also published on [IPFS](https://ipfs.io).
|
||||
|
||||
- v4.5.4: QmWv3pxJy3MH8vU5nLUVyzqFxfNKXLQQEnz1rgStNuQijd
|
||||
- v6.2.10: `bafybeifsioj3ba77u2763nsyuzq53gtbdxsnqpoipvdl4immj6ytznjaoy`
|
||||
- (with dependencies, node v14.18.2 and npm v8.8.0): `bafybeihfendd4oj6uxvvecm7sluobwwhpb5wdcxhvhmx56e667nxdncd4a`
|
||||
|
||||
They are also posted to the Radicle:
|
||||
- v6.2.10: `rad:git:hnrkkroqnbfwj6uxpfjuhspoxnfm4i8e6oqwy`
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+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
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
Generated
+19244
-6514
File diff suppressed because it is too large
Load Diff
+58
-58
@@ -1,27 +1,30 @@
|
||||
{
|
||||
"name": "@psf/bch-js",
|
||||
"version": "4.0.0",
|
||||
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
|
||||
"version": "6.0.0",
|
||||
"description": "A JavaScript library for working with Bitcoin Cash, eCash, 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: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 -g '#hydrateUtxosWL' --timeout 30000 test/integration/",
|
||||
"test:temp": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 -g '#hydrateUtxos' test/integration/",
|
||||
"test:temp2": "mocha --timeout=30000 -g '#_hydrateUtxo' test/unit/",
|
||||
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#hydrateUtxos' test/integration/",
|
||||
"test:temp4": "export RESTURL=http://localhost:3000/v4/ && mocha --timeout 30000 -g '#decodeOpReturn' test/integration/",
|
||||
"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:bchn:slpdb": "export TESTSLP=1 && export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
||||
"test:integration:local:abc": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
|
||||
"test:integration:local:bchn": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
||||
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v5/ mocha --timeout 30000 test/integration/chains/testnet",
|
||||
"test:integration:decatur:bchn": "export RESTURL=http://192.168.2.129:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
|
||||
"test:integration:decatur:abc": "export RESTURL=http://192.168.2.141:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
|
||||
"test:integration: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/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,50 @@
|
||||
"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",
|
||||
"@chris.troutner/bip32-utils": "1.0.5",
|
||||
"@psf/bip21": "2.0.1",
|
||||
"@psf/bitcoincash-ops": "2.0.0",
|
||||
"@psf/bitcoincashjs-lib": "4.0.2",
|
||||
"@psf/coininfo": "4.0.0",
|
||||
"axios": "0.26.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",
|
||||
"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.50.5",
|
||||
"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.2.1",
|
||||
"node-mocks-http": "1.7.0",
|
||||
"nyc": "15.1.0",
|
||||
"semantic-release": "^19.0.2",
|
||||
"sinon": "9.2.2",
|
||||
"standard": "^16.0.4"
|
||||
},
|
||||
"apidoc": {
|
||||
"title": "bch-js",
|
||||
|
||||
+93
-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,78 @@ 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]
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Address.toHash160() toHash160()
|
||||
* @apiName toHash160
|
||||
* @apiGroup Address
|
||||
* @apiDescription Converts any address format to hash160
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // cash address mainnet p2pkh
|
||||
* bchjs.Address.toHash160("bitcoincash:qptnmya5wkly7xf97wm5ak23yqdsz3l2cyj7k9vyyh")
|
||||
* // 573d93b475be4f1925f3b74ed951201b0147eac1
|
||||
*
|
||||
* // cash address mainnet p2sh
|
||||
* bchjs.Address.toHash160("bitcoincash:pp7ushdxf5we8mcpaa3wqgsuqt639cu59ur5xu5fug")
|
||||
* // 7dc85da64d1d93ef01ef62e0221c02f512e3942f
|
||||
*/
|
||||
// Converts any address format to hash160
|
||||
toHash160 (address) {
|
||||
const legacyAddress = this.toLegacyAddress(address)
|
||||
@@ -166,7 +238,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 +343,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 +809,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
|
||||
|
||||
+12
-8
@@ -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')
|
||||
@@ -28,13 +28,15 @@ const Script = require('./script')
|
||||
const Price = require('./price')
|
||||
const Schnorr = require('./schnorr')
|
||||
const SLP = require('./slp/slp')
|
||||
const IPFS = require('./ipfs')
|
||||
const Encryption = require('./encryption')
|
||||
const Utxo = require('./utxo')
|
||||
const Transaction = require('./transaction')
|
||||
const DSProof = require('./dsproof')
|
||||
const Ecash = require('./ecash')
|
||||
|
||||
// Indexers
|
||||
const Ninsight = require('./ninsight')
|
||||
const Electrumx = require('./electrumx')
|
||||
const PsfSlpIndexer = require('./psf-slp-indexer')
|
||||
|
||||
class BCHJS {
|
||||
constructor (config) {
|
||||
@@ -79,9 +81,6 @@ class BCHJS {
|
||||
|
||||
// console.log(`apiToken: ${this.apiToken}`)
|
||||
|
||||
// Bitcoin.com Ninsight indexer
|
||||
this.Ninsight = new Ninsight(config)
|
||||
|
||||
// ElectrumX indexer
|
||||
this.Electrumx = new Electrumx(libConfig)
|
||||
|
||||
@@ -111,8 +110,13 @@ class BCHJS {
|
||||
this.SLP = new SLP(libConfig)
|
||||
this.SLP.HDNode = this.HDNode
|
||||
|
||||
this.IPFS = new IPFS()
|
||||
this.Utxo = new Utxo(libConfig)
|
||||
this.Transaction = new Transaction(libConfig)
|
||||
|
||||
this.DSProof = new DSProof(libConfig)
|
||||
this.eCash = new Ecash()
|
||||
|
||||
this.PsfSlpIndexer = new PsfSlpIndexer(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
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
Utility library for converting units with eCash.
|
||||
*/
|
||||
|
||||
class eCash {
|
||||
/**
|
||||
* @api eCash.toSatoshi() toSatoshi()
|
||||
* @apiName toSatoshi
|
||||
* @apiGroup eCash
|
||||
* @apiDescription
|
||||
* Convert XEC units into satoshi units
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // convert 10,704.35 XEC to satoshis:
|
||||
* bchjs.eCash.toSatoshi(10704.35)
|
||||
* // 1070435
|
||||
*/
|
||||
toSatoshi (xec) {
|
||||
if (typeof xec !== 'number') {
|
||||
throw new Error('input must be a floating number representing XEC')
|
||||
}
|
||||
|
||||
return Math.floor(xec * 100)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api eCash.toXec() toXec()
|
||||
* @apiName toXec
|
||||
* @apiGroup eCash
|
||||
* @apiDescription
|
||||
* Convert satoshi units to XEC units
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* // convert 1,070,435 satoshis to XEC:
|
||||
* bchjs.eCash.toSatoshi(1070435)
|
||||
* // 10704.35
|
||||
*/
|
||||
toXec (sats) {
|
||||
if (typeof sats !== 'number') {
|
||||
throw new Error('input must be a floating number representing satoshis')
|
||||
}
|
||||
|
||||
return sats / 100
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = eCash
|
||||
+32
-19
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,7 +44,7 @@ class ElectrumX {
|
||||
* @apiDescription Return a list of uxtos for an address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Electrumx.utxo('bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9');
|
||||
* console.log(utxo);
|
||||
@@ -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
|
||||
@@ -638,13 +638,24 @@ class ElectrumX {
|
||||
// Sort confirmed Transactions by the block height
|
||||
sortConfTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// console.log(`sortConfTxs txs: ${JSON.stringify(txs, null, 2)}`)
|
||||
|
||||
// Filter out unconfirmed transactions, with a height of 0 or less.
|
||||
txs = txs.filter(elem => elem.height > 0)
|
||||
|
||||
if (sortingOrder === 'DESCENDING') {
|
||||
return txs.sort((a, b) => b.height - a.height)
|
||||
// console.log('Sorting in descending order')
|
||||
return txs.sort((a, b) => {
|
||||
// console.log(`descending b.height: ${b.height}, a.height: ${a.height}`)
|
||||
return b.height - a.height
|
||||
})
|
||||
}
|
||||
return txs.sort((a, b) => a.height - b.height)
|
||||
|
||||
// console.log('Sorting in ascending order')
|
||||
return txs.sort((a, b) => {
|
||||
// console.log(`ascending b.height: ${b.height}, a.height: ${a.height}`)
|
||||
return a.height - b.height
|
||||
})
|
||||
} catch (err) {
|
||||
console.log('Error in util.js/sortConfTxs()')
|
||||
throw err
|
||||
@@ -685,6 +696,8 @@ class ElectrumX {
|
||||
// Substitute zero-conf txs with the current block-height + 1
|
||||
async sortAllTxs (txs, sortingOrder = 'DESCENDING') {
|
||||
try {
|
||||
// console.log(`sortingOrder: ${sortingOrder}`)
|
||||
|
||||
// Calculate the height of the next block
|
||||
const nextBlock = (await this.blockchain.getBlockCount()) + 1
|
||||
|
||||
|
||||
+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(
|
||||
|
||||
-454
@@ -1,454 +0,0 @@
|
||||
/*
|
||||
This library interacts with the IPFS REST API at FullStack.cash for uploading
|
||||
and downloading data from the IPFS network.
|
||||
*/
|
||||
|
||||
const axios = require('axios')
|
||||
const Uppy = require('@uppy/core')
|
||||
const Tus = require('@uppy/tus')
|
||||
const fs = require('fs')
|
||||
|
||||
let _this
|
||||
|
||||
class IPFS {
|
||||
constructor (config) {
|
||||
this.IPFS_API = process.env.IPFS_API
|
||||
? process.env.IPFS_API
|
||||
: 'https://ipfs-file-upload.fullstackcash.nl'
|
||||
// : `http://localhost:5001`
|
||||
|
||||
// Default options when calling axios.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
// authorization: `Token ${this.apiToken}`,
|
||||
timeout: 15000 // Timeout if the server does not respond in time.
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
|
||||
_this.axios = axios
|
||||
_this.fs = fs
|
||||
|
||||
// Initialize Uppy
|
||||
_this.uppy = _this.initUppy()
|
||||
}
|
||||
|
||||
// Initializes Uppy, which is used for file uploads.
|
||||
initUppy () {
|
||||
const uppy = Uppy({
|
||||
allowMultipleUploads: false,
|
||||
meta: { test: 'avatar' },
|
||||
debug: false,
|
||||
restrictions: {
|
||||
maxFileSize: null,
|
||||
maxNumberOfFiles: 1,
|
||||
minNumberOfFiles: 1,
|
||||
allowedFileTypes: null // type of files allowed to load
|
||||
}
|
||||
})
|
||||
uppy.use(Tus, { endpoint: `${_this.IPFS_API}/uppy-files` })
|
||||
|
||||
return uppy
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.createFileModelServer() createFileModelServer()
|
||||
* @apiName createFileModelServer()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Creates a new model on the server. If successful, will
|
||||
* return an object with file data. That object contains a BCH address,
|
||||
* payment amount, and _id required to be able to upload a file.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const path = `${__dirname}/ipfs.js`
|
||||
* let fileData = await bchjs.IPFS.createFileModelServer(path);
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "success": true,
|
||||
* "hostingCostBCH": 0.00004197,
|
||||
* "hostingCostUSD": 0.01,
|
||||
* "file": {
|
||||
* "payloadLink": "",
|
||||
* "hasBeenPaid": false,
|
||||
* "_id": "5ec562319bfacc745e8d8a52",
|
||||
* "schemaVersion": 1,
|
||||
* "size": 4458,
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js",
|
||||
* "createdTimestamp": "1589994033.655",
|
||||
* "hostingCost": 4196,
|
||||
* "walletIndex": 49,
|
||||
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
|
||||
* "__v": 0
|
||||
* }
|
||||
*/
|
||||
async createFileModelServer (path) {
|
||||
try {
|
||||
// Ensure the file exists.
|
||||
if (!_this.fs.existsSync(path)) {
|
||||
throw new Error(`Could not find this file: ${path}`)
|
||||
}
|
||||
|
||||
// Read in the file.
|
||||
const fileBuf = _this.fs.readFileSync(path)
|
||||
// console.log(`fileBuf: `, fileBuf)
|
||||
|
||||
// console.log(`Buffer length: ${fileBuf.length}`)
|
||||
|
||||
// Get the file name from the path.
|
||||
const splitPath = path.split('/')
|
||||
const fileName = splitPath[splitPath.length - 1]
|
||||
|
||||
// Get the file extension.
|
||||
const splitExt = fileName.split('.')
|
||||
const fileExt = splitExt[splitExt.length - 1]
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: fileBuf.length,
|
||||
fileName: fileName,
|
||||
fileExtension: fileExt
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
|
||||
file: fileObj
|
||||
})
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
return fileData.data
|
||||
} catch (err) {
|
||||
console.error('Error in createFileModel()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.uploadFileServer() uploadFileServer()
|
||||
* @apiName uploadFile()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
|
||||
* successful, it will return an object with an ID, a BCH address, and an
|
||||
* amount of BCH to pay.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const path = `${__dirname}/ipfs.js`
|
||||
* let fileData = await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52");
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "schemaVersion": 1,
|
||||
* "size": 2374,
|
||||
* "fileId": "5ec562319bfacc745e8d8a52",
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js"
|
||||
* }
|
||||
*/
|
||||
async uploadFileServer (path, modelId) {
|
||||
try {
|
||||
// Ensure the file exists.
|
||||
if (!_this.fs.existsSync(path)) {
|
||||
throw new Error(`Could not find this file: ${path}`)
|
||||
}
|
||||
|
||||
if (!modelId) {
|
||||
throw new Error(
|
||||
'Must include a file model ID in order to upload a file.'
|
||||
)
|
||||
}
|
||||
|
||||
// Read in the file.
|
||||
const fileBuf = _this.fs.readFileSync(path)
|
||||
|
||||
// Get the file name from the path.
|
||||
const splitPath = path.split('/')
|
||||
const fileName = splitPath[splitPath.length - 1]
|
||||
|
||||
// Prepare the upload object. Get a file ID from Uppy.
|
||||
const id = _this.uppy.addFile({
|
||||
name: fileName,
|
||||
data: fileBuf,
|
||||
source: 'Local',
|
||||
isRemote: false
|
||||
})
|
||||
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
|
||||
|
||||
// Add the model ID, required to be allowed to upload the file.
|
||||
_this.uppy.setFileMeta(id, { fileModelId: modelId })
|
||||
|
||||
// Upload the file to the server.
|
||||
const upData = await _this.uppy.upload()
|
||||
|
||||
if (upData.failed.length) {
|
||||
throw new Error('The file could not be uploaded')
|
||||
}
|
||||
|
||||
if (upData.successful.length) {
|
||||
delete upData.successful[0].data
|
||||
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: upData.successful[0].progress.bytesTotal,
|
||||
fileId: upData.successful[0].meta.fileModelId,
|
||||
fileName: upData.successful[0].name,
|
||||
fileExtension: upData.successful[0].extension
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
return fileObj
|
||||
// return true
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (err) {
|
||||
console.error('Error in bch-js/src/ipfs.js/upload(): ')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.getStatus() getStatus()
|
||||
* @apiName getStatus()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Gets the status of the uploaded file. Will return an object
|
||||
* that indicates the payment status. If the file is paid, it should contain
|
||||
* an IPFS hash.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const modelId = "5ec7392c2acfe57aa62e945a"
|
||||
* let fileData = await bchjs.IPFS.getStatus(modelId)
|
||||
* console.log(fileData)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "hasBeenPaid": true,
|
||||
* "satCost": 4403,
|
||||
* "bchAddr": "bchtest:qz5z82u0suqh80x5tfx4ht8kdrkkw664vcy44uz0wk",
|
||||
* "ipfsHash": "QmRDHPhY5hCNVRMVQvS2H9uty8P1skdwgLaHpUAkEvsjcE",
|
||||
* "fileId": "5ec7392c2acfe57aa62e945a",
|
||||
* "fileName": "ipfs-e2e.js"
|
||||
* }
|
||||
*/
|
||||
async getStatus (modelId) {
|
||||
try {
|
||||
if (!modelId) throw new Error('Must include a file model ID.')
|
||||
|
||||
const fileData = await _this.axios.get(
|
||||
`${this.IPFS_API}/files/${modelId}`
|
||||
)
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
hasBeenPaid: fileData.data.file.hasBeenPaid,
|
||||
satCost: fileData.data.file.hostingCost,
|
||||
bchAddr: fileData.data.file.bchAddr,
|
||||
ipfsHash: fileData.data.file.payloadLink,
|
||||
fileId: fileData.data.file._id,
|
||||
fileName: fileData.data.file.fileName
|
||||
}
|
||||
|
||||
return fileObj
|
||||
} catch (err) {
|
||||
console.error('Error in getStatus()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.createFileModelWeb() createFileModelWeb()
|
||||
* @apiName createFileModelWeb()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Creates a new model on the server. If successful, will
|
||||
* return an object with file data. That object contains a BCH address,
|
||||
* payment amount, and _id required to be able to upload a file.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const content = ['PSF']
|
||||
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
|
||||
* const options = { type: "text/plain" }
|
||||
* const file = new File(content,name,options)
|
||||
* let fileData = await bchjs.IPFS.createFileModelWeb(file);
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "success": true,
|
||||
* "hostingCostBCH": 0.00004197,
|
||||
* "hostingCostUSD": 0.01,
|
||||
* "file": {
|
||||
* "payloadLink": "",
|
||||
* "hasBeenPaid": false,
|
||||
* "_id": "5ec562319bfacc745e8d8a52",
|
||||
* "schemaVersion": 1,
|
||||
* "size": 4458,
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js",
|
||||
* "createdTimestamp": "1589994033.655",
|
||||
* "hostingCost": 4196,
|
||||
* "walletIndex": 49,
|
||||
* "bchAddr": "bchtest:qzrpkevu7h2ayfa4rjx08r5elvpfu72dg567x3mh3c",
|
||||
* "__v": 0
|
||||
* }
|
||||
*/
|
||||
async createFileModelWeb (file) {
|
||||
try {
|
||||
if (!file) throw new Error('File is required')
|
||||
|
||||
const { name, size } = file
|
||||
|
||||
if (!name || typeof name !== 'string') {
|
||||
throw new Error("File should have the property 'name' of string type")
|
||||
}
|
||||
|
||||
if (!size || typeof size !== 'number') {
|
||||
throw new Error("File should have the property 'size' of number type")
|
||||
}
|
||||
|
||||
// Get the file extension.
|
||||
const splitExt = name.split('.')
|
||||
const fileExt = splitExt[splitExt.length - 1]
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: size,
|
||||
fileName: name,
|
||||
fileExtension: fileExt
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
const fileData = await _this.axios.post(`${this.IPFS_API}/files`, {
|
||||
file: fileObj
|
||||
})
|
||||
// console.log(`fileData.data: ${JSON.stringify(fileData.data, null, 2)}`)
|
||||
|
||||
return fileData.data
|
||||
} catch (err) {
|
||||
console.error('Error in createFileModelWeb()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api IPFS.uploadFileWeb() uploadFileWeb()
|
||||
* @apiName uploadFileWeb()
|
||||
* @apiGroup IPFS
|
||||
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
|
||||
* successful, it will return an object with an ID, a BCH address, and an
|
||||
* amount of BCH to pay.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* * const content = ['PSF']
|
||||
* const name = 'psf.txt' // Content can be Array , ArrayBuffer , Blob
|
||||
* const options = { type: "text/plain" }
|
||||
* const file = new File(content,name,options)
|
||||
* let fileData = await bchjs.IPFS.uploadFile(file, "5ec562319bfacc745e8d8a52");
|
||||
* console.log(fileData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "schemaVersion": 1,
|
||||
* "size": 2374,
|
||||
* "fileId": "5ec562319bfacc745e8d8a52",
|
||||
* "fileName": "ipfs.js",
|
||||
* "fileExtension": "js"
|
||||
* }
|
||||
*/
|
||||
async uploadFileWeb (file, modelId) {
|
||||
try {
|
||||
if (!file) throw new Error('File is required')
|
||||
|
||||
const { name, type, size } = file
|
||||
|
||||
if (!name || typeof name !== 'string') {
|
||||
throw new Error("File should have the property 'name' of string type")
|
||||
}
|
||||
|
||||
if (!type || typeof type !== 'string') {
|
||||
throw new Error("File should have the property 'type' of string type")
|
||||
}
|
||||
|
||||
if (!size || typeof size !== 'number') {
|
||||
throw new Error("File should have the property 'size' of number type")
|
||||
}
|
||||
|
||||
if (!modelId) {
|
||||
throw new Error(
|
||||
'Must include a file model ID in order to upload a file.'
|
||||
)
|
||||
}
|
||||
|
||||
// Prepare the upload object. Get a file ID from Uppy.
|
||||
const id = _this.uppy.addFile({
|
||||
name: name,
|
||||
data: file,
|
||||
source: 'Local',
|
||||
isRemote: false
|
||||
})
|
||||
// console.log(`id: ${JSON.stringify(id, null, 2)}`)
|
||||
|
||||
// Add the model ID, required to be allowed to upload the file.
|
||||
_this.uppy.setFileMeta(id, { fileModelId: modelId })
|
||||
|
||||
// Upload the file to the server.
|
||||
const upData = await _this.uppy.upload()
|
||||
|
||||
if (upData.failed.length) {
|
||||
throw new Error('The file could not be uploaded')
|
||||
}
|
||||
|
||||
if (upData.successful.length) {
|
||||
delete upData.successful[0].data
|
||||
// console.log(`upData: ${JSON.stringify(upData, null, 2)}`)
|
||||
|
||||
const fileObj = {
|
||||
schemaVersion: 1,
|
||||
size: upData.successful[0].progress.bytesTotal,
|
||||
fileId: upData.successful[0].meta.fileModelId,
|
||||
fileName: upData.successful[0].name,
|
||||
fileExtension: upData.successful[0].extension
|
||||
}
|
||||
// console.log(`fileObj: ${JSON.stringify(fileObj, null, 2)}`)
|
||||
|
||||
return fileObj
|
||||
// return true
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (err) {
|
||||
console.error('Error in bch-js/src/ipfs.js/uploadFileWeb(): ')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = IPFS
|
||||
+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
|
||||
|
||||
-319
@@ -1,319 +0,0 @@
|
||||
/*
|
||||
This library interacts with the ninsight REST API endpoints operated by
|
||||
Bitcoin.com
|
||||
*/
|
||||
|
||||
const axios = require('axios')
|
||||
|
||||
let _this
|
||||
|
||||
class Ninsight {
|
||||
constructor (config) {
|
||||
// this.restURL = config.restURL
|
||||
// this.apiToken = config.apiToken
|
||||
|
||||
// this.ninsightURL = `https://bch-explorer.api.bitcoin.com/v1/`
|
||||
if (config) {
|
||||
this.ninsightURL = config.ninsightURL
|
||||
? config.ninsightURL
|
||||
: 'https://rest.bitcoin.com/v2'
|
||||
}
|
||||
|
||||
// Add JWT token to the authorization header.
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`,
|
||||
timeout: 15000
|
||||
}
|
||||
}
|
||||
|
||||
_this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.utxo() utxo()
|
||||
* @apiName Ninsight Utxo
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Return list of uxto for address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Ninsight.utxo('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "txid": "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
|
||||
* // "vout": 1,
|
||||
* // "amount": 0.03608203,
|
||||
* // "satoshis": 3608203,
|
||||
* // "height": 585570,
|
||||
* // "confirmations": 10392
|
||||
* // },
|
||||
* // {
|
||||
* // "txid": "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
|
||||
* // "vout": 0,
|
||||
* // "amount": 0.03608203,
|
||||
* // "satoshis": 3608203,
|
||||
* // "height": 577125,
|
||||
* // "confirmations": 18837
|
||||
* // }
|
||||
* // ]
|
||||
*
|
||||
*/
|
||||
async utxo (address) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/utxo`,
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Handle array of addresses.
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/utxo`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.unconfirmed() unconfirmed()
|
||||
* @apiName Ninsight Unconfirmed Utxo
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Returns a list of unconfirmed UTXOs for an address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let utxo = await bchjs.Ninsight.unconfirmed('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
|
||||
* console.log(utxo);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "txid": "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
|
||||
* // "vout": 1,
|
||||
* // "amount": 0.03608203,
|
||||
* // "satoshis": 3608203,
|
||||
* // "confirmations": 0
|
||||
* // "ts": 1559670801
|
||||
* // },
|
||||
* // {
|
||||
* // "txid": "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
|
||||
* // "vout": 0,
|
||||
* // "amount": 0.03608203,
|
||||
* // "satoshis": 3608203,
|
||||
* // "confirmations": 0
|
||||
* // "ts": 1559670902
|
||||
* // }
|
||||
* // ]
|
||||
*
|
||||
*/
|
||||
async unconfirmed (address) {
|
||||
try {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/unconfirmed`,
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/unconfirmed`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.transactions() transactions()
|
||||
* @apiName Ninsight TX History
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Return transactions history for address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txs = await bchjs.Ninsight.transactions('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
|
||||
* console.log(txs);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "pagesTotal": 1,
|
||||
* // "txs": [
|
||||
* // {
|
||||
* // "txid": "ec7bc8349386e3e1939bbdc4f8092fdbdd6a380734e68486b558cd594c451d5b",
|
||||
* // "version": 2,
|
||||
* // "locktime": 0,
|
||||
* // "vin": [
|
||||
* // {
|
||||
* // "txid": "4f1fc57c33659628938db740449bf92fb75799e1d5750a4aeef80eb52d6df1e0",
|
||||
* // "vout": 0,
|
||||
* // "sequence": 4294967295,
|
||||
* // "n": 0,
|
||||
* // ...
|
||||
* // }
|
||||
* // ...
|
||||
* // ]
|
||||
* // ...
|
||||
* // }
|
||||
* // ],
|
||||
* // "legacyAddress": "1LpbYkEM5cryfhs58tH8c93p4SGzit7UrP",
|
||||
* // "cashAddress": "bitcoincash:qrvk436u4r0ew2wj0rd9pnxhx4w90p2yfc29ta0d2n",
|
||||
* // "currentPage": 0
|
||||
* // }
|
||||
* // ]
|
||||
* //
|
||||
*
|
||||
*/
|
||||
async transactions (address) {
|
||||
try {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/transactions`,
|
||||
{
|
||||
addresses: [address]
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(address)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/address/transactions`,
|
||||
{
|
||||
addresses: address
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input address must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Ninsight.txDetails() txDetails()
|
||||
* @apiName Ninsight TX Details
|
||||
* @apiGroup Ninsight
|
||||
* @apiDescription Return transactions details for address(es).
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let result = await bchjs.Ninsight.txDetails('fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33');
|
||||
* console.log(result);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // [
|
||||
* // {
|
||||
* // "txid": "fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33",
|
||||
* // "version": 1,
|
||||
* // "locktime": 0,
|
||||
* // "vin": [
|
||||
* // {
|
||||
* // "sequence": 4294967295,
|
||||
* // "n": 0
|
||||
* // }
|
||||
* // ],
|
||||
* // "vout": [
|
||||
* // ...
|
||||
* // ],
|
||||
* // "blockhash": "00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09",
|
||||
* // "blockheight": 1000,
|
||||
* // "confirmations": 659909,
|
||||
* // "time": 1232346882,
|
||||
* // "blocktime": 1232346882,
|
||||
* // "firstSeenTime": null,
|
||||
* // "valueOut": 50,
|
||||
* // "size": 135
|
||||
* // }
|
||||
* // ]
|
||||
*
|
||||
*/
|
||||
async txDetails (txid) {
|
||||
try {
|
||||
if (typeof txid === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/transaction/details`,
|
||||
{
|
||||
txids: [txid]
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
} else if (Array.isArray(txid)) {
|
||||
const response = await axios.post(
|
||||
`${this.ninsightURL}/transaction/details`,
|
||||
{
|
||||
txids: txid
|
||||
},
|
||||
_this.axiosOptions
|
||||
)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Transaction ID must be a string or array of strings.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Ninsight
|
||||
+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)}`)
|
||||
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
This library interacts with the PSF slp indexer REST API endpoints operated
|
||||
by FullStack.cash
|
||||
|
||||
TODO:
|
||||
- detect TXs from tokens in the blacklist.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
const axios = require('axios')
|
||||
|
||||
// Local libraries
|
||||
const RawTransaction = require('./raw-transactions')
|
||||
const SlpUtils = require('./slp/utils')
|
||||
|
||||
// let _this
|
||||
|
||||
class PsfSlpIndexer {
|
||||
constructor (config = {}) {
|
||||
this.restURL = config.restURL
|
||||
this.apiToken = config.apiToken
|
||||
this.authToken = config.authToken
|
||||
|
||||
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}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Encapsulate dependencies
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
|
||||
// _this = this
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.status() status()
|
||||
* @apiName Status
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return status from psf slp indexer.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let status = await bchjs.PsfSlpIndexer.status()
|
||||
* console.log(status)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* "status": {
|
||||
* "startBlockHeight": 543376,
|
||||
* "syncedBlockHeight": 723249,
|
||||
* "chainBlockHeight": 722679
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async status () {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}psf/slp/status`,
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.balance() balance()
|
||||
* @apiName SLP Balance
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return slp balance for a single address.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n')
|
||||
* console.log(balance)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* balance: {
|
||||
* utxos: [
|
||||
* {
|
||||
* txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
* vout: 1,
|
||||
* type: 'token',
|
||||
* qty: '1800',
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
|
||||
* }
|
||||
* ],
|
||||
* txs: [
|
||||
* {
|
||||
* txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
|
||||
* height: 717796
|
||||
* },
|
||||
* {
|
||||
* txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
* height: 717832
|
||||
* }
|
||||
* ],
|
||||
* balances: [
|
||||
* {
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* qty: '1800'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async balance (address) {
|
||||
try {
|
||||
// console.log('balance() address: ', address)
|
||||
|
||||
// Handle single address.
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}psf/slp/address`,
|
||||
{ address },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
throw new Error('Input address must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.tokenStats() tokenStats()
|
||||
* @apiName Token Stats
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return list stats for a single slp token.
|
||||
* The second input is a Boolean, which determins the the transaction history
|
||||
* of the token is included in the returned data. The default is false.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', true)
|
||||
* console.log(tokenStats)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* tokenData: {
|
||||
* type: 1,
|
||||
* ticker: 'TROUT',
|
||||
* name: "Trout's test token",
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* documentUri: 'troutsblog.com',
|
||||
* documentHash: '',
|
||||
* decimals: 2,
|
||||
* mintBatonIsActive: true,
|
||||
* tokensInCirculationBN: '100098953386',
|
||||
* tokensInCirculationStr: '100098953386',
|
||||
* blockCreated: 622414,
|
||||
* totalBurned: '1046614',
|
||||
* totalMinted: '100100000000'
|
||||
* txs: [
|
||||
* {
|
||||
* txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* height: 622414,
|
||||
* type: 'GENESIS',
|
||||
* qty: '100000000000'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
|
||||
async tokenStats (tokenId, withTxHistory = false) {
|
||||
try {
|
||||
// Handle single address.
|
||||
if (typeof tokenId === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}psf/slp/token`,
|
||||
{ tokenId, withTxHistory },
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
throw new Error('Input tokenId must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api PsfSlpIndexer.tx() tx()
|
||||
* @apiName SLP Transaction Data
|
||||
* @apiGroup PSF SLP
|
||||
* @apiDescription Return slp transaction data.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
|
||||
* console.log(txData)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* {
|
||||
* txData: {
|
||||
* txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* version: 2,
|
||||
* size: 339,
|
||||
* locktime: 0,
|
||||
* vin: [
|
||||
* {
|
||||
* txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
|
||||
* vout: 0,
|
||||
* scriptSig: {
|
||||
* asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
|
||||
* hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
|
||||
* },
|
||||
* sequence: 4294967295,
|
||||
* address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
|
||||
* value: 0.00051303,
|
||||
* tokenQty: 0,
|
||||
* tokenQtyStr: '0',
|
||||
* tokenId: null
|
||||
* }
|
||||
* ],
|
||||
* vout: [
|
||||
* {
|
||||
* value: 0,
|
||||
* n: 0,
|
||||
* scriptPubKey: {
|
||||
* asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
|
||||
* hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
|
||||
* type: 'nulldata'
|
||||
* },
|
||||
* tokenQtyStr: '0',
|
||||
* tokenQty: 0
|
||||
* }
|
||||
* ],
|
||||
* hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
|
||||
* blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
|
||||
* confirmations: 97398,
|
||||
* time: 1581773131,
|
||||
* blocktime: 1581773131,
|
||||
* blockheight: 622414,
|
||||
* isSlpTx: true,
|
||||
* tokenTxType: 'GENESIS',
|
||||
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
* tokenType: 1,
|
||||
* tokenTicker: 'TROUT',
|
||||
* tokenName: "Trout's test token",
|
||||
* tokenDecimals: 2,
|
||||
* tokenUri: 'troutsblog.com',
|
||||
* tokenDocHash: '',
|
||||
* isValidSlp: true
|
||||
* }
|
||||
* }
|
||||
*
|
||||
*/
|
||||
async tx (txid) {
|
||||
try {
|
||||
// console.log('txid: ', txid)
|
||||
|
||||
// Handle single address.
|
||||
if (typeof txid === 'string') {
|
||||
const response = await axios.post(
|
||||
`${this.restURL}psf/slp/txid`,
|
||||
{ txid },
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log('response: ', response)
|
||||
|
||||
return response.data
|
||||
}
|
||||
|
||||
throw new Error('Input txid must be a string.')
|
||||
} catch (error) {
|
||||
// console.log('error: ', error)
|
||||
|
||||
// Case: txid is not stored in the psf-slp-indexer tx database.
|
||||
// Response: If it's not in the database, then it can be assumed the TX
|
||||
// is not a token TX?
|
||||
if (
|
||||
error.response &&
|
||||
error.response.data &&
|
||||
error.response.data.error &&
|
||||
error.response.data.error.includes('Key not found in database')
|
||||
) {
|
||||
// console.log(
|
||||
// 'TX not found in psf-slp-indexer. Retrieving from full node.'
|
||||
// )
|
||||
|
||||
// Check if this txid belongs to a blacklisted token.
|
||||
const isInBlacklist = await this.checkBlacklist(txid)
|
||||
// console.log('isInBlacklist: ', isInBlacklist)
|
||||
|
||||
// Get the TX Details from the full node.
|
||||
const txDetails = await this.rawTransaction.getTxData(txid)
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
if (isInBlacklist) {
|
||||
txDetails.isValidSlp = null
|
||||
} else {
|
||||
txDetails.isValidSlp = false
|
||||
}
|
||||
|
||||
const outObj = {
|
||||
txData: txDetails
|
||||
}
|
||||
|
||||
return outObj
|
||||
} else throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the txid has an OP_RETURN containing a tokenID that is in the
|
||||
// blacklist. In that case, the isValidSlp property should be marked as
|
||||
// null, and not false.
|
||||
async checkBlacklist (txid) {
|
||||
try {
|
||||
// TODO: Add endpoint to psf-slp-indexer to retrieve current blacklist.
|
||||
// This should be done once at startup, and not each time this function
|
||||
// is called.
|
||||
const blacklist = [
|
||||
'dd21be4532d93661e8ffe16db6535af0fb8ee1344d1fef81a193e2b4cfa9fbc9'
|
||||
]
|
||||
|
||||
const outTokenData = await this.slpUtils.decodeOpReturn(txid)
|
||||
// console.log('outTokenData: ', outTokenData)
|
||||
|
||||
// Loop through each token in the blacklist.
|
||||
for (let i = 0; i < blacklist.length; i++) {
|
||||
// If a match is found, return true.
|
||||
if (outTokenData.tokenId === blacklist[i]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// By default, return false.
|
||||
return false
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
// Exit quietly.
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PsfSlpIndexer
|
||||
+65
-111
@@ -1,8 +1,6 @@
|
||||
const axios = require('axios')
|
||||
|
||||
const SlpUtils = require('./slp/utils')
|
||||
|
||||
let _this
|
||||
// let _this
|
||||
|
||||
class RawTransactions {
|
||||
constructor (config) {
|
||||
@@ -29,10 +27,7 @@ class RawTransactions {
|
||||
// Encapsulate dependencies
|
||||
this.axios = axios
|
||||
|
||||
// Dependencies
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
|
||||
_this = this
|
||||
// this = this
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,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
|
||||
@@ -116,9 +111,7 @@ class RawTransactions {
|
||||
data: {
|
||||
hexes: hex
|
||||
},
|
||||
headers: {
|
||||
authorization: `Token ${_this.apiToken}`
|
||||
}
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await axios(options)
|
||||
|
||||
@@ -127,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
|
||||
}
|
||||
}
|
||||
@@ -172,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
|
||||
@@ -183,9 +177,7 @@ class RawTransactions {
|
||||
data: {
|
||||
hexes: script
|
||||
},
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
headers: this.axiosOptions.headers
|
||||
}
|
||||
const response = await axios(options)
|
||||
|
||||
@@ -194,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
|
||||
}
|
||||
}
|
||||
@@ -266,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
|
||||
@@ -281,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)
|
||||
|
||||
@@ -290,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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,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)
|
||||
@@ -328,14 +334,21 @@ class RawTransactions {
|
||||
|
||||
retArray.push({
|
||||
vin: i,
|
||||
address: voutSender.scriptPubKey.addresses[0]
|
||||
address: voutSender.scriptPubKey.addresses[0],
|
||||
value: voutSender.value
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,104 +381,44 @@ class RawTransactions {
|
||||
// Appends the BCH address to the inputs of the transaction.
|
||||
async getTxData (txid) {
|
||||
try {
|
||||
// console.log('getTxData() txid: ', txid)
|
||||
|
||||
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.
|
||||
const txDetails = await this.getRawTransaction(txid, true)
|
||||
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
|
||||
|
||||
const inAddrs = await this._getInputAddrs(txDetails)
|
||||
// console.log(`inAddrs: ${JSON.stringify(inAddrs, null, 2)}`)
|
||||
|
||||
// Add the input address to the transaction data.
|
||||
for (let i = 0; i < inAddrs.length; i++) {
|
||||
txDetails.vin[i].address = inAddrs[i].address
|
||||
}
|
||||
|
||||
return txDetails
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps getTxData(), but also appends SLP token information to each input
|
||||
// and output of the transaction. This is a very API-heavy call.
|
||||
// Returns false if the txid is not an SLP transaction.
|
||||
//
|
||||
// Warning! This is a prototype function and the output can change at any time
|
||||
// without reflecting a change in the semantic version. DO NOT USE IN PRODUCTION.
|
||||
async getTxDataSlp (txid) {
|
||||
try {
|
||||
if (typeof txid !== 'string') {
|
||||
throw new Error('Input must be a string or array of strings.')
|
||||
}
|
||||
|
||||
const txDetails = await this.getTxData(txid)
|
||||
|
||||
// First get the token information for the output. If that fails, then
|
||||
// this is not an SLP transaction, and this method can return false.
|
||||
let outTokenData
|
||||
try {
|
||||
outTokenData = await this.slpUtils.decodeOpReturn(txid)
|
||||
// console.log(`outTokenData: ${JSON.stringify(outTokenData, null, 2)}`)
|
||||
const inAddrs = await this._getInputAddrs(txDetails)
|
||||
// console.log(`inAddrs: ${JSON.stringify(inAddrs, null, 2)}`)
|
||||
|
||||
// Add token information to the tx details object.
|
||||
txDetails.tokenTxType = outTokenData.txType
|
||||
txDetails.tokenId = outTokenData.tokenId
|
||||
|
||||
// Add the token quantity to each output.
|
||||
for (let i = 0; i < outTokenData.amounts.length; i++) {
|
||||
txDetails.vout[i + 1].tokenQty = outTokenData.amounts[i]
|
||||
}
|
||||
|
||||
// Loop through each input and retrieve the token data.
|
||||
for (let i = 0; i < txDetails.vin.length; i++) {
|
||||
const thisVin = txDetails.vin[i]
|
||||
|
||||
try {
|
||||
// If decodeOpReturn() throws an error, then this input is not
|
||||
// from an SLP transaction and can be ignored.
|
||||
const inTokenData = await this.slpUtils.decodeOpReturn(thisVin.txid)
|
||||
// console.log(
|
||||
// `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)}`)
|
||||
|
||||
if (tokenQty) {
|
||||
thisVin.tokenQty = tokenQty
|
||||
// txDetails.vin[i].tokenQty = tokenQty
|
||||
} else {
|
||||
thisVin.tokenQty = null
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log('catch 2: ', err)
|
||||
// If decodeOpReturn() throws an error, then this input is not
|
||||
// from an SLP transaction and can be ignored.
|
||||
// thisVin.tokenQty = null
|
||||
thisVin.tokenQty = null
|
||||
continue
|
||||
}
|
||||
// console.log(
|
||||
// `2: txDetails.vin[i]: ${JSON.stringify(txDetails.vin[i], null, 2)}`
|
||||
// )
|
||||
// Add the input address to the transaction data.
|
||||
for (let i = 0; i < inAddrs.length; i++) {
|
||||
txDetails.vin[i].address = inAddrs[i].address
|
||||
txDetails.vin[i].value = inAddrs[i].value
|
||||
}
|
||||
} catch (err) {
|
||||
// console.log('catch 1: ', err)
|
||||
return false
|
||||
// Coinbase transactions will throw an error. Just ignore them and
|
||||
// pass back the raw transaction data.
|
||||
/* exit quietly */
|
||||
}
|
||||
|
||||
return txDetails
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
else throw error
|
||||
// console.log('error: ', 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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,7 +460,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') {
|
||||
@@ -528,16 +481,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
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ class Address extends BCHJSAddress {
|
||||
* bchjs.SLP.Address.toSLPAddress('qzm47qz5ue99y9yl4aca7jnz7dwgdenl85jkfx3znl')
|
||||
* // simpleledger:qzm47qz5ue99y9yl4aca7jnz7dwgdenl857dzayzdp
|
||||
*
|
||||
* // tesnet legacy
|
||||
* // testnet legacy
|
||||
* bchjs.SLP.Address.toSLPAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx')
|
||||
* // slptest:qzq9je6pntpva3wf6scr7mlnycr54sjgeqauyclpwv
|
||||
*
|
||||
|
||||
+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)
|
||||
}
|
||||
}
|
||||
|
||||
+25
-14
@@ -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()
|
||||
@@ -367,20 +375,21 @@ class TokenType1 {
|
||||
if (!Array.isArray(tokenUtxos)) {
|
||||
throw new Error('tokenUtxos must be an array.')
|
||||
}
|
||||
if (!mintQty) {
|
||||
throw new Error('mintQty must be a positive number.')
|
||||
}
|
||||
|
||||
// Loop through the tokenUtxos array and find the minting baton.
|
||||
let mintBatonUtxo
|
||||
for (let i = 0; i < tokenUtxos.length; i++) {
|
||||
if (tokenUtxos[i].utxoType === 'minting-baton') {
|
||||
if (tokenUtxos[i].utxoType === 'minting-baton' || tokenUtxos[i].type === 'baton') {
|
||||
mintBatonUtxo = tokenUtxos[i]
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -389,7 +398,7 @@ class TokenType1 {
|
||||
if (!tokenId) {
|
||||
throw new Error('tokenId property not found in mint-baton UTXO.')
|
||||
}
|
||||
if (!decimals) {
|
||||
if (!decimals && decimals !== 0) {
|
||||
throw new Error('decimals property not found in mint-baton UTXO.')
|
||||
}
|
||||
|
||||
@@ -398,6 +407,8 @@ class TokenType1 {
|
||||
baseQty = Math.floor(baseQty)
|
||||
baseQty = baseQty.toString()
|
||||
|
||||
if (isNaN(baseQty)) throw new Error('baseQty is non a number!')
|
||||
|
||||
// Signal that the baton should be passed or detroyed.
|
||||
let batonVout = 2
|
||||
if (destroyBaton) batonVout = null
|
||||
@@ -454,7 +465,7 @@ class TokenType1 {
|
||||
const result = await _this.axios.post(
|
||||
`${this.restURL}slp/generatesendopreturn`,
|
||||
data,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
|
||||
const slpSendObj = result.data
|
||||
|
||||
+5
-1751
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
High-level functions for working with Transactions
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
// const BigNumber = require('bignumber.js')
|
||||
|
||||
// Local libraries
|
||||
const RawTransaction = require('./raw-transactions')
|
||||
const SlpUtils = require('./slp/utils')
|
||||
const Blockchain = require('./blockchain')
|
||||
const PsfSlpIndexer = require('./psf-slp-indexer')
|
||||
|
||||
class Transaction {
|
||||
constructor (config = {}) {
|
||||
// Encapsulate dependencies
|
||||
this.slpUtils = new SlpUtils(config)
|
||||
this.rawTransaction = new RawTransaction(config)
|
||||
this.blockchain = new Blockchain(config)
|
||||
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction.get() get()
|
||||
* @apiName get
|
||||
* @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.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.Transaction.get("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||
* console.log(txData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*/
|
||||
async get (txid) {
|
||||
// console.log('transaction.get() txid: ', txid)
|
||||
return await this.psfSlpIndexer.tx(txid)
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Transaction.getTokenInfo() getTokenInfo()
|
||||
* @apiName getTokenInfo
|
||||
* @apiGroup Transaction
|
||||
* @apiDescription
|
||||
* Given the TXID of a token transaction, it will return data about that
|
||||
* token by retrieving the data from the Genesis transaction and docoding
|
||||
* the OP_RETURN.
|
||||
*
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* let txData = await bchjs.Transaction.getTokenInfo("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
|
||||
* console.log(txData);
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*/
|
||||
// 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
|
||||
+63
-12
@@ -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,57 @@ class Util {
|
||||
}
|
||||
|
||||
/**
|
||||
* @api util.sleep() sleep()
|
||||
* @api Util.chunk100() chunk100()
|
||||
* @apiName chunk100
|
||||
* @apiGroup Util
|
||||
* @apiDescription chunk up an array into multiple arrays of 100 elements each.
|
||||
* Input: arrayToSlice - a one-dimensional array of elements.
|
||||
* Returns a two-dimensional array. An array of 100-element arrays.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
* try {
|
||||
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,...,148, 149, 150]
|
||||
*
|
||||
* const chunked = bchjs.Util.chunk20(bigArray)
|
||||
* console.log(chunked)
|
||||
* } catch(error) {
|
||||
* console.error(error)
|
||||
* }
|
||||
* })()
|
||||
*
|
||||
* // returns
|
||||
* [
|
||||
* [0,1,2,3,4,5,6,7,8,9,10,11,...,98,99],
|
||||
* [100,101,102,...,148,149,150]
|
||||
* ]
|
||||
*/
|
||||
chunk100 (arrayToSlice) {
|
||||
try {
|
||||
// Validate inputs
|
||||
if (!Array.isArray(arrayToSlice)) {
|
||||
throw new Error('input must be an array')
|
||||
}
|
||||
|
||||
let offset = 0
|
||||
const result = []
|
||||
|
||||
// Loop over the array and slice off chunks of 100 elements.
|
||||
while (offset < arrayToSlice.length) {
|
||||
const chunk = arrayToSlice.slice(offset, offset + 100)
|
||||
result.push(chunk)
|
||||
offset = offset + 100
|
||||
}
|
||||
|
||||
return result
|
||||
} catch (err) {
|
||||
console.error('Error in chunk100()')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Util.sleep() sleep()
|
||||
* @apiName sleep
|
||||
* @apiGroup Util
|
||||
* @apiDescription Promise-based delay.
|
||||
@@ -175,7 +225,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,22 +275,23 @@ class Util {
|
||||
if (typeof address === 'string') {
|
||||
const response = await axios.get(
|
||||
`${this.restURL}util/validateAddress/${address}`,
|
||||
_this.axiosOptions
|
||||
this.axiosOptions
|
||||
)
|
||||
return response.data
|
||||
|
||||
// Array of blocks.
|
||||
} else if (Array.isArray(address)) {
|
||||
const options = {
|
||||
const options1 = {
|
||||
method: 'POST',
|
||||
url: `${this.restURL}util/validateAddress`,
|
||||
data: {
|
||||
addresses: address
|
||||
},
|
||||
headers: {
|
||||
authorization: `Token ${this.apiToken}`
|
||||
}
|
||||
// headers: {
|
||||
// authorization: `Token ${this.apiToken}`
|
||||
// }
|
||||
}
|
||||
const options = Object.assign({}, options1, this.axiosOptions)
|
||||
const response = await axios(options)
|
||||
|
||||
return response.data
|
||||
|
||||
+192
-199
@@ -8,19 +8,23 @@
|
||||
// Local libraries
|
||||
const Electrumx = require('./electrumx')
|
||||
const Slp = require('./slp/slp')
|
||||
const PsfSlpIndexer = require('./psf-slp-indexer')
|
||||
const BigNumber = require('bignumber.js')
|
||||
|
||||
class UTXO {
|
||||
constructor (config) {
|
||||
constructor (config = {}) {
|
||||
// Encapsulate dependencies for easier mocking.
|
||||
this.electrumx = new Electrumx(config)
|
||||
this.slp = new Slp(config)
|
||||
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
||||
this.BigNumber = BigNumber
|
||||
}
|
||||
|
||||
/**
|
||||
* @api Utxo.get() get()
|
||||
* @apiName get
|
||||
* @apiGroup UTXO
|
||||
* @apiDescription Get UTXOs for an address
|
||||
* @apiDescription Get UTXOs for an address (from psf-slp-indexer)
|
||||
*
|
||||
* Given an address, this function will return an object with thre following
|
||||
* properties:
|
||||
@@ -37,10 +41,6 @@ class UTXO {
|
||||
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
|
||||
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
|
||||
*
|
||||
* Note: You can pass in an optional second Boolean argument. The default
|
||||
* `false` will use the normal waterfall validation method. Set to `true`,
|
||||
* SLP UTXOs will be validated with the whitelist filtered SLPDB. This will
|
||||
* result is many more UTXOs in the `nullUtxos` array.
|
||||
*
|
||||
* @apiExample Example usage:
|
||||
* (async () => {
|
||||
@@ -67,215 +67,208 @@ class UTXO {
|
||||
* "isValid": false
|
||||
* }
|
||||
* ],
|
||||
* "nullUtxos": [],
|
||||
* "slpUtxos": {
|
||||
* "type1": {
|
||||
* "mintBatons": [
|
||||
* {
|
||||
* "height": 674512,
|
||||
* "tx_hash": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
|
||||
* "tx_pos": 2,
|
||||
* "value": 546,
|
||||
* "txid": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
|
||||
* "vout": 2,
|
||||
* "utxoType": "minting-baton",
|
||||
* "tokenId": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
|
||||
* "tokenTicker": "SLPTEST",
|
||||
* "tokenName": "SLP Test Token",
|
||||
* "tokenDocumentUrl": "https://FullStack.cash",
|
||||
* "tokenDocumentHash": "",
|
||||
* "decimals": 8,
|
||||
* "tokenType": 1,
|
||||
* "isValid": true
|
||||
* }
|
||||
* ],
|
||||
* "tokens": [
|
||||
* {
|
||||
* "height": 674512,
|
||||
* "tx_hash": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
|
||||
* "tx_pos": 1,
|
||||
* "value": 546,
|
||||
* "txid": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
|
||||
* "vout": 1,
|
||||
* "utxoType": "token",
|
||||
* "tokenQty": "100",
|
||||
* "tokenId": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
|
||||
* "tokenTicker": "SLPTEST",
|
||||
* "tokenName": "SLP Test Token",
|
||||
* "tokenDocumentUrl": "https://FullStack.cash",
|
||||
* "tokenDocumentHash": "",
|
||||
* "decimals": 8,
|
||||
* "tokenType": 1,
|
||||
* "isValid": true
|
||||
* }
|
||||
* ]
|
||||
* },
|
||||
* "nft": {
|
||||
* "groupMintBatons": [
|
||||
* {
|
||||
* "height": 674513,
|
||||
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
||||
* "tx_pos": 2,
|
||||
* "value": 546,
|
||||
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
||||
* "vout": 2,
|
||||
* "utxoType": "minting-baton",
|
||||
* "transactionType": "mint",
|
||||
* "tokenId": "a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395",
|
||||
* "tokenType": 129,
|
||||
* "tokenTicker": "NFTGT",
|
||||
* "tokenName": "NFT Test Group Token",
|
||||
* "tokenDocumentUrl": "https://FullStack.cash",
|
||||
* "tokenDocumentHash": "",
|
||||
* "decimals": 0,
|
||||
* "mintBatonVout": 2,
|
||||
* "isValid": true
|
||||
* }
|
||||
* ],
|
||||
* "groupTokens": [
|
||||
* {
|
||||
* "height": 674513,
|
||||
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
||||
* "tx_pos": 1,
|
||||
* "value": 546,
|
||||
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
|
||||
* "vout": 1,
|
||||
* "utxoType": "token",
|
||||
* "tokenQty": "10",
|
||||
* "transactionType": "mint",
|
||||
* "tokenId": "a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395",
|
||||
* "tokenType": 129,
|
||||
* "tokenTicker": "NFTGT",
|
||||
* "tokenName": "NFT Test Group Token",
|
||||
* "tokenDocumentUrl": "https://FullStack.cash",
|
||||
* "tokenDocumentHash": "",
|
||||
* "decimals": 0,
|
||||
* "mintBatonVout": 2,
|
||||
* "isValid": true
|
||||
* }
|
||||
* ],
|
||||
* "tokens": [
|
||||
* {
|
||||
* "height": 674512,
|
||||
* "tx_hash": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
|
||||
* "tx_pos": 1,
|
||||
* "value": 546,
|
||||
* "txid": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
|
||||
* "vout": 1,
|
||||
* "utxoType": "token",
|
||||
* "tokenQty": "1",
|
||||
* "tokenId": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
|
||||
* "tokenTicker": "NFT004",
|
||||
* "tokenName": "NFT Child",
|
||||
* "tokenDocumentUrl": "https://FullStack.cash",
|
||||
* "tokenDocumentHash": "",
|
||||
* "decimals": 0,
|
||||
* "tokenType": 65,
|
||||
* "isValid": true
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ]
|
||||
*
|
||||
*
|
||||
*/
|
||||
async get (address, useWhitelist = false) {
|
||||
// This version of get() uses the psf-slp-indexer. It will replace the older
|
||||
// get() function that uses SLPDB.
|
||||
// TODO: NFT UTXOs are identified as non-token UTXOs, which will cause a wallet
|
||||
// to burn them. The psf-slp-indexer needs to be updated to mark these UTXOs.
|
||||
async get (address) {
|
||||
try {
|
||||
// Convert address to an array if it is a string.
|
||||
if (typeof address === 'string') address = [address]
|
||||
if (typeof address !== 'string') {
|
||||
throw new Error('address input must be a string')
|
||||
}
|
||||
|
||||
// Throw an error if there are more than 20 addresses passed in at a time.
|
||||
if (address.length > 20) throw new Error('Too many elements, 20 max.')
|
||||
|
||||
// Covert each address to a BCH address.
|
||||
const addr = address.map(elem => this.slp.Address.toCashAddress(elem))
|
||||
// Ensure the address is a BCH address.
|
||||
const addr = this.slp.Address.toCashAddress(address)
|
||||
|
||||
// Get the UTXOs associated with the address.
|
||||
const utxoData = await this.electrumx.utxo(addr)
|
||||
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
|
||||
const utxos = utxoData.utxos
|
||||
|
||||
// Hydate the utxos with token information.
|
||||
let hydratedUtxos
|
||||
if (useWhitelist) {
|
||||
hydratedUtxos = await this.slp.Utils.hydrateUtxosWL(utxoData.utxos)
|
||||
} else {
|
||||
hydratedUtxos = await this.slp.Utils.hydrateUtxos(utxoData.utxos)
|
||||
}
|
||||
// console.log(`hydratedUtxos: ${JSON.stringify(hydratedUtxos, null, 2)}`)
|
||||
let slpUtxos = []
|
||||
|
||||
const retAry = [] // Return array
|
||||
// Get SLP UTXOs from the psf-slp-indexer
|
||||
try {
|
||||
const slpUtxoData = await this.psfSlpIndexer.balance(addr)
|
||||
console.log(`slpUtxoData: ${JSON.stringify(slpUtxoData, null, 2)}`)
|
||||
|
||||
// Loop through each address.
|
||||
for (let i = 0; i < hydratedUtxos.slpUtxos.length; i++) {
|
||||
const thisAddr = hydratedUtxos.slpUtxos[i]
|
||||
slpUtxos = slpUtxoData.balance.utxos
|
||||
} catch (err) {
|
||||
// console.log('err: ', err)
|
||||
|
||||
const addrObj = {
|
||||
address: thisAddr.address
|
||||
// Exit quietly if address has no SLP UTXOs. Otherwise, throw the error.
|
||||
if (err.error && !err.error.includes('Key not found in database')) {
|
||||
throw err
|
||||
}
|
||||
|
||||
// Filter out the different types of UTXOs.
|
||||
addrObj.bchUtxos = thisAddr.utxos.filter(elem => elem.isValid === false)
|
||||
addrObj.nullUtxos = thisAddr.utxos.filter(elem => elem.isValid === null)
|
||||
|
||||
// Break down the SLP UTXOs.
|
||||
addrObj.slpUtxos = {
|
||||
type1: {},
|
||||
nft: {}
|
||||
}
|
||||
|
||||
// Token Type 1 Minting Batons.
|
||||
addrObj.slpUtxos.type1.mintBatons = thisAddr.utxos.filter(elem => {
|
||||
const isValid = elem.isValid === true
|
||||
const tokenTypeIs1 = elem.tokenType === 1
|
||||
const isMintingBaton = elem.utxoType === 'minting-baton'
|
||||
|
||||
return isValid && tokenTypeIs1 && isMintingBaton
|
||||
})
|
||||
|
||||
// Token Type 1 tokens.
|
||||
addrObj.slpUtxos.type1.tokens = thisAddr.utxos.filter(elem => {
|
||||
const isValid = elem.isValid === true
|
||||
const tokenTypeIs1 = elem.tokenType === 1
|
||||
const isToken = elem.utxoType === 'token'
|
||||
|
||||
return isValid && tokenTypeIs1 && isToken
|
||||
})
|
||||
|
||||
// NFT Group Minting Batons
|
||||
addrObj.slpUtxos.nft.groupMintBatons = thisAddr.utxos.filter(elem => {
|
||||
const isValid = elem.isValid === true
|
||||
const tokenTypeIs129 = elem.tokenType === 129
|
||||
const isMintingBaton = elem.utxoType === 'minting-baton'
|
||||
|
||||
return isValid && tokenTypeIs129 && isMintingBaton
|
||||
})
|
||||
|
||||
// NFT Group tokens
|
||||
addrObj.slpUtxos.nft.groupTokens = thisAddr.utxos.filter(elem => {
|
||||
const isValid = elem.isValid === true
|
||||
const tokenTypeIs129 = elem.tokenType === 129
|
||||
const isToken = elem.utxoType === 'token'
|
||||
|
||||
return isValid && tokenTypeIs129 && isToken
|
||||
})
|
||||
|
||||
// NFT (Child) tokens
|
||||
addrObj.slpUtxos.nft.tokens = thisAddr.utxos.filter(elem => {
|
||||
const isValid = elem.isValid === true
|
||||
const tokenTypeIs65 = elem.tokenType === 65
|
||||
const isToken = elem.utxoType === 'token'
|
||||
|
||||
return isValid && tokenTypeIs65 && isToken
|
||||
})
|
||||
|
||||
retAry.push(addrObj)
|
||||
}
|
||||
|
||||
return retAry
|
||||
// Loop through the Fulcrum UTXOs.
|
||||
for (let i = 0; i < utxos.length; i++) {
|
||||
const thisUtxo = utxos[i]
|
||||
|
||||
// Loop through the UTXOs from psf-slp-indexer.
|
||||
for (let j = 0; j < slpUtxos.length; j++) {
|
||||
const thisSlpUtxo = slpUtxos[j]
|
||||
|
||||
// If the non-hydrated UTXO matches the SLP UTXO, then combine the data
|
||||
// and mark the UTXO as an SLP token.
|
||||
if (
|
||||
thisUtxo.tx_hash === thisSlpUtxo.txid &&
|
||||
thisUtxo.tx_pos === thisSlpUtxo.vout
|
||||
) {
|
||||
thisUtxo.txid = thisUtxo.tx_hash
|
||||
thisUtxo.vout = thisUtxo.tx_pos
|
||||
thisUtxo.isSlp = true
|
||||
thisUtxo.type = thisSlpUtxo.type
|
||||
thisUtxo.qty = thisSlpUtxo.qty
|
||||
thisUtxo.tokenId = thisSlpUtxo.tokenId
|
||||
thisUtxo.address = thisSlpUtxo.address
|
||||
thisUtxo.tokenType = thisSlpUtxo.tokenType
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no match, then this is a normal BCH UTXO. Mark it as such.
|
||||
if (!thisUtxo.isSlp) {
|
||||
thisUtxo.txid = thisUtxo.tx_hash
|
||||
thisUtxo.vout = thisUtxo.tx_pos
|
||||
thisUtxo.address = addr
|
||||
|
||||
// Check the transaction to see if its a 'null' token, ignored by
|
||||
// the indexer.
|
||||
const txData = await this.psfSlpIndexer.tx(thisUtxo.tx_hash)
|
||||
// console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
|
||||
if (txData.txData.isValidSlp === null) {
|
||||
thisUtxo.isSlp = null
|
||||
} else {
|
||||
thisUtxo.isSlp = false
|
||||
}
|
||||
// console.log(`thisUtxo.isSlp: ${thisUtxo.isSlp}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Get token UTXOs
|
||||
let type1TokenUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'token' && x.tokenType === 1
|
||||
)
|
||||
|
||||
// Hydrate the UTXOs with additional token data.
|
||||
type1TokenUtxos = await this.hydrateTokenData(type1TokenUtxos)
|
||||
|
||||
// Collect and hydrate any type1 baton UTXOs
|
||||
const bchUtxos = utxos.filter(x => x.isSlp === false)
|
||||
let type1BatonUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'baton' && x.tokenType === 1
|
||||
)
|
||||
type1BatonUtxos = await this.hydrateTokenData(type1BatonUtxos)
|
||||
|
||||
// Collect and hydrate NFT Group tokens
|
||||
let nftGroupTokenUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'token' && x.tokenType === 129
|
||||
)
|
||||
nftGroupTokenUtxos = await this.hydrateTokenData(nftGroupTokenUtxos)
|
||||
|
||||
// Collect and hydrate any Group baton UTXOs
|
||||
let groupBatonUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'baton' && x.tokenType === 129
|
||||
)
|
||||
groupBatonUtxos = await this.hydrateTokenData(groupBatonUtxos)
|
||||
|
||||
// Collect and hydrate NFT child tokens
|
||||
let nftChildTokenUtxos = utxos.filter(
|
||||
x => x.isSlp === true && x.type === 'token' && x.tokenType === 65
|
||||
)
|
||||
nftChildTokenUtxos = await this.hydrateTokenData(nftChildTokenUtxos)
|
||||
|
||||
// Isolate any UTXOs that are marked null by the SLP indexer.
|
||||
const nullUtxos = utxos.filter(x => x.isSlp === null)
|
||||
|
||||
const outObj = {
|
||||
address: addr,
|
||||
bchUtxos,
|
||||
slpUtxos: {
|
||||
type1: {
|
||||
tokens: type1TokenUtxos,
|
||||
mintBatons: type1BatonUtxos
|
||||
},
|
||||
group: {
|
||||
tokens: nftGroupTokenUtxos,
|
||||
mintBatons: groupBatonUtxos
|
||||
},
|
||||
nft: {
|
||||
tokens: nftChildTokenUtxos
|
||||
}
|
||||
},
|
||||
nullUtxos
|
||||
}
|
||||
|
||||
return outObj
|
||||
} catch (err) {
|
||||
console.error('Error in bchjs.utxo.get()')
|
||||
console.error('Error in bchjs.Utxo.get(): ', err)
|
||||
|
||||
if (err.error) throw new Error(err.error)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
// Hydrate an array of token UTXOs with token information.
|
||||
// Returns an array of token UTXOs with additional data.
|
||||
async hydrateTokenData (utxoAry) {
|
||||
try {
|
||||
// console.log('utxoAry: ', utxoAry)
|
||||
|
||||
// Create a list of token IDs without duplicates.
|
||||
let tokenIds = utxoAry.map(x => x.tokenId)
|
||||
|
||||
// Remove duplicates. https://stackoverflow.com/questions/9229645/remove-duplicate-values-from-js-array
|
||||
tokenIds = [...new Set(tokenIds)]
|
||||
// console.log('tokenIds: ', tokenIds)
|
||||
|
||||
// Get Genesis data for each tokenId
|
||||
const genesisData = []
|
||||
for (let i = 0; i < tokenIds.length; i++) {
|
||||
const thisTokenId = tokenIds[i]
|
||||
const thisTokenData = await this.psfSlpIndexer.tokenStats(thisTokenId)
|
||||
// console.log('thisTokenData: ', thisTokenData)
|
||||
|
||||
genesisData.push(thisTokenData)
|
||||
}
|
||||
// console.log('genesisData: ', genesisData)
|
||||
|
||||
// Hydrate each token UTXO with data from the genesis transaction.
|
||||
for (let i = 0; i < utxoAry.length; i++) {
|
||||
const thisUtxo = utxoAry[i]
|
||||
|
||||
// Get the genesis data for this token.
|
||||
const genData = genesisData.filter(
|
||||
x => x.tokenData.tokenId === thisUtxo.tokenId
|
||||
)
|
||||
// console.log('genData: ', genData)
|
||||
|
||||
thisUtxo.ticker = genData[0].tokenData.ticker
|
||||
thisUtxo.name = genData[0].tokenData.name
|
||||
thisUtxo.documentUri = genData[0].tokenData.documentUri
|
||||
thisUtxo.documentHash = genData[0].tokenData.documentHash
|
||||
thisUtxo.decimals = genData[0].tokenData.decimals
|
||||
|
||||
if (thisUtxo.type !== 'baton') {
|
||||
// Calculate the real token quantity
|
||||
const qty = new BigNumber(thisUtxo.qty).dividedBy(
|
||||
10 ** parseInt(thisUtxo.decimals)
|
||||
)
|
||||
thisUtxo.qtyStr = qty.toString()
|
||||
|
||||
// tokenQty is property expected by SLP.tokentype1.js library
|
||||
thisUtxo.tokenQty = thisUtxo.qtyStr
|
||||
}
|
||||
}
|
||||
|
||||
return utxoAry
|
||||
} catch (err) {
|
||||
console.log('Error in hydrateTokenData()')
|
||||
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,51 @@ 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)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getBlockchainInfo', () => {
|
||||
it('should get info about the blockchain', async () => {
|
||||
const result = await bchjs.Blockchain.getBlockchainInfo()
|
||||
|
||||
console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
/*
|
||||
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(1000)
|
||||
|
||||
bchjs = new BCHJS({ restURL: process.env.RESTURL })
|
||||
})
|
||||
|
||||
describe('#util', () => {
|
||||
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)
|
||||
assert.equal(data[4].isValid, null)
|
||||
|
||||
// Token send on ABC network
|
||||
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
|
||||
assert.equal(data[5].isValid, true)
|
||||
|
||||
// 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
|
||||
// "a0d6406eecfd8634158efa9314ff15b4cbf451938e9dc7b5678c46b41eabc6ed" // Mint baton
|
||||
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
|
||||
]
|
||||
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txids)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.equal(result[0].valid, null)
|
||||
assert.equal(result[1].valid, null)
|
||||
assert.equal(result[2].valid, true)
|
||||
// assert.equal(result[2].valid, null)
|
||||
assert.equal(result[3].valid, true)
|
||||
assert.equal(result[4].valid, null)
|
||||
})
|
||||
})
|
||||
|
||||
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].valid, null)
|
||||
assert.equal(result[1].valid, null)
|
||||
assert.equal(result[2].valid, true)
|
||||
// assert.equal(result[2].valid, null)
|
||||
assert.equal(result[3].valid, true)
|
||||
assert.equal(result[4].valid, null)
|
||||
assert.equal(result[5].valid, true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
@@ -2,18 +2,19 @@
|
||||
Integration tests for the utxo.js library.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
// const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
// const BCHJS = require('../../../../src/bch-js')
|
||||
// const bchjs = new BCHJS()
|
||||
|
||||
describe('#UTXO', () => {
|
||||
beforeEach(async () => {
|
||||
// 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', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
@@ -31,6 +32,7 @@ describe('#UTXO', () => {
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
})
|
||||
*/
|
||||
})
|
||||
|
||||
function sleep (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)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
Integration tests for the psf-slp-indexer.js library
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
let bchjs
|
||||
|
||||
describe('#psf-slp-indexer', () => {
|
||||
beforeEach(async () => {
|
||||
// Introduce a delay so that the BVT doesn't trip the rate limits.
|
||||
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe('#status', () => {
|
||||
it('should return the status of the indexer.', async () => {
|
||||
const result = await bchjs.PsfSlpIndexer.status()
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.status, 'startBlockHeight')
|
||||
assert.property(result.status, 'syncedBlockHeight')
|
||||
assert.property(result.status, 'chainBlockHeight')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#balance', () => {
|
||||
it('should get balance data for an address.', async () => {
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.balance, 'utxos')
|
||||
assert.property(result.balance, 'txs')
|
||||
assert.property(result.balance, 'balances')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tokenStats', () => {
|
||||
it('should get stats on a token, without tx history', async () => {
|
||||
const tokenId =
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
})
|
||||
|
||||
it('should get stats on a token, with tx history', async () => {
|
||||
const tokenId =
|
||||
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId, true)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'txs')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tx', () => {
|
||||
it('should get hydrated tx data for an SLP transaction', async () => {
|
||||
const txid =
|
||||
'947ccb2a0d62ca287bc4b0993874ab0f9f6afd454193e631e2bf84dca66731fc'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, true)
|
||||
})
|
||||
|
||||
it('should mark non-SLP transaction as false', async () => {
|
||||
const txid =
|
||||
'03d6e6b63647ce7b02ecc73dc6d41b485be14a3e20eed4474b8a840358ddf14e'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
// FlexUSD transactions
|
||||
// Currently FlexUSD UTXOs are reported as invalid SLP UTXOs, which means
|
||||
// the wallet will burn them. There is a TODO in the code. This test will
|
||||
// need to be changed when it is done.
|
||||
it('should mark blacklisted token as null', async () => {
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
assert.equal(result.txData.isValidSlp, null)
|
||||
})
|
||||
|
||||
it('should throw error for non-existent txid', async () => {
|
||||
try {
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85e'
|
||||
|
||||
await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
|
||||
assert.include(err.message, 'No such mempool or blockchain transaction')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// Promise-based sleep function
|
||||
function sleep (ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, 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(3000)
|
||||
})
|
||||
|
||||
/*
|
||||
|
||||
+242
-213
@@ -25,245 +25,274 @@ 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(3000)
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
describe('#util', () => {
|
||||
describe('#decodeOpReturn', () => {
|
||||
it('should decode a NFT Child transaction', async () => {
|
||||
const txid =
|
||||
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
|
||||
if (process.env.TESTSLP) {
|
||||
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)}`)
|
||||
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.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')
|
||||
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 = [
|
||||
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
|
||||
{
|
||||
note: 'Malformed SLP tx',
|
||||
tx_hash:
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
|
||||
assert.equal(data[0].isValid, false)
|
||||
|
||||
// Normal TX (non-SLP)
|
||||
{
|
||||
note: 'Normal TX (non-SLP)',
|
||||
tx_hash:
|
||||
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
|
||||
tx_pos: 0,
|
||||
value: 400000
|
||||
},
|
||||
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
|
||||
assert.equal(data[1].isValid, false)
|
||||
|
||||
// Valid PSF SLP tx
|
||||
{
|
||||
note: 'Valid PSF SLP tx',
|
||||
tx_hash:
|
||||
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
|
||||
assert.equal(data[2].isValid, true)
|
||||
|
||||
// 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
|
||||
}
|
||||
]
|
||||
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
|
||||
assert.equal(data[3].isValid, true)
|
||||
|
||||
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
|
||||
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
|
||||
// Token send on BCHN network
|
||||
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
|
||||
assert.equal(data[4].isValid, true)
|
||||
|
||||
// Malformed SLP tx
|
||||
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
|
||||
assert.equal(data[0].isValid, false)
|
||||
// Token send on ABC network
|
||||
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
|
||||
assert.equal(data[5].isValid, null)
|
||||
|
||||
// Normal TX (non-SLP)
|
||||
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
|
||||
assert.equal(data[1].isValid, false)
|
||||
// Known invalid SLP token send of PSF tokens
|
||||
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
|
||||
assert.equal(data[6].isValid, false)
|
||||
})
|
||||
})
|
||||
|
||||
// Valid PSF SLP tx
|
||||
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
|
||||
assert.equal(data[2].isValid, true)
|
||||
describe('#validateTxid3', () => {
|
||||
it('should invalidate a known invalid TXID', async () => {
|
||||
const txid =
|
||||
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
|
||||
|
||||
// Valid SLP token not in whitelist
|
||||
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
|
||||
assert.equal(data[3].isValid, true)
|
||||
const result = await bchjs.SLP.Utils.validateTxid3(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Token send on BCHN network
|
||||
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
|
||||
assert.equal(data[4].isValid, true)
|
||||
assert.isArray(result)
|
||||
|
||||
// Token send on ABC network
|
||||
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
|
||||
assert.equal(data[5].isValid, null)
|
||||
assert.property(result[0], 'txid')
|
||||
assert.equal(result[0].txid, txid)
|
||||
|
||||
// Known invalid SLP token send of PSF tokens
|
||||
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
|
||||
assert.equal(data[6].isValid, false)
|
||||
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() but null in validateTxid3()
|
||||
assert.equal(result[3].txid, txids[3])
|
||||
assert.equal(result[3].valid, null)
|
||||
|
||||
// Note: This should change from null to true once SLPDB finishes indexing.
|
||||
assert.equal(result[4].txid, txids[4])
|
||||
assert.equal(result[4].valid, null)
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
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() but null in validateTxid3()
|
||||
assert.equal(result[3].txid, txids[3])
|
||||
assert.equal(result[3].valid, null)
|
||||
|
||||
// Note: This should change from null to true once SLPDB finishes indexing.
|
||||
assert.equal(result[4].txid, txids[4])
|
||||
assert.equal(result[4].valid, null)
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
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
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Integration tests for the transaction.js library.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe('#Transaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should get a tx details for a non-SLP TX with an OP_RETURN', async () => {
|
||||
const txid =
|
||||
'01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
|
||||
|
||||
const result = await bchjs.Transaction.get(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result.txData, 'txid')
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
it('should handle a coinbase transaction', async () => {
|
||||
const txid = 'cca1d2dd3a533d2f501448dec03face2cb2814afd59a533a611e9a2909f2302b'
|
||||
|
||||
const details = await bchjs.Transaction.get(txid)
|
||||
// console.log(`details: ${JSON.stringify(details, null, 2)}`)
|
||||
|
||||
assert.property(details.txData, 'txid')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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)
|
||||
|
||||
@@ -6,117 +6,122 @@ const assert = require('chai').assert
|
||||
|
||||
const BCHJS = require('../../../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
// const bchjs = new BCHJS({ restURL: 'http://192.168.2.129:3000/v5/' })
|
||||
|
||||
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(3000)
|
||||
})
|
||||
|
||||
describe('#hydrateTokenData', () => {
|
||||
it('should hydrate token UTXOs', async () => {
|
||||
const utxos = [
|
||||
{
|
||||
txid: '384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '10000000',
|
||||
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '100000000',
|
||||
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '4764',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '238',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
}
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.hydrateTokenData(utxos)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.property(result[0], 'ticker')
|
||||
assert.property(result[0], 'name')
|
||||
assert.property(result[0], 'qtyStr')
|
||||
assert.property(result[0], 'documentUri')
|
||||
assert.property(result[0], 'documentHash')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
it('should hydrate address with BCH and SLP UTXOs', async () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
// Assert expected properties exist.
|
||||
assert.property(result, 'address')
|
||||
assert.property(result, 'bchUtxos')
|
||||
assert.property(result, 'slpUtxos')
|
||||
assert.property(result.slpUtxos, 'type1')
|
||||
assert.property(result.slpUtxos, 'nft')
|
||||
assert.property(result, 'nullUtxos')
|
||||
|
||||
assert.isAbove(result.bchUtxos.length, 0)
|
||||
assert.isAbove(result.slpUtxos.type1.tokens.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
|
||||
})
|
||||
|
||||
it('should handle an array of addresses', async () => {
|
||||
const addr = [
|
||||
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
||||
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
]
|
||||
it('should handle Type1 minting batons', async () => {
|
||||
const addr = 'simpleledger:qz5l5yzz9r09hw9aadcz53elp2knx6gyg5qk3s8md7'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert that minting batons are correctly identified.
|
||||
assert.isAbove(result.slpUtxos.type1.mintBatons.length, 0)
|
||||
})
|
||||
|
||||
it('should return UTXOs for address with no SLP tokens', async () => {
|
||||
const addr = 'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7'
|
||||
|
||||
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.isAbove(result.bchUtxos.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
||||
})
|
||||
|
||||
it('should handle NFTs and minting batons', async () => {
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
it('should filter Group tokens and mint batons', async () => {
|
||||
const addr = 'bitcoincash:qzeqcrpe5fcnslv8rfqjq4gh4gzdwytmdc4qmh0ztv'
|
||||
|
||||
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)
|
||||
assert.isAbove(result.slpUtxos.group.tokens.length, 0)
|
||||
assert.isAbove(result.slpUtxos.group.mintBatons.length, 0)
|
||||
})
|
||||
|
||||
it('should use the whitelist when flag is set', async () => {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
const useWhitelist = true
|
||||
|
||||
const result = await bchjs.Utxo.get(addr, useWhitelist)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
|
||||
// Most token UTXOs should end up in the nullUtxos array.
|
||||
assert.isAbove(result[0].bchUtxos.length, 0)
|
||||
assert.isAbove(result[0].nullUtxos.length, 1)
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
// it('should filter NFTs', async () => {
|
||||
// const addr = 'bitcoincash:qq7vp2kvejsql898a2760kuq6xz00h0a5vs4h72ysz'
|
||||
//
|
||||
// const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// // assert.isAbove(result.slpUtxos.group.tokens.length, 0)
|
||||
// // assert.isAbove(result.slpUtxos.group.mintBatons.length, 0)
|
||||
// })
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
Integration tests for the bchjs. Only covers calls made to
|
||||
rest.bitcoin.com.
|
||||
Integration tests for the bchjs.
|
||||
|
||||
TODO
|
||||
*/
|
||||
@@ -20,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', () => {
|
||||
@@ -192,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')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -252,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)}`)
|
||||
@@ -263,43 +265,46 @@ describe('#rawtransaction', () => {
|
||||
assert.equal(result.length, 1)
|
||||
assert.property(result[0], 'vin')
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'value')
|
||||
})
|
||||
})
|
||||
|
||||
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)}`)
|
||||
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'value')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getTxDataSlp', () => {
|
||||
it('should return tx data with SLP information', async () => {
|
||||
const txid = 'b438855cfcab64516b44097d7212df9cdb99226c8d7c7ab504d35fcfd834cb5b'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxDataSlp(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result.vin[0], 'address')
|
||||
assert.property(result.vin[0], 'tokenQty')
|
||||
|
||||
assert.equal(result.vin[0].tokenQty, null)
|
||||
assert.equal(result.vin[1].tokenQty, '100000000000')
|
||||
})
|
||||
|
||||
it('should handle non-slp tx', async () => {
|
||||
const txid = '04a6aca328af2445327015895b8da9766093b1989b52e477559759eb8072fc0a'
|
||||
|
||||
const result = await bchjs.RawTransactions.getTxDataSlp(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
// describe('#getTxDataSlp', () => {
|
||||
// it('should return tx data with SLP information', async () => {
|
||||
// const txid = 'b438855cfcab64516b44097d7212df9cdb99226c8d7c7ab504d35fcfd834cb5b'
|
||||
//
|
||||
// const result = await bchjs.RawTransactions.getTxDataSlp(txid)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.property(result.vin[0], 'address')
|
||||
// assert.property(result.vin[0], 'tokenQty')
|
||||
//
|
||||
// assert.equal(result.vin[0].tokenQty, null)
|
||||
// assert.equal(result.vin[1].tokenQty, '100000000000')
|
||||
// })
|
||||
//
|
||||
// it('should handle non-slp tx', async () => {
|
||||
// const txid = '04a6aca328af2445327015895b8da9766093b1989b52e477559759eb8072fc0a'
|
||||
//
|
||||
// const result = await bchjs.RawTransactions.getTxDataSlp(txid)
|
||||
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
//
|
||||
// assert.equal(result, false)
|
||||
// })
|
||||
// })
|
||||
})
|
||||
|
||||
function sleep (ms) {
|
||||
|
||||
+476
-940
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
Integration tests for the transaction.js library.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
describe('#Transaction', () => {
|
||||
beforeEach(async () => {
|
||||
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
|
||||
})
|
||||
|
||||
if (process.env.TESTSLP) {
|
||||
describe('#getOld', () => {
|
||||
it('should get details about a non-SLP transaction', async () => {
|
||||
const txid =
|
||||
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
|
||||
|
||||
const result = await bchjs.Transaction.getOld(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.property(result, 'isValidSLPTx')
|
||||
assert.equal(result.isValidSLPTx, false)
|
||||
})
|
||||
|
||||
it('should get details about a SLP transaction', async () => {
|
||||
const txid =
|
||||
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
|
||||
|
||||
const result = await bchjs.Transaction.getOld(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)
|
||||
})
|
||||
|
||||
// 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.getOld(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,56 @@
|
||||
/*
|
||||
Unit tests for eCash library.
|
||||
*/
|
||||
|
||||
// Global npm libraries
|
||||
const assert = require('chai').assert
|
||||
const Ecash = require('../../src/ecash')
|
||||
const uut = new Ecash()
|
||||
|
||||
describe('#eCash', () => {
|
||||
describe('#toSatoshi', () => {
|
||||
it('should convert XEC to satoshis', () => {
|
||||
const xec = 10704.35
|
||||
|
||||
const result = uut.toSatoshi(xec)
|
||||
|
||||
assert.equal(result, 1070435)
|
||||
})
|
||||
|
||||
it('should throw an error if input is not a number', () => {
|
||||
try {
|
||||
uut.toSatoshi('test')
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(
|
||||
err.message,
|
||||
'input must be a floating number representing XEC'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#toXec', () => {
|
||||
it('should convert satoshis to XEC', () => {
|
||||
const sats = 1070435
|
||||
|
||||
const result = uut.toXec(sats)
|
||||
|
||||
assert.equal(result, 10704.35)
|
||||
})
|
||||
|
||||
it('should throw an error if input is not a number', () => {
|
||||
try {
|
||||
uut.toXec('test')
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(
|
||||
err.message,
|
||||
'input must be a floating number representing satoshis'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
Mocking data for unit tests for the Ninsight library.
|
||||
*/
|
||||
|
||||
const utxo = {
|
||||
utxos: [
|
||||
{
|
||||
txid: '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
|
||||
vout: 0,
|
||||
amount: 0.00001,
|
||||
satoshis: 1000,
|
||||
height: 602405,
|
||||
confirmations: 36459
|
||||
}
|
||||
],
|
||||
legacyAddress: '15NCRBJsHaJy8As5bX1oh2YauRejnZ1MKF',
|
||||
cashAddress: 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9',
|
||||
slpAddress: 'simpleledger:qqh793x9au6ehvh7r2zflzguanlme760wuwzunhjfm',
|
||||
scriptPubKey: '76a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788ac',
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 2fe2c4c5ef359bb2fe1a849f891cecffbcfb4f77 OP_EQUALVERIFY OP_CHECKSIG'
|
||||
}
|
||||
|
||||
const unconfirmed = {
|
||||
utxos: [
|
||||
{
|
||||
txid: '3904ffe6f8fba4ceda5e887130f60fcb18bdc7dcee10392a57f89475c5c108f1',
|
||||
vout: 0,
|
||||
amount: 0.03608203,
|
||||
satoshis: 3608203,
|
||||
confirmations: 0,
|
||||
ts: 1559670801
|
||||
}
|
||||
],
|
||||
legacyAddress: '1AyWs8U4HUnTLmxxFiGoJbsXauRsvBrcKW',
|
||||
cashAddress: 'bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5',
|
||||
slpAddress: 'simpleledger:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rkuk8mdxlf2',
|
||||
scriptPubKey: '76a9146d695af951760b837c8e58b045b098f9c17e23b788ac'
|
||||
}
|
||||
|
||||
const utxoPost = [utxo, utxo]
|
||||
const unconfirmedPost = [unconfirmed, unconfirmed]
|
||||
|
||||
const transactions = {
|
||||
pagesTotal: 1,
|
||||
txs: [
|
||||
{
|
||||
txid: 'ec7bc8349386e3e1939bbdc4f8092fdbdd6a380734e68486b558cd594c451d5b',
|
||||
version: 2,
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
txid:
|
||||
'4f1fc57c33659628938db740449bf92fb75799e1d5750a4aeef80eb52d6df1e0',
|
||||
vout: 0,
|
||||
sequence: 4294967295,
|
||||
n: 0,
|
||||
scriptSig: {
|
||||
hex:
|
||||
'483045022100a3662a19ae384a1ceddea57765e425e61b04823e976d574da3911ac6b55d7f9b02200e571d985bce987675a2d58587a346fa40c39f4df13dc88548a92c52d5b24422412103f953f7630acc15bd3f5078c698f3af777286ae955b57e4857c158f75d87adb5f',
|
||||
asm:
|
||||
'3045022100a3662a19ae384a1ceddea57765e425e61b04823e976d574da3911ac6b55d7f9b02200e571d985bce987675a2d58587a346fa40c39f4df13dc88548a92c52d5b24422[ALL|FORKID] 03f953f7630acc15bd3f5078c698f3af777286ae955b57e4857c158f75d87adb5f'
|
||||
},
|
||||
addr: '17HPz8RQ4XM6mjre6aspvqyj1j648CZidM',
|
||||
valueSat: 1111,
|
||||
value: 0.00001111,
|
||||
doubleSpentTxID: null
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'126d62c299e7e14c66fe0b485d13082c23641f003690462046bc24ad2d1180c1',
|
||||
vout: 0,
|
||||
sequence: 4294967295,
|
||||
n: 1,
|
||||
scriptSig: {
|
||||
hex:
|
||||
'47304402203e3f923207111ff9bbd2fb5ab1a49a9145ad809ee0cad0e0ddaed64bfe38dc16022012ee288fb413bd500c63f8bb95e46b6b57d34762decd46b7188478a1c398eeda412103f953f7630acc15bd3f5078c698f3af777286ae955b57e4857c158f75d87adb5f',
|
||||
asm:
|
||||
'304402203e3f923207111ff9bbd2fb5ab1a49a9145ad809ee0cad0e0ddaed64bfe38dc16022012ee288fb413bd500c63f8bb95e46b6b57d34762decd46b7188478a1c398eeda[ALL|FORKID] 03f953f7630acc15bd3f5078c698f3af777286ae955b57e4857c158f75d87adb5f'
|
||||
},
|
||||
addr: '17HPz8RQ4XM6mjre6aspvqyj1j648CZidM',
|
||||
valueSat: 1000,
|
||||
value: 0.00001,
|
||||
doubleSpentTxID: null
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: '0.00001736',
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
hex: '76a914d96ac75ca8df9729d278da50ccd7355c5785444e88ac',
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 d96ac75ca8df9729d278da50ccd7355c5785444e OP_EQUALVERIFY OP_CHECKSIG',
|
||||
addresses: ['1LpbYkEM5cryfhs58tH8c93p4SGzit7UrP'],
|
||||
type: 'pubkeyhash'
|
||||
},
|
||||
spentTxId: null,
|
||||
spentIndex: null,
|
||||
spentHeight: null
|
||||
}
|
||||
],
|
||||
blockheight: -1,
|
||||
confirmations: 0,
|
||||
time: 1559673337,
|
||||
valueOut: 0.00001736,
|
||||
size: 339,
|
||||
valueIn: 0.00002111,
|
||||
fees: 0.00000375
|
||||
}
|
||||
],
|
||||
legacyAddress: '1LpbYkEM5cryfhs58tH8c93p4SGzit7UrP',
|
||||
cashAddress: 'bitcoincash:qrvk436u4r0ew2wj0rd9pnxhx4w90p2yfc29ta0d2n',
|
||||
currentPage: 0
|
||||
}
|
||||
|
||||
const transactionsPost = [transactions, transactions]
|
||||
|
||||
const details = {
|
||||
txid: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
version: 1,
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
coinbase: '04ffff001d02fd04',
|
||||
sequence: 4294967295,
|
||||
n: 0
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: '50.00000000',
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
hex:
|
||||
'4104f5eeb2b10c944c6b9fbcfff94c35bdeecd93df977882babc7f3a2cf7f5c81d3b09a68db7f0e04f21de5d4230e75e6dbe7ad16eefe0d4325a62067dc6f369446aac',
|
||||
asm:
|
||||
'04f5eeb2b10c944c6b9fbcfff94c35bdeecd93df977882babc7f3a2cf7f5c81d3b09a68db7f0e04f21de5d4230e75e6dbe7ad16eefe0d4325a62067dc6f369446a OP_CHECKSIG',
|
||||
addresses: ['1BW18n7MfpU35q4MTBSk8pse3XzQF8XvzT'],
|
||||
type: 'pubkeyhash',
|
||||
cashAddrs: ['bitcoincash:qpej6mkrwca4tvy2snq4crhrf88v4ljspysx0ueetk']
|
||||
},
|
||||
spentTxId: null,
|
||||
spentIndex: null,
|
||||
spentHeight: null
|
||||
}
|
||||
],
|
||||
blockhash:
|
||||
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
|
||||
blockheight: 1000,
|
||||
confirmations: 585610,
|
||||
time: 1232346882,
|
||||
blocktime: 1232346882,
|
||||
isCoinBase: true,
|
||||
valueOut: 50,
|
||||
size: 135
|
||||
}
|
||||
|
||||
const detailsPost = [details, details]
|
||||
|
||||
module.exports = {
|
||||
utxo,
|
||||
utxoPost,
|
||||
unconfirmed,
|
||||
unconfirmedPost,
|
||||
transactions,
|
||||
transactionsPost,
|
||||
details,
|
||||
detailsPost
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
Mocking data for unit tests for the PSF SLP INDEXER library.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const tokenStats = {
|
||||
tokenData: {
|
||||
type: 1,
|
||||
ticker: 'TP03',
|
||||
name: 'Test Plugin 03',
|
||||
tokenId: '13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
|
||||
documentUri: 'fullstack.cash',
|
||||
documentHash: '',
|
||||
decimals: 0,
|
||||
mintBatonIsActive: false,
|
||||
tokensInCirculationBN: '1',
|
||||
tokensInCirculationStr: '1',
|
||||
blockCreated: 722420,
|
||||
totalBurned: '0',
|
||||
totalMinted: '1',
|
||||
txs: [
|
||||
{
|
||||
txid:
|
||||
'13cad617d523c8eb4ab11fff19c010e0e0a4ea4360b58e0c8c955a45a146a669',
|
||||
height: 722420,
|
||||
type: 'GENESIS',
|
||||
qty: '1'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const txData = {
|
||||
txData: {
|
||||
txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
version: 2,
|
||||
size: 339,
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
txid:
|
||||
'8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
|
||||
vout: 0,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
|
||||
hex:
|
||||
'47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
|
||||
},
|
||||
sequence: 4294967295,
|
||||
address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
|
||||
value: 0.00051303,
|
||||
tokenQty: 0,
|
||||
tokenQtyStr: '0',
|
||||
tokenId: null
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
|
||||
hex:
|
||||
'6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
|
||||
type: 'nulldata'
|
||||
},
|
||||
tokenQtyStr: '0',
|
||||
tokenQty: 0
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
|
||||
blockhash:
|
||||
'0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
|
||||
confirmations: 97398,
|
||||
time: 1581773131,
|
||||
blocktime: 1581773131,
|
||||
blockheight: 622414,
|
||||
isSlpTx: true,
|
||||
tokenTxType: 'GENESIS',
|
||||
tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
tokenType: 1,
|
||||
tokenTicker: 'TROUT',
|
||||
tokenName: "Trout's test token",
|
||||
tokenDecimals: 2,
|
||||
tokenUri: 'troutsblog.com',
|
||||
tokenDocHash: '',
|
||||
isValidSlp: true
|
||||
}
|
||||
}
|
||||
const balance = {
|
||||
balance: {
|
||||
utxos: [
|
||||
{
|
||||
txid:
|
||||
'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '1800',
|
||||
tokenId:
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
|
||||
}
|
||||
],
|
||||
txs: [
|
||||
{
|
||||
txid:
|
||||
'078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
|
||||
height: 717796
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
|
||||
height: 717832
|
||||
}
|
||||
],
|
||||
balances: [
|
||||
{
|
||||
tokenId:
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
|
||||
qty: '1800'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const status = {
|
||||
status: {
|
||||
startBlockHeight: 543376,
|
||||
syncedBlockHeight: 722860,
|
||||
chainBlockHeight: 722679
|
||||
}
|
||||
}
|
||||
|
||||
const tokenData01 = {
|
||||
tokenType: 1,
|
||||
txType: 'MINT',
|
||||
tokenId: 'dd21be4532d93661e8ffe16db6535af0fb8ee1344d1fef81a193e2b4cfa9fbc9',
|
||||
mintBatonVout: 2,
|
||||
qty: {}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
tokenStats,
|
||||
txData,
|
||||
balance,
|
||||
status,
|
||||
tokenData01
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -0,0 +1,795 @@
|
||||
/*
|
||||
Mocking data used in the transaction-unit.js tests.
|
||||
*/
|
||||
|
||||
const nonSlpTxDetails = {
|
||||
txid: '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
|
||||
hash: '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
|
||||
version: 2,
|
||||
size: 225,
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
txid: '5f09d317e24c5d376f737a2711f3bd1d381abdb41743fff3819b4f76382e1eac',
|
||||
vout: 1,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c[ALL|FORKID] 038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9',
|
||||
hex:
|
||||
'473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9'
|
||||
},
|
||||
sequence: 4294967295,
|
||||
address: 'bitcoincash:qqxy8hycqe89j7wa79gnggq6z3gaqu2uvqy26xehfe',
|
||||
value: 0.00047504
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0.00001,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 2fe2c4c5ef359bb2fe1a849f891cecffbcfb4f77 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00046256,
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 2dbf5e1804c39a497b908c876097d63210c84902 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a9142dbf5e1804c39a497b908c876097d63210c8490288ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qqkm7hscqnpe5jtmjzxgwcyh6ceppjzfqg3jdn422e']
|
||||
}
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'0200000001ac1e2e38764f9b81f3ff4317b4bd1a381dbdf311277a736f375d4ce217d3095f010000006a473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9ffffffff02e8030000000000001976a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788acb0b40000000000001976a9142dbf5e1804c39a497b908c876097d63210c8490288ac00000000',
|
||||
blockhash: '0000000000000000010903a1fc4274499037c9339be9ec7338ee980331c20ce5',
|
||||
confirmations: 77741,
|
||||
time: 1569792892,
|
||||
blocktime: 1569792892
|
||||
}
|
||||
|
||||
const slpTxDetails = {
|
||||
txid: '266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1',
|
||||
hash: '266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1',
|
||||
version: 2,
|
||||
size: 479,
|
||||
locktime: 0,
|
||||
vin: [
|
||||
{
|
||||
txid: 'abc685f1f2a95f51e5e05a350f3fb9c74676e9f78c835b2a019c888ac0a2a736',
|
||||
vout: 2,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'3045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f7[ALL|FORKID] 0245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07ac',
|
||||
hex:
|
||||
'483045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f741210245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07ac'
|
||||
},
|
||||
sequence: 4294967295,
|
||||
address: 'bitcoincash:qzv7t2pzn2d0pklnetdjt65crh6fe8vnhuwvhsk2nn',
|
||||
value: 0.00000546
|
||||
},
|
||||
{
|
||||
txid: '58c8576404c01c23a224053307399483d3a070599b3e9eb6d45be9714b8d6856',
|
||||
vout: 1,
|
||||
scriptSig: {
|
||||
asm:
|
||||
'30430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db12753[ALL|FORKID] 0209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505',
|
||||
hex:
|
||||
'4630430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db1275341210209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505'
|
||||
},
|
||||
sequence: 4294967295,
|
||||
address: 'bitcoincash:qppzuxemgqyxf07nz3kan33gmc83mf3z3yz295c4s7',
|
||||
value: 0.68369626
|
||||
}
|
||||
],
|
||||
vout: [
|
||||
{
|
||||
value: 0,
|
||||
n: 0,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_RETURN 5262419 1 1145980243 497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7 0000000005f5e100 00005ad7e49d9100',
|
||||
hex:
|
||||
'6a04534c500001010453454e4420497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7080000000005f5e1000800005ad7e49d9100',
|
||||
type: 'nulldata'
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 1,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 36be3b7d185a85b6cf6fc61d63c16f2f10e54260 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a91436be3b7d185a85b6cf6fc61d63c16f2f10e5426088ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qqmtuwmarpdgtdk0dlrp6c7pduh3pe2zvqrkys2ex8']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.00000546,
|
||||
n: 2,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 99e5a8229a9af0dbf3cadb25ea981df49c9d93bf OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a91499e5a8229a9af0dbf3cadb25ea981df49c9d93bf88ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qzv7t2pzn2d0pklnetdjt65crh6fe8vnhuwvhsk2nn']
|
||||
}
|
||||
},
|
||||
{
|
||||
value: 0.68368564,
|
||||
n: 3,
|
||||
scriptPubKey: {
|
||||
asm:
|
||||
'OP_DUP OP_HASH160 422e1b3b400864bfd3146dd9c628de0f1da62289 OP_EQUALVERIFY OP_CHECKSIG',
|
||||
hex: '76a914422e1b3b400864bfd3146dd9c628de0f1da6228988ac',
|
||||
reqSigs: 1,
|
||||
type: 'pubkeyhash',
|
||||
addresses: ['bitcoincash:qppzuxemgqyxf07nz3kan33gmc83mf3z3yz295c4s7']
|
||||
}
|
||||
}
|
||||
],
|
||||
hex:
|
||||
'020000000236a7a2c08a889c012a5b838cf7e97646c7b93f0f355ae0e5515fa9f2f185c6ab020000006b483045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f741210245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07acffffffff56688d4b71e95bd4b69e3e9b5970a0d383943907330524a2231cc0046457c85801000000694630430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db1275341210209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505ffffffff040000000000000000406a04534c500001010453454e4420497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7080000000005f5e1000800005ad7e49d910022020000000000001976a91436be3b7d185a85b6cf6fc61d63c16f2f10e5426088ac22020000000000001976a91499e5a8229a9af0dbf3cadb25ea981df49c9d93bf88acb4381304000000001976a914422e1b3b400864bfd3146dd9c628de0f1da6228988ac00000000',
|
||||
blockhash: '0000000000000000015284202422a688554b7fc80c54f18122847a99c4f79607',
|
||||
confirmations: 76722,
|
||||
time: 1570392893,
|
||||
blocktime: 1570392893
|
||||
}
|
||||
|
||||
const mockOpReturnData01 = {
|
||||
tokenType: 1,
|
||||
txType: 'SEND',
|
||||
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
||||
amounts: ['100000000', '99883300000000']
|
||||
}
|
||||
|
||||
const mockOpReturnData02 = {
|
||||
tokenType: 1,
|
||||
txType: 'GENESIS',
|
||||
ticker: 'TOK-CH',
|
||||
name: 'TokyoCash',
|
||||
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
||||
documentUri: '',
|
||||
documentHash: '',
|
||||
decimals: 8,
|
||||
mintBatonVout: 0,
|
||||
qty: '2100000000000000'
|
||||
}
|
||||
|
||||
const mockOpReturnData03 = {
|
||||
tokenType: 1,
|
||||
txType: 'SEND',
|
||||
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
|
||||
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,
|
||||
genesisTestInputTx,
|
||||
mintTestInputTx,
|
||||
genesisTestOpReturnData01,
|
||||
genesisTestOpReturnData02,
|
||||
mintTestOpReturnData01,
|
||||
mintTestOpReturnData02,
|
||||
mintTestOpReturnData03,
|
||||
sendTestInputTx01,
|
||||
sendTestOpReturnData01,
|
||||
sendTestOpReturnData02,
|
||||
sendTestOpReturnData03,
|
||||
sendTestOpReturnData04,
|
||||
mintTestInputTx02,
|
||||
mintTestOpReturnData04,
|
||||
mintTestOpReturnData05,
|
||||
genesisTestInputTx02,
|
||||
genesisTestOpReturn03
|
||||
}
|
||||
@@ -280,10 +280,214 @@ const electrumxUtxos = {
|
||||
]
|
||||
}
|
||||
|
||||
const fulcrumUtxos01 = {
|
||||
success: true,
|
||||
utxos: [
|
||||
{
|
||||
height: 674512,
|
||||
tx_hash:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674512,
|
||||
tx_hash:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
tx_pos: 2,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674512,
|
||||
tx_hash:
|
||||
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674513,
|
||||
tx_hash:
|
||||
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
||||
tx_pos: 1,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674513,
|
||||
tx_hash:
|
||||
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
||||
tx_pos: 2,
|
||||
value: 546
|
||||
},
|
||||
{
|
||||
height: 674513,
|
||||
tx_hash:
|
||||
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
||||
tx_pos: 3,
|
||||
value: 38134
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const fulcrumUtxos02 = {
|
||||
success: true,
|
||||
utxos: [
|
||||
{
|
||||
height: 674513,
|
||||
tx_hash:
|
||||
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
|
||||
tx_pos: 3,
|
||||
value: 38134
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const psfSlpIndexerUtxos01 = {
|
||||
balance: {
|
||||
utxos: [
|
||||
{
|
||||
txid:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '10000000000',
|
||||
tokenId:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
address: 'bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv'
|
||||
},
|
||||
{
|
||||
txid:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
vout: 2,
|
||||
type: 'baton',
|
||||
tokenId:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
address: 'bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv'
|
||||
}
|
||||
],
|
||||
txs: [
|
||||
{
|
||||
txid:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
height: 674512
|
||||
}
|
||||
],
|
||||
balances: [
|
||||
{
|
||||
tokenId:
|
||||
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
|
||||
qty: '10000000000'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const tokenUtxos01 = [
|
||||
{
|
||||
txid: '384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '10000000',
|
||||
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '4fc789405d58ec612c69eba29aa56cf0c7f228349801114138424eb68df4479d',
|
||||
vout: 1,
|
||||
type: 'token',
|
||||
qty: '100000000',
|
||||
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '42054bba4d69bfe7801ece0cffc754194b04239034fdfe9dbe321ef76c9a2d93',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '4764',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
},
|
||||
{
|
||||
txid: '06938d0a0d15aa76524ffe61fe111d6d2b2ea9dd8dcd4c7c7744614ced370861',
|
||||
vout: 5,
|
||||
type: 'token',
|
||||
qty: '238',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
|
||||
}
|
||||
]
|
||||
|
||||
const genesisData01 = {
|
||||
tokenData: {
|
||||
type: 1,
|
||||
ticker: 'sleven',
|
||||
name: 'sleven',
|
||||
tokenId: 'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
|
||||
documentUri: 'sleven',
|
||||
documentHash: '',
|
||||
decimals: 7,
|
||||
mintBatonIsActive: false,
|
||||
tokensInCirculationBN: '770059999999',
|
||||
tokensInCirculationStr: '770059999999',
|
||||
blockCreated: 555483,
|
||||
totalBurned: '7711234568',
|
||||
totalMinted: '777771234567'
|
||||
}
|
||||
}
|
||||
|
||||
const genesisData02 = {
|
||||
tokenData: {
|
||||
type: 1,
|
||||
ticker: 'NAKAMOTO',
|
||||
name: 'NAKAMOTO',
|
||||
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
|
||||
documentUri: '',
|
||||
documentHash: '',
|
||||
decimals: 8,
|
||||
mintBatonIsActive: false,
|
||||
tokensInCirculationBN: '2099260968799900',
|
||||
tokensInCirculationStr: '2099260968799900',
|
||||
blockCreated: 555671,
|
||||
totalBurned: '739031200100',
|
||||
totalMinted: '2100000000000000'
|
||||
}
|
||||
}
|
||||
|
||||
const genesisData03 = {
|
||||
tokenData: {
|
||||
type: 1,
|
||||
ticker: 'AUDC',
|
||||
name: 'AUD Coin',
|
||||
tokenId: 'f05faf13a29c7f5e54ab921750aafb6afaa953db863bd2cf432e918661d4132f',
|
||||
documentUri: 'audcoino@gmail.com',
|
||||
documentHash: '',
|
||||
decimals: 6,
|
||||
mintBatonIsActive: false,
|
||||
tokensInCirculationBN: '974791786216512742',
|
||||
tokensInCirculationStr: '974791786216512742',
|
||||
blockCreated: 603311,
|
||||
totalBurned: '1025208213783487258',
|
||||
totalMinted: '2000000000000000000'
|
||||
}
|
||||
}
|
||||
|
||||
const noUtxoErr = {
|
||||
success: false,
|
||||
error:
|
||||
'Key not found in database [bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7]'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mockUtxoData,
|
||||
mockHydratedUtxos,
|
||||
mockTwoHydratedAddrs,
|
||||
mockEveryUtxoType,
|
||||
electrumxUtxos
|
||||
electrumxUtxos,
|
||||
fulcrumUtxos01,
|
||||
fulcrumUtxos02,
|
||||
psfSlpIndexerUtxos01,
|
||||
tokenUtxos01,
|
||||
genesisData01,
|
||||
genesisData02,
|
||||
genesisData03,
|
||||
noUtxoErr
|
||||
}
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
/*
|
||||
Unit tests for the IPFS Class.
|
||||
*/
|
||||
|
||||
const assert = require('chai').assert
|
||||
const sinon = require('sinon')
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
let bchjs
|
||||
|
||||
const mockData = require('./fixtures/ipfs-mock')
|
||||
|
||||
describe('#IPFS', () => {
|
||||
let sandbox
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
bchjs = new BCHJS()
|
||||
})
|
||||
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#initUppy', () => {
|
||||
it('should initialize uppy', () => {
|
||||
bchjs.IPFS.initUppy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createFileModelServer', () => {
|
||||
it('should throw an error if file does not exist', async () => {
|
||||
try {
|
||||
const path = '/non-existant-file'
|
||||
|
||||
await bchjs.IPFS.createFileModelServer(path)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Could not find this file')
|
||||
}
|
||||
})
|
||||
|
||||
it('should create a new file model', async () => {
|
||||
const path = `${__dirname.toString()}/ipfs.js`
|
||||
|
||||
sandbox
|
||||
.stub(bchjs.IPFS.axios, 'post')
|
||||
.resolves({ data: mockData.mockNewFileModel })
|
||||
|
||||
const result = await bchjs.IPFS.createFileModelServer(path)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, 'hostingCostBCH')
|
||||
assert.property(result, 'hostingCostUSD')
|
||||
assert.property(result, 'file')
|
||||
|
||||
assert.property(result.file, 'payloadLink')
|
||||
assert.property(result.file, 'hasBeenPaid')
|
||||
assert.property(result.file, '_id')
|
||||
assert.property(result.file, 'schemaVersion')
|
||||
assert.property(result.file, 'size')
|
||||
assert.property(result.file, 'fileName')
|
||||
assert.property(result.file, 'fileExtension')
|
||||
assert.property(result.file, 'createdTimestamp')
|
||||
assert.property(result.file, 'hostingCost')
|
||||
assert.property(result.file, 'walletIndex')
|
||||
assert.property(result.file, 'bchAddr')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#uploadFileServer', () => {
|
||||
it('should throw an error if file does not exist', async () => {
|
||||
try {
|
||||
const path = '/non-existant-file'
|
||||
|
||||
await bchjs.IPFS.uploadFileServer(path)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Could not find this file')
|
||||
}
|
||||
})
|
||||
|
||||
it('should throw an error if modelId is not included', async () => {
|
||||
try {
|
||||
const path = `${__dirname.toString()}/ipfs.js`
|
||||
|
||||
await bchjs.IPFS.uploadFileServer(path)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Must include a file model ID')
|
||||
}
|
||||
})
|
||||
|
||||
it('Should throw error if the file was not uploaded', async () => {
|
||||
try {
|
||||
const mock = {
|
||||
successful: [],
|
||||
failed: [
|
||||
{
|
||||
id: 'file id'
|
||||
}
|
||||
]
|
||||
}
|
||||
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mock)
|
||||
|
||||
const path = `${__dirname.toString()}/ipfs.js`
|
||||
await bchjs.IPFS.uploadFileServer(path, '5ec562319bfacc745e8d8a52')
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'The file could not be uploaded')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return file object if the file is uploaded', async () => {
|
||||
try {
|
||||
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mockData.uploadData)
|
||||
|
||||
const path = `${__dirname.toString()}/ipfs.js`
|
||||
const result = await bchjs.IPFS.uploadFileServer(
|
||||
path,
|
||||
'5ec562319bfacc745e8d8a52'
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'schemaVersion')
|
||||
assert.property(result, 'size')
|
||||
assert.property(result, 'fileId')
|
||||
assert.property(result, 'fileName')
|
||||
assert.property(result, 'fileExtension')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#getStatus', () => {
|
||||
it('should throw an error if modelId is not included', async () => {
|
||||
try {
|
||||
await bchjs.IPFS.getStatus()
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Must include a file model ID')
|
||||
}
|
||||
})
|
||||
|
||||
it('should get data on an unpaid file', async () => {
|
||||
const modelId = '5ec7392c2acfe57aa62e945a'
|
||||
|
||||
sandbox
|
||||
.stub(bchjs.IPFS.axios, 'get')
|
||||
.resolves({ data: mockData.unpaidFileData })
|
||||
|
||||
const result = await bchjs.IPFS.getStatus(modelId)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'hasBeenPaid')
|
||||
assert.property(result, 'satCost')
|
||||
assert.property(result, 'bchAddr')
|
||||
assert.property(result, 'ipfsHash')
|
||||
assert.property(result, 'fileId')
|
||||
assert.property(result, 'fileName')
|
||||
})
|
||||
|
||||
it('should get data on an unpaid file', async () => {
|
||||
const modelId = '5ec7392c2acfe57aa62e945a'
|
||||
|
||||
sandbox
|
||||
.stub(bchjs.IPFS.axios, 'get')
|
||||
.resolves({ data: mockData.paidFileData })
|
||||
|
||||
const result = await bchjs.IPFS.getStatus(modelId)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'hasBeenPaid')
|
||||
assert.property(result, 'satCost')
|
||||
assert.property(result, 'bchAddr')
|
||||
assert.property(result, 'ipfsHash')
|
||||
assert.property(result, 'fileId')
|
||||
assert.property(result, 'fileName')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#createFileModelWeb', () => {
|
||||
it('should throw an error if file is undefined', async () => {
|
||||
try {
|
||||
let file
|
||||
|
||||
await bchjs.IPFS.createFileModelWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'File is required')
|
||||
}
|
||||
})
|
||||
it('should throw an error if file is empty', async () => {
|
||||
try {
|
||||
const file = {}
|
||||
|
||||
await bchjs.IPFS.createFileModelWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'name\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it("should throw an error if 'name' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
size: 5000
|
||||
}
|
||||
|
||||
await bchjs.IPFS.createFileModelWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'name\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("should throw an error if 'size' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
name: 'ipfs.js'
|
||||
}
|
||||
|
||||
await bchjs.IPFS.createFileModelWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'size\' of number type'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should create a new file model', async () => {
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
sandbox
|
||||
.stub(bchjs.IPFS.axios, 'post')
|
||||
.resolves({ data: mockData.mockNewFileModel })
|
||||
|
||||
const result = await bchjs.IPFS.createFileModelWeb(file)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'success')
|
||||
assert.equal(result.success, true)
|
||||
|
||||
assert.property(result, 'hostingCostBCH')
|
||||
assert.property(result, 'hostingCostUSD')
|
||||
assert.property(result, 'file')
|
||||
|
||||
assert.property(result.file, 'payloadLink')
|
||||
assert.property(result.file, 'hasBeenPaid')
|
||||
assert.property(result.file, '_id')
|
||||
assert.property(result.file, 'schemaVersion')
|
||||
assert.property(result.file, 'size')
|
||||
assert.property(result.file, 'fileName')
|
||||
assert.property(result.file, 'fileExtension')
|
||||
assert.property(result.file, 'createdTimestamp')
|
||||
assert.property(result.file, 'hostingCost')
|
||||
assert.property(result.file, 'walletIndex')
|
||||
assert.property(result.file, 'bchAddr')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#uploadFileWeb', () => {
|
||||
it('should throw an error if file is undefined', async () => {
|
||||
try {
|
||||
let file
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'File is required')
|
||||
}
|
||||
})
|
||||
it('should throw an error if file is empty', async () => {
|
||||
try {
|
||||
const file = {}
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'name\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it("should throw an error if 'name' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'name\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it("should throw an error if 'size' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
type: 'text/plain'
|
||||
}
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'size\' of number type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it("should throw an error if 'type' property is not included", async () => {
|
||||
try {
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000
|
||||
}
|
||||
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(
|
||||
err.message,
|
||||
'File should have the property \'type\' of string type'
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should throw an error if modelId is not included', async () => {
|
||||
try {
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
await bchjs.IPFS.uploadFileWeb(file)
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(`err.message: ${err.message}`)
|
||||
assert.include(err.message, 'Must include a file model ID')
|
||||
}
|
||||
})
|
||||
|
||||
it('Should throw error if the file was not uploaded', async () => {
|
||||
try {
|
||||
const mock = {
|
||||
successful: [],
|
||||
failed: [
|
||||
{
|
||||
id: 'file id'
|
||||
}
|
||||
]
|
||||
}
|
||||
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mock)
|
||||
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
await bchjs.IPFS.uploadFileWeb(file, '5ec562319bfacc745e8d8a52')
|
||||
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.include(err.message, 'The file could not be uploaded')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return file object if the file is uploaded', async () => {
|
||||
try {
|
||||
sandbox.stub(bchjs.IPFS.uppy, 'upload').resolves(mockData.uploadData)
|
||||
|
||||
const file = {
|
||||
name: 'ipfs.js',
|
||||
size: 5000,
|
||||
type: 'text/plain'
|
||||
}
|
||||
const result = await bchjs.IPFS.uploadFileWeb(
|
||||
file,
|
||||
'5ec562319bfacc745e8d8a52'
|
||||
)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'schemaVersion')
|
||||
assert.property(result, 'size')
|
||||
assert.property(result, 'fileId')
|
||||
assert.property(result, 'fileName')
|
||||
assert.property(result, 'fileExtension')
|
||||
} catch (err) {
|
||||
// console.log(err)
|
||||
assert.equal(true, false, 'Unexpected result')
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,255 +0,0 @@
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const axios = require('axios')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
const mockData = require('./fixtures/ninsight-mock')
|
||||
|
||||
describe('#Ninsight', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#utxo', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Ninsight.utxo(addr)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
'Input address must be a string or array of strings.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should GET utxos for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.utxo })
|
||||
|
||||
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
|
||||
const result = await bchjs.Ninsight.utxo(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'utxos')
|
||||
assert.property(result, 'legacyAddress')
|
||||
assert.property(result, 'cashAddress')
|
||||
assert.property(result, 'slpAddress')
|
||||
assert.property(result, 'scriptPubKey')
|
||||
assert.property(result, 'asm')
|
||||
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], 'txid')
|
||||
assert.property(result.utxos[0], 'vout')
|
||||
assert.property(result.utxos[0], 'amount')
|
||||
assert.property(result.utxos[0], 'satoshis')
|
||||
assert.property(result.utxos[0], 'height')
|
||||
assert.property(result.utxos[0], 'confirmations')
|
||||
})
|
||||
|
||||
it('should POST utxo details for an array of addresses', async () => {
|
||||
// Mock the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.utxoPost })
|
||||
|
||||
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('#unconfirmed', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Ninsight.unconfirmed(addr)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(
|
||||
err.message,
|
||||
'Input address must be a string or array of strings.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it('should POST utxos for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.unconfirmed })
|
||||
|
||||
const addr = 'bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5'
|
||||
|
||||
const result = await bchjs.Ninsight.unconfirmed(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.property(result, 'utxos')
|
||||
assert.property(result, 'legacyAddress')
|
||||
assert.property(result, 'cashAddress')
|
||||
assert.property(result, 'slpAddress')
|
||||
assert.property(result, 'scriptPubKey')
|
||||
|
||||
assert.isArray(result.utxos)
|
||||
|
||||
assert.property(result.utxos[0], 'txid')
|
||||
assert.property(result.utxos[0], 'vout')
|
||||
assert.property(result.utxos[0], 'amount')
|
||||
assert.property(result.utxos[0], 'satoshis')
|
||||
assert.property(result.utxos[0], 'confirmations')
|
||||
assert.property(result.utxos[0], 'ts')
|
||||
})
|
||||
|
||||
it('should POST utxo details for an array of addresses', async () => {
|
||||
// Mock the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.unconfirmedPost })
|
||||
|
||||
const addr = [
|
||||
'bitcoincash:qpkkjkhe29mqhqmu3evtq3dsnruuzl3rku6usknlh5',
|
||||
'bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr'
|
||||
]
|
||||
|
||||
const result = await bchjs.Ninsight.unconfirmed(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')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#transactions', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.Ninsight.transactions(addr)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
'Input address must be a string or array of strings.'
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should POST transaction history for a single address', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.transactionsPost })
|
||||
|
||||
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 transaction history for an array of addresses', async () => {
|
||||
// Mock the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.transactionsPost })
|
||||
|
||||
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')
|
||||
})
|
||||
})
|
||||
describe('#txDetails', () => {
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.Ninsight.txDetails(txid)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(
|
||||
err.message,
|
||||
'Transaction ID must be a string or array of strings.'
|
||||
)
|
||||
}
|
||||
})
|
||||
it('should POST transaction details for a single TxID', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.detailsPost })
|
||||
|
||||
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], 'isCoinBase')
|
||||
assert.property(result[0], 'valueOut')
|
||||
assert.property(result[0], 'size')
|
||||
})
|
||||
it('should POST transaction details for an array of TxIDs', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.detailsPost })
|
||||
|
||||
const txid = [
|
||||
'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
|
||||
'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], 'vin')
|
||||
assert.property(result[0], 'vout')
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,339 @@
|
||||
const chai = require('chai')
|
||||
const assert = chai.assert
|
||||
const axios = require('axios')
|
||||
const sinon = require('sinon')
|
||||
|
||||
const BCHJS = require('../../src/bch-js')
|
||||
const bchjs = new BCHJS()
|
||||
|
||||
const mockData = require('./fixtures/psf-slp-indexer-mock')
|
||||
|
||||
describe('#PsfSlpIndexer', () => {
|
||||
let sandbox
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#status', () => {
|
||||
it('should GET status', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'get').resolves({ data: mockData.status })
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.status()
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, 'status')
|
||||
assert.property(result.status, 'startBlockHeight')
|
||||
assert.property(result.status, 'syncedBlockHeight')
|
||||
assert.property(result.status, 'chainBlockHeight')
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'get').throws(new Error('test error'))
|
||||
|
||||
await bchjs.PsfSlpIndexer.status()
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle request error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
sandbox.stub(axios, 'get').throws(testErr)
|
||||
|
||||
await bchjs.PsfSlpIndexer.status()
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#balance', () => {
|
||||
it('should GET balance', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.balance })
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
const result = await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, 'balance')
|
||||
|
||||
assert.property(result.balance, 'utxos')
|
||||
assert.property(result.balance, 'txs')
|
||||
assert.property(result.balance, 'balances')
|
||||
assert.isArray(result.balance.utxos)
|
||||
assert.isArray(result.balance.txs)
|
||||
assert.isArray(result.balance.balances)
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const addr = 12345
|
||||
|
||||
await bchjs.PsfSlpIndexer.balance(addr)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, 'Input address must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').throws(new Error('test error'))
|
||||
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
|
||||
await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle request error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
sandbox.stub(axios, 'post').throws(testErr)
|
||||
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
|
||||
await bchjs.PsfSlpIndexer.balance(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tokenStats', () => {
|
||||
it('should GET token stats', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.tokenStats })
|
||||
|
||||
const tokenId =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, 'tokenData')
|
||||
assert.property(result.tokenData, 'type')
|
||||
assert.property(result.tokenData, 'ticker')
|
||||
assert.property(result.tokenData, 'name')
|
||||
assert.property(result.tokenData, 'tokenId')
|
||||
assert.property(result.tokenData, 'documentUri')
|
||||
assert.property(result.tokenData, 'documentHash')
|
||||
assert.property(result.tokenData, 'decimals')
|
||||
assert.property(result.tokenData, 'mintBatonIsActive')
|
||||
assert.property(result.tokenData, 'tokensInCirculationBN')
|
||||
assert.property(result.tokenData, 'tokensInCirculationStr')
|
||||
assert.property(result.tokenData, 'blockCreated')
|
||||
assert.property(result.tokenData, 'totalBurned')
|
||||
assert.property(result.tokenData, 'totalMinted')
|
||||
assert.property(result.tokenData, 'txs')
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const tokenId = 12345
|
||||
|
||||
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, 'Input tokenId must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').throws(new Error('test error'))
|
||||
|
||||
const tokenId =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle request error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
sandbox.stub(axios, 'post').throws(testErr)
|
||||
|
||||
const tokenId =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#tx', () => {
|
||||
it('should GET transaction data', async () => {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').resolves({ data: mockData.txData })
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.property(result, 'txData')
|
||||
assert.property(result.txData, 'txid')
|
||||
assert.property(result.txData, 'hash')
|
||||
assert.property(result.txData, 'version')
|
||||
assert.property(result.txData, 'size')
|
||||
assert.property(result.txData, 'locktime')
|
||||
assert.property(result.txData, 'vin')
|
||||
assert.property(result.txData, 'vout')
|
||||
assert.property(result.txData, 'hex')
|
||||
assert.property(result.txData, 'blockhash')
|
||||
assert.property(result.txData, 'confirmations')
|
||||
assert.property(result.txData, 'time')
|
||||
assert.property(result.txData, 'blocktime')
|
||||
assert.property(result.txData, 'blockheight')
|
||||
assert.property(result.txData, 'isSlpTx')
|
||||
assert.property(result.txData, 'tokenTxType')
|
||||
assert.property(result.txData, 'tokenId')
|
||||
assert.property(result.txData, 'tokenType')
|
||||
assert.property(result.txData, 'tokenTicker')
|
||||
assert.property(result.txData, 'tokenName')
|
||||
assert.property(result.txData, 'tokenDecimals')
|
||||
assert.property(result.txData, 'tokenUri')
|
||||
assert.property(result.txData, 'tokenDocHash')
|
||||
assert.property(result.txData, 'isValidSlp')
|
||||
})
|
||||
|
||||
it('should throw an error for improper input', async () => {
|
||||
try {
|
||||
const txid = 12345
|
||||
|
||||
await bchjs.PsfSlpIndexer.tx(txid)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
// console.log(`err: `, err)
|
||||
assert.include(err.message, 'Input txid must be a string.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle axios error', async () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
sandbox.stub(axios, 'post').throws(new Error('test error'))
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should get tx from full node if not available from slp indexer', async () => {
|
||||
// Stub the call to the SLP indexer.
|
||||
const testErr = {
|
||||
response: { data: { error: 'Key not found in database' } }
|
||||
}
|
||||
sandbox.stub(axios, 'post').rejects(testErr)
|
||||
|
||||
// Stub the call to the full node
|
||||
sandbox.stub(bchjs.PsfSlpIndexer, 'checkBlacklist').resolves(false)
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
|
||||
.resolves({ txid: 'fakeTxid' })
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.txData.txid, 'fakeTxid')
|
||||
assert.equal(result.txData.isValidSlp, false)
|
||||
})
|
||||
|
||||
it('should return isValidSlp=null for blacklisted token', async () => {
|
||||
// Stub the call to the SLP indexer.
|
||||
const testErr = {
|
||||
response: { data: { error: 'Key not found in database' } }
|
||||
}
|
||||
sandbox.stub(axios, 'post').rejects(testErr)
|
||||
|
||||
// Stub the call to the full node
|
||||
sandbox.stub(bchjs.PsfSlpIndexer, 'checkBlacklist').resolves(true)
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.rawTransaction, 'getTxData')
|
||||
.resolves({ txid: 'fakeTxid' })
|
||||
|
||||
const txid =
|
||||
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2'
|
||||
const result = await bchjs.PsfSlpIndexer.tx(txid)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.txData.txid, 'fakeTxid')
|
||||
assert.equal(result.txData.isValidSlp, null)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#checkBlacklist', () => {
|
||||
it('should return true if txid contains token in blacklist', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.resolves(mockData.tokenData01)
|
||||
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist(txid)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result, true)
|
||||
})
|
||||
|
||||
it('should return false if there is an error', async () => {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist()
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
|
||||
it('should return false if there is no tokenId match', async () => {
|
||||
// Mock dependencies
|
||||
mockData.tokenData01.tokenId = 'abc123'
|
||||
sandbox
|
||||
.stub(bchjs.PsfSlpIndexer.slpUtils, 'decodeOpReturn')
|
||||
.resolves(mockData.tokenData01)
|
||||
|
||||
const txid =
|
||||
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
|
||||
|
||||
const result = await bchjs.PsfSlpIndexer.checkBlacklist(txid)
|
||||
|
||||
assert.equal(result, false)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
TODO:
|
||||
-Create a mocking library of data to compare unit and integration tests.
|
||||
Unit tests for raw-transactions.js library.
|
||||
*/
|
||||
|
||||
// Public npm libraries
|
||||
@@ -172,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)}`)
|
||||
@@ -191,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.'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -202,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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+1
-2104
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Unit tests for the transaction.js library.
|
||||
*/
|
||||
|
||||
// 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 mockDataLib = require('./fixtures/transaction-mock.js')
|
||||
|
||||
describe('#TransactionLib', () => {
|
||||
let sandbox
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.createSandbox()
|
||||
|
||||
// mockData = cloneDeep(mockDataLib)
|
||||
})
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#get', () => {
|
||||
it('should proxy psf-slp-indexer', async () => {
|
||||
// console.log('bchjs.Transaction: ', bchjs.Transaction)
|
||||
sandbox.stub(bchjs.Transaction.psfSlpIndexer, 'tx').resolves('test data')
|
||||
|
||||
const result = await bchjs.Transaction.get()
|
||||
|
||||
assert.equal(result, 'test data')
|
||||
})
|
||||
})
|
||||
})
|
||||
+117
-131
@@ -15,137 +15,6 @@ describe('#utxo', () => {
|
||||
beforeEach(() => (sandbox = sinon.createSandbox()))
|
||||
afterEach(() => sandbox.restore())
|
||||
|
||||
describe('#get', () => {
|
||||
it('should get hydrated and filtered UTXOs for an address', async () => {
|
||||
// Mock dependencies.
|
||||
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves(mockData.mockUtxoData)
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
|
||||
.resolves(mockData.mockHydratedUtxos)
|
||||
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const result = await bchjs.Utxo.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 catch and throw an error', async () => {
|
||||
try {
|
||||
// Force an error
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.electrumx, 'utxo')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
await bchjs.Utxo.get(addr)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle an array of addresses', async () => {
|
||||
// Mock dependencies.
|
||||
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves({ utxos: {} })
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
|
||||
.resolves(mockData.mockTwoHydratedAddrs)
|
||||
|
||||
const addr = [
|
||||
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
|
||||
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
|
||||
]
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.equal(result.length, 2)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
|
||||
it('should throw an error for array of 21 elements', async () => {
|
||||
try {
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
|
||||
const addrs = []
|
||||
for (let i = 0; i < 21; i++) {
|
||||
addrs.push(addr)
|
||||
}
|
||||
|
||||
await bchjs.Utxo.get(addrs)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'Too many elements, 20 max.')
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle NFTs and minting batons', async () => {
|
||||
// Mock dependencies.
|
||||
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves({ utxos: {} })
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
|
||||
.resolves(mockData.mockEveryUtxoType)
|
||||
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.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)
|
||||
})
|
||||
|
||||
it('should use the whitelist when flag is set', async () => {
|
||||
// Mock dependencies.
|
||||
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves(mockData.mockUtxoData)
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxosWL')
|
||||
.resolves(mockData.mockHydratedUtxos)
|
||||
|
||||
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
|
||||
const useWhitelist = true
|
||||
|
||||
const result = await bchjs.Utxo.get(addr, useWhitelist)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.isArray(result)
|
||||
assert.property(result[0], 'address')
|
||||
assert.property(result[0], 'bchUtxos')
|
||||
assert.property(result[0], 'nullUtxos')
|
||||
assert.property(result[0], 'slpUtxos')
|
||||
assert.isArray(result[0].bchUtxos)
|
||||
assert.isArray(result[0].nullUtxos)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#findBiggestUtxo', () => {
|
||||
it('should throw error for non-array input', async () => {
|
||||
try {
|
||||
@@ -188,4 +57,121 @@ describe('#utxo', () => {
|
||||
assert.equal(result.satoshis, 800)
|
||||
})
|
||||
})
|
||||
|
||||
describe('#hydrateTokenData', () => {
|
||||
it('should hydrate token UTXOs', async () => {
|
||||
// Mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'tokenStats')
|
||||
.onCall(0)
|
||||
.resolves(mockData.genesisData01)
|
||||
.onCall(1)
|
||||
.resolves(mockData.genesisData02)
|
||||
.onCall(2)
|
||||
.resolves(mockData.genesisData03)
|
||||
|
||||
const result = await bchjs.Utxo.hydrateTokenData(mockData.tokenUtxos01)
|
||||
// console.log('result: ', result)
|
||||
|
||||
assert.equal(result.length, 4)
|
||||
assert.property(result[0], 'qtyStr')
|
||||
assert.property(result[0], 'ticker')
|
||||
assert.property(result[0], 'name')
|
||||
assert.property(result[0], 'documentUri')
|
||||
assert.property(result[0], 'documentHash')
|
||||
})
|
||||
|
||||
it('should should catch and throw errors', async () => {
|
||||
try {
|
||||
// Force error
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'tokenStats')
|
||||
.rejects(new Error('test error'))
|
||||
|
||||
await bchjs.Utxo.hydrateTokenData(mockData.tokenUtxos01)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.equal(err.message, 'test error')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('#get', () => {
|
||||
it('should throw an error if input is not a string', async () => {
|
||||
try {
|
||||
const addr = 123
|
||||
|
||||
await bchjs.Utxo.get(addr)
|
||||
|
||||
assert.fail('Unexpected code path')
|
||||
} catch (err) {
|
||||
assert.include(err.message, 'address input must be a string')
|
||||
}
|
||||
})
|
||||
|
||||
it('should return UTXO information', async () => {
|
||||
// mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.electrumx, 'utxo')
|
||||
.resolves(mockData.fulcrumUtxos01)
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
||||
.resolves(mockData.psfSlpIndexerUtxos01)
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
|
||||
.resolves({ txData: { isValidSlp: false } })
|
||||
|
||||
// Mock function to return the same input. Good enough for this test.
|
||||
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(x => x)
|
||||
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
// Assert expected properties exist
|
||||
assert.property(result, 'address')
|
||||
assert.property(result, 'bchUtxos')
|
||||
assert.property(result, 'slpUtxos')
|
||||
assert.property(result.slpUtxos, 'type1')
|
||||
assert.property(result.slpUtxos.type1, 'tokens')
|
||||
assert.property(result.slpUtxos.type1, 'mintBatons')
|
||||
assert.property(result.slpUtxos, 'nft')
|
||||
assert.property(result, 'nullUtxos')
|
||||
|
||||
assert.equal(result.bchUtxos.length, 4)
|
||||
assert.equal(result.slpUtxos.type1.tokens.length, 1)
|
||||
assert.equal(result.slpUtxos.type1.mintBatons.length, 1)
|
||||
assert.equal(result.nullUtxos.length, 0)
|
||||
})
|
||||
|
||||
it('should handle an address with no SLP UTXOs', async () => {
|
||||
// mock dependencies
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.electrumx, 'utxo')
|
||||
.resolves(mockData.fulcrumUtxos02)
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'tx')
|
||||
.resolves({ txData: { isValidSlp: false } })
|
||||
|
||||
// Force psf-slp-indexer to return no UTXOs
|
||||
sandbox
|
||||
.stub(bchjs.Utxo.psfSlpIndexer, 'balance')
|
||||
.rejects(mockData.noUtxoErr)
|
||||
|
||||
// Mock function to return the same input. Good enough for this test.
|
||||
sandbox.stub(bchjs.Utxo, 'hydrateTokenData').resolves(() => [])
|
||||
|
||||
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
|
||||
|
||||
const result = await bchjs.Utxo.get(addr)
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
|
||||
assert.equal(result.bchUtxos.length, 1)
|
||||
assert.equal(result.slpUtxos.type1.tokens.length, 0)
|
||||
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
|
||||
assert.equal(result.nullUtxos.length, 0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user