Compare commits

..
121 Commits
Author SHA1 Message Date
Chris Troutner 617bb16ddb Merge pull request #59 from Permissionless-Software-Foundation/ct-unstable
fix(price): Removing documentation for deprecated endpoint
2020-11-17 17:12:45 -08:00
Chris Troutner 83f14f5522 fix(price): Removing documentation for deprecated endpoint 2020-11-17 17:09:23 -08:00
Chris Troutner b8e22e1c99 Merge pull request #58 from Permissionless-Software-Foundation/ct-unstable
fix(price): Adding BCHA price
2020-11-17 14:47:47 -08:00
Chris Troutner 72d560e407 fix(price): Adding BCHA price 2020-11-17 14:41:04 -08:00
Chris Troutner 248b2fa2a6 Merge pull request #57 from Permissionless-Software-Foundation/ct-unstable
fix(basic auth): Fixing some bugs around basic auth
2020-11-12 09:52:29 -08:00
Chris Troutner 563cb297fe fix(basic auth): Fixing some bugs around basic auth 2020-11-12 09:48:54 -08:00
Chris Troutner 4c020240e1 fix(rate limits): Updated e2e tests for post-fork servers 2020-11-11 18:39:57 -08:00
Chris Troutner e070409a95 Merge pull request #56 from Permissionless-Software-Foundation/ct-unstable
Basic Authentication
2020-11-11 18:20:09 -08:00
Chris Troutner fbbd855145 feat(Basic Authentication): Adding authPass for auth token to all libraries 2020-11-11 17:47:46 -08:00
Chris Troutner 731987b35b fix(Basic Authentication): Adding back in 2020-11-11 14:56:53 -08:00
Chris Troutner 1e8e7b79f2 Updating comments in e2e rate limit tests 2020-11-11 14:11:13 -08:00
Chris Troutner 099eb25e88 Merge pull request #53 from Permissionless-Software-Foundation/ct-unstable
fix(blockbook): Removing documentation as a step towards deprecation
2020-11-10 08:44:32 -08:00
Chris Troutner 12a7c666c6 fix(blockbook): Removing documentation as a step towards deprecation 2020-11-10 08:39:26 -08:00
Chris Troutner e482e09e77 Merge pull request #52 from Permissionless-Software-Foundation/ninsight-tx-details
Add Ninsight TX Details
2020-11-10 08:27:12 -08:00
Chris Troutner 7ca6498ca0 feat(Ninsight): Adding TX Details endpoint 2020-11-10 08:24:19 -08:00
Chris Troutner f89b52ac86 Merge branch 'master' into ninsight-tx-details 2020-11-10 08:21:07 -08:00
Stoyan Zhekov 7990d0bffd Add Ninsight TX Details 2020-11-10 19:04:16 +09:00
Chris Troutner c0eaaf77c8 fix(integration tests): Updating scripts in package.json 2020-11-09 18:17:44 -08:00
Chris Troutner f424047a69 Merge pull request #51 from Permissionless-Software-Foundation/ninsight-tx-history
Add Ninsight TX History to bch-js
2020-11-09 16:14:39 -08:00
Chris Troutner fc0d8b7f8c fix(ninsight): Adding tx history endpoint 2020-11-09 16:12:10 -08:00
Chris Troutner 4551d1ca99 Merge pull request #50 from Permissionless-Software-Foundation/ninsight-unconfirmed-utxos
Add Ninsight Unconfirmed UTXOs
2020-11-09 15:47:11 -08:00
Chris Troutner b8083c6b88 fix(ninsight): Adding unconfirmed UTXOs endpoint 2020-11-09 15:45:11 -08:00
Stoyan Zhekov 0ef8555837 Add Ninsight TX History to bch-js 2020-11-09 19:49:41 +09:00
Stoyan Zhekov a7564ce611 Add Ninsight Unconfirmed UTXOs 2020-11-09 17:44:09 +09:00
Chris Troutner 601b87cb4a Updating README 2020-11-08 19:14:43 -08:00
Chris Troutner edfcc92b7b Merge pull request #48 from Permissionless-Software-Foundation/ct-unstable
Updating README
2020-11-08 19:04:29 -08:00
Chris Troutner a32e0da9be Updating README 2020-11-08 19:02:30 -08:00
Chris Troutner a1bf617d91 Updating README 2020-11-08 18:56:48 -08:00
Chris Troutner 33d2da503d Updating READMe 2020-11-08 18:49:45 -08:00
Chris Troutner c2ff1e2d68 Updating npm lib 2020-11-08 17:46:40 -08:00
Chris Troutner cc9e8c0319 Fixing links in README 2020-11-08 17:38:11 -08:00
Chris Troutner 83389129d6 Merge pull request #47 from Permissionless-Software-Foundation/ct-unstable
fix(network): Updates for fork
2020-11-08 14:07:01 -08:00
Chris Troutner 360d56e283 fix(network): Updates for fork 2020-11-08 14:05:10 -08:00
Chris Troutner 53a0da4ef1 Merge branch 'master' into ct-unstable 2020-11-08 09:36:41 -08:00
Chris Troutner 6d4534f177 Removing test 2020-11-08 09:36:31 -08:00
Chris Troutner 90ec06adaa Merge pull request #46 from Permissionless-Software-Foundation/ct-unstable
Updating e2e rate limit tests
2020-11-08 09:33:29 -08:00
Chris Troutner a6759cdae5 Testing CI 2020-11-08 09:26:24 -08:00
Chris Troutner 078f157e4e fix(rate limits): Updating e2e rate limit tests for new bch-api rate limits 2020-11-08 09:15:53 -08:00
Chris Troutner 98029f74f8 Merge pull request #45 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): slowing down integration tests for BVT
2020-11-03 09:40:53 -08:00
Chris Troutner 9348ccb7de fix(integration tests): slowing down integration tests for BVT 2020-11-03 08:14:55 -08:00
Chris Troutner 6a6f5fa5fa Merge pull request #44 from Permissionless-Software-Foundation/ct-unstable
Updates to Integration Tests
2020-11-02 15:45:20 -08:00
Chris Troutner 9863fad6a6 fix(testnet): Updating testnet integration tests to pass BVT 2020-11-02 14:49:28 -08:00
Chris Troutner 3a2f38bcf0 fix(integration): Removed all non-integration tests from integration tests 2020-11-02 14:27:31 -08:00
Chris Troutner bc5c7052a1 Merge pull request #43 from Permissionless-Software-Foundation/ct-unstable
Ct unstable
2020-10-31 11:07:41 -07:00
Chris Troutner 89d5171e92 Merge branch 'master' into ct-unstable 2020-10-31 11:04:33 -07:00
Chris Troutner c80a0dcde4 fix(bchn): Starting separate integration tests for bchn 2020-10-30 16:05:26 -07:00
Chris Troutner a2f7ae7fb4 fix(electrumx): Updates for new abc v0.22.4 2020-10-30 15:10:55 -07:00
Chris Troutner 4c2c7a4f6f Merge pull request #42 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Fixing integration test
2020-10-30 07:45:06 -07:00
Chris Troutner 6efb08565a fix(hydrateUtxos): Fixing integration test 2020-10-29 21:46:53 -07:00
Chris Troutner 8a8dcbe593 Merge pull request #41 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): Updating testnet integration tests
2020-10-29 08:08:17 -07:00
Chris Troutner ae58895d71 fix(integration tests): Updating testnet integration tests 2020-10-29 07:18:59 -07:00
Chris Troutner a50ace320b Merge pull request #40 from Permissionless-Software-Foundation/ct-unstable
fix(rawtransactions): Updating integration test for latest version of…
2020-10-27 19:16:41 -07:00
Chris Troutner b1d583cfe4 fix(rawtransactions): Updating integration test for latest version of ABC 2020-10-27 18:03:32 -07:00
Chris Troutner 0b86a19726 Merge pull request #39 from Permissionless-Software-Foundation/ct-unstable
fix(price): Fixing broken integration test
2020-10-20 18:34:13 -07:00
Chris Troutner eb3bc7bcff fix(price): Fixing broken integration test 2020-10-20 18:18:18 -07:00
Chris Troutner c5361e0516 Merge pull request #38 from Permissionless-Software-Foundation/ct-unstable
fix(docs): Update to price documentation
2020-10-19 19:55:57 -07:00
Chris Troutner 54d9233f41 fix(docs): Update to price documentation 2020-10-19 19:46:27 -07:00
Chris Troutner 37a5f0e6fa Merge pull request #37 from Permissionless-Software-Foundation/ct-unstable
feat(price): Added additional endpoints for retrieving market price o…
2020-10-19 19:40:51 -07:00
Chris Troutner 2180b14e1f feat(price): Added additional endpoints for retrieving market price of BCH 2020-10-19 19:36:55 -07:00
Chris Troutner 40e1704572 Merge pull request #36 from Permissionless-Software-Foundation/ct-unstable
Added testnet SLP integration tests
2020-10-16 22:25:59 -07:00
Chris Troutner 9539119c57 fix(testnet): Added additional integration tests for testnet SLP 2020-10-16 22:22:18 -07:00
Chris Troutner b397381c7d fix(hydrateUtxos): Improving documentation 2020-10-13 21:49:22 -07:00
Chris Troutner 6cdb9a0867 fix(documentation): Fixing documentation conflict on slp addrs 2020-10-13 15:58:45 -07:00
Chris Troutner 01f45d7103 Merge pull request #34 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetails): handling dust attack transactions better
2020-10-11 17:59:04 -07:00
Chris Troutner ba38d3e107 fix(tokenUtxoDetails): handling dust attack transactions better 2020-10-11 17:46:27 -07:00
Chris Troutner cbb0d0a8ca Merge pull request #33 from Permissionless-Software-Foundation/ct-unstable
validateTxid2
2020-10-11 16:51:40 -07:00
Chris Troutner 7ec8442540 fix(Electrumx.blockHeader): Mocking negative test-case unit tests 2020-10-11 15:39:17 -07:00
Chris Troutner be3094fc77 feat(validateTxid2): slp-validate based validation of SLP txids 2020-10-11 15:23:50 -07:00
Chris Troutner d35f0e5473 Merge pull request #30 from Permissionless-Software-Foundation/broacast-tx
Add Electrumx Broadcast TX method
2020-10-09 11:35:12 -07:00
Chris Troutner 79088fc68d linting 2020-10-09 11:30:07 -07:00
Chris Troutner fe84729737 Merge branch 'master' into broacast-tx 2020-10-09 11:21:29 -07:00
Chris Troutner 2ec72e73c1 Merge pull request #32 from Permissionless-Software-Foundation/ct-unstable
fix(bchn): Adding bchn free tier to integration tests
2020-10-09 11:21:05 -07:00
Chris Troutner a2c121bf3b fix(bchn): Adding bchn free tier to integration tests 2020-10-09 11:17:40 -07:00
Chris Troutner fd6f58fd45 Merge pull request #31 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Added additional comments
2020-10-07 10:53:01 -07:00
Chris Troutner e8b2f88ff5 fix(decodeOpReturn): Added additional comments 2020-10-07 08:07:06 -07:00
Chris Troutner 4a11db7c45 Merge pull request #29 from kaellis/master
add internal caching for slp.tokenUtxoDetails to reduce duplicate calls
2020-10-07 08:02:08 -07:00
Stoyan Zhekov af118f8a62 Add Electrumx Broadcast TX method 2020-10-07 17:14:30 +09:00
Ken Ellis 1f41b67e3b add internal caching for slp.tokenUtxoDetails to reduce duplicate calls 2020-10-01 09:33:06 -04:00
Chris Troutner 054fb9c14e Merge pull request #28 from Permissionless-Software-Foundation/ct-unstable
Improvements to hydrateUtxos()
2020-09-28 19:33:20 -07:00
Chris Troutner 1fd4095dcb fix(hydateUtxos): Refined integration test 2020-09-28 19:30:06 -07:00
Chris Troutner 0eb1f74ba1 fix(hydrateUtxos): fixing documentation for the function 2020-09-28 19:18:27 -07:00
Chris Troutner 1a13c41c22 Merge pull request #27 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetails): returning isValid:null when 429 error encounte…
2020-09-28 18:00:27 -07:00
Chris Troutner 8b6952b1d4 Updating docs 2020-09-28 17:52:37 -07:00
Chris Troutner 73dd2c010d fix(tokenUtxoDetails): returning isValid:null when 429 error encountered with decodeOpReturn() 2020-09-28 17:39:26 -07:00
Chris Troutner 70743876d2 Merge pull request #25 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Adapted to accept Electrumx.utxo() output
2020-09-27 17:56:36 -07:00
Chris Troutner ab51afb2a8 fix(hydrateUtxos): Adapted to accept Electrumx.utxo() output 2020-09-27 17:52:31 -07:00
Chris Troutner 8a59374831 Merge pull request #24 from Permissionless-Software-Foundation/ct-unstable
feat(SLP.Utils.hydrateUtxos): Added hydrateUtxos to match new bch-api…
2020-09-27 01:51:53 -07:00
Chris Troutner 32b8efa7a2 feat(SLP.Utils.hydrateUtxos): Added hydrateUtxos to match new bch-api endpoint 2020-09-27 01:47:46 -07:00
Chris Troutner b0bdc50193 Merge pull request #22 from Permissionless-Software-Foundation/add-electrumx-raw-tx-data
Add ElectrumX Raw Transaction Details method and tests
2020-09-12 06:58:33 -07:00
Stoyan Zhekov 0a897f19bb code, unit and integration tests and api-doc added 2020-09-11 23:46:01 +09:00
Chris Troutner d8e746acd9 Merge pull request #21 from Permissionless-Software-Foundation/add-electrumx-get-block-header
Add ElectrumX get block headers method and tests
2020-09-11 07:17:47 -07:00
Stoyan Zhekov 47bd0d66bc Add ElectrumX get block headers method and tests 2020-09-01 23:52:50 +09:00
Chris Troutner 6a4a861c07 Merge pull request #20 from Permissionless-Software-Foundation/ct-unstable
fix(ninsight): fixing testnet integration test
2020-08-13 16:45:45 -07:00
Chris Troutner caee2db99e fix(ninsight): fixing testnet integration test 2020-08-13 16:41:49 -07:00
Chris Troutner 392f51b166 Merge pull request #19 from Permissionless-Software-Foundation/ct-unstable
fix(ninsight): Fixing failing ninsight integration test
2020-08-13 16:19:06 -07:00
Chris Troutner 8facfd69ac fix(ninsight): Fixing failing ninsight integration test 2020-08-13 16:07:08 -07:00
Chris Troutner 7f1b87215c Merge branch 'master' into ct-unstable 2020-08-13 15:44:37 -07:00
Chris Troutner 662e53aaa2 debugging ninsight test 2020-08-13 15:44:26 -07:00
Chris Troutner 98fc4b9530 Merge pull request #18 from Permissionless-Software-Foundation/update/cleanUp-docs
Clean up documentation
2020-08-13 15:43:57 -07:00
nicocappabianca de8c8e6284 clean up documentation 2020-08-12 21:34:50 -03:00
Chris Troutner 739bdf1f34 Merge pull request #17 from Permissionless-Software-Foundation/ct-unstable
fix(docs): Adding deploy script to automatically update documentation
2020-08-07 18:17:44 -07:00
Chris Troutner b207a1639e Removing unit test for SLP.Utils.balance() 2020-08-07 18:13:52 -07:00
Chris Troutner a169c0f7d0 fix(docs): Adding deploy script to automatically update documentation 2020-08-07 18:03:55 -07:00
Chris Troutner 65cf49adf0 Merge pull request #16 from Permissionless-Software-Foundation/zh
fix(electrumx): Added unconfirmed endpoint for Electrumx library.
2020-08-05 09:02:29 -07:00
Chris Troutner 65104d60ad Commented out failing integration tests 2020-08-05 08:54:54 -07:00
Chris Troutner fd0a1a6af2 editing api docs for electrumx 2020-08-05 08:47:04 -07:00
Chris Troutner 25299e2f6e Merge pull request #14 from zh/add-electrumx-unconfirmed-utxo
Access to unconfirmed UTXOs API endpoints
2020-08-05 08:42:14 -07:00
Stoyan Zhekov b184c080f1 Access to unconfirmed UTXOs API endpoints 2020-08-04 22:58:22 +09:00
Chris Troutner d2c999f739 Merge pull request #9 from Permissionless-Software-Foundation/ct-unstable
fix(Ninsight): Using POST endpoint only
2020-07-31 14:07:28 -07:00
Chris Troutner 4d2d8d0e5a fix(Ninsight): Using POST endpoint only 2020-07-31 14:04:22 -07:00
Chris Troutner fec9d371af Merge pull request #8 from Permissionless-Software-Foundation/ct-unstable
Ninsight UTXO Bulk call
2020-07-31 13:33:11 -07:00
Chris Troutner b3c74b4a8b fixing integration test 2020-07-31 13:19:21 -07:00
Chris Troutner 1aa99a820a fix(Ninsight utxo): Added call to POST bulk endpoint 2020-07-31 12:58:06 -07:00
Chris Troutner 6652e1e4af ran npm audit fix 2020-07-31 12:35:10 -07:00
Chris Troutner b70e0a16c0 Merge pull request #3 from Permissionless-Software-Foundation/ct-unstable
feat(upload): Expanded IPFS Class for uploading files to the web
2020-07-29 10:19:55 -07:00
Chris Troutner a94392c4a5 Merge branch 'master' of https://github.com/christroutner/bch-js into ct-unstable 2020-07-29 10:16:06 -07:00
Chris Troutner ba02724d54 Merge pull request #54 from christroutner/dh-upload-files
feat(upload): Expanded IPFS Class for uploading files to the web
2020-07-29 10:14:37 -07:00
Daniel Gonzalez b12110a617 feat(upload): Expanded IPFS Class for uploading files to the web 2020-07-29 01:18:33 -04:00
Chris Troutner cfd94cb529 Merge pull request #1 from Permissionless-Software-Foundation/ct-unstable
fix(semantic release): Making sure semantic release works
2020-07-28 18:25:41 -07:00
Chris Troutner 7377a93c06 fix(semantic release): Making sure semantic release works 2020-07-28 18:19:48 -07:00
Chris Troutner ea501bb4ef Updating package.json 2020-07-28 18:17:39 -07:00
80 changed files with 6569 additions and 1504 deletions
+1 -1
View File
@@ -27,4 +27,4 @@ deploy:
provider: script
skip_cleanup: true
script:
- npx semantic-release
- npx semantic-release && ./deploy-production.sh
+42 -52
View File
@@ -1,68 +1,71 @@
# bch-js
[![Build Status](https://travis-ci.org/christroutner/bch-js.svg?branch=master)](https://travis-ci.org/christroutner/bch-js)
[![Version](https://img.shields.io/npm/v/@chris.troutner/bch-js)](https://www.npmjs.com/package/@chris.troutner/bch-js)
[![Downloads/week](https://img.shields.io/npm/dw/@chris.troutner/bch-js)](https://npmjs.org/package/@chris.troutner/bch-js)
[![License](https://img.shields.io/npm/l/@chris.troutner/bch-js)](https://github.com/christroutner/bch-js/blob/master/LICENSE.md)
[![Build Status](https://travis-ci.org/Permissionless-Software-Foundation/bch-js.svg?branch=master)](https://travis-ci.org/Permissionless-Software-Foundation/bch-js)
[![Version](https://img.shields.io/npm/v/@psf/bch-js)](https://www.npmjs.com/package/@psf/bch-js)
[![Downloads/week](https://img.shields.io/npm/dw/@psf/bch-js)](https://npmjs.org/package/@psf/bch-js)
[![License](https://img.shields.io/npm/l/@psf/bch-js)](https://github.com/Permissionless-Software-Foundation/bch-js/blob/master/LICENSE.md)
[bch-js](https://www.npmjs.com/package/@chris.troutner/bch-js) is a JavaScript npm library for creating web and mobile apps for interacting with the Bitcoin Cash (BCH) blockchain. It can be used for free, but requires an account on [FullStack.cash](https://fullstack.cash) for increased rate limits. Find out more from [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer).
[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.
### Quick Start Videos:
Here are two YouTube walk-through videos to help you get started:
- [Introduction to bch-js and the bch-js-examples repository](https://youtu.be/GD2i1ZUiyrk)
- [Working with the FullStack.cash JWT token](https://youtu.be/GD2i1ZUiyrk)
### Quick Links
- [npm Library](https://www.npmjs.com/package/@chris.troutner/bch-js)
- [npm Library](https://www.npmjs.com/package/@psf/bch-js)
- [Documentation](https://bchjs.fullstack.cash/)
- [Examples](https://github.com/Permissionless-Software-Foundation/bch-js-examples)
- [api.fullstack.cash](https://api.fullstack.cash) - The REST API this library talks to by default.
- [bchn.fullstack.cash](https://bchn.fullstack.cash) - The REST API this library talks to by default.
- [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.
- [FullStack.cash](https://fullstack.cash) - cloud-based infrastructure for application
developers.
- [Permissionless Software Foundation](https://psfoundation.cash) - The organization that maintains this library.
### Quick Notes
- Install library: `npm install @chris.troutner/bch-js`
- Install library: `npm install @psf/bch-js`
- Instantiate the library in your code:
```
const BCHJS = require("@chris.troutner/bch-js")
let bchjs = new BCHJS()
const BCHJS = require("@psf/bch-js")
let bchjs = new BCHJS() // Defaults to BCHN network.
// testnet
bchjs = new BCHJS({ restURL: 'https://tapi.fullstack.cash/v3/' })
bchjs = new BCHJS({ restURL: 'https://testnet3.fullstack.cash/v3/' })
```
This is a fork of the [BITBOX SDK](https://github.com/Bitcoin-com/bitbox-sdk) (which is maintained by Bitcoin.com). This library is intended to be paired with
the [bch-api](https://github.com/christroutner/bch-api) REST API.
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:
If you need a backward-compatible instance of this library, you can use a
'shim'. Do it like this:
```
const BCHJS = require("@chris.troutner/bch-js")
const bitbox = BCHJS.BitboxShim({ restURL: 'https://api.fullstack.cash/v3/' })
```
- BCHN Mainnet REST API server: https://bchn.fullstack.cash/v3/
- ABC Mainnet REST API server: https://abc.fullstack.cash/v3/
- Testnet3 REST API server: https://testnet3.fullstack.cash/v3/
- Check server status: https://metrics.fullstack.cash
### API Key
The [bch-api](https://github.com/christroutner/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*.
- Implicitly: bch-js will detect your JWT token by setting the `BCHJSTOKEN` environment variable.
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_.
- 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:
```
const BCHJS = require("@chris.troutner/bch-js")
const BCHJS = require("@psf/bch-js")
let bchjs = new BCHJS({
restURL: 'https://api.fullstack.cash/v3/',
restURL: 'https://bchn.fullstack.cash/v3/',
apiToken: 'eyJhbGciO...' // Your JWT token here.
})
```
### Gatsby
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):
```
exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
@@ -75,60 +78,47 @@ exports.onCreateWebpackConfig = ({ actions }) => {
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.
## Features
This library sets itself apart from BITBOX with the following 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.
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).
- [Semantic Release](https://github.com/semantic-release/semantic-release) for
continuous delivery using semantic versioning.
continuous delivery using semantic versioning.
- [Greenkeeper](https://greenkeeper.io/) automatic dependency management for
automatically maintaining the latest, most secure dependencies.
automatically maintaining the latest, most secure dependencies.
- [IPFS uploads](https://ipfs.io) of all files and dependencies, to backup
dependencies in case they are ever inaccessible from GitHub or npm.
dependencies in case they are ever inaccessible from GitHub or npm.
## Documentation:
Full documentation for this library can be found here:
- [Documentation](https://bchjs.fullstack.cash/)
Original documentation on BITBOX is available at:
- [General docs](https://developer.bitcoin.com)
- [BITBOX Introduction](https://developer.bitcoin.com/bitbox)
- [BITBOX API Reference](https://developer.bitcoin.com/bitbox/docs/getting-started)
bch-js uses [APIDOC](http://apidocjs.com/) so that documentation and working code
live in the same repository. To generate the documentation:
- `npm run docs`
- Open the generated `docs/index.html` file in a web browser.
## Support
Have questions? Need help? Join our community support
[Telegram channel](https://t.me/bch_js_toolkit)
## IPFS Releases
I will periodically publish IPFS releases of this repository, including all
dependencies in the `node_modules` folder. This ensures working copies of this
repository can be retrieved in case there is any drift in dependency files, or
if dependencies are pulled from npm or GitHub.
Copies of this repository are also published on [IPFS](https://ipfs.io).
- Initial fork on 5/9/2019:
- without node_modules folder: QmQFHfbBQdEHfhtiRLbXtX1NcgnfL45hZb7TbQimTXAuzG (4 MB)
- with node_modules folder: QmXq9Ds6Qdkg9xbRhcF8pay9KabA6QN2y7bx3wvSqiXifk (107 MB)
- v1.0.0 - refactored to pure JavaScript:
- without node_modules folder: QmNjFsiTZRMAUa9rZpXqZqivv9JLaNicwLSPHjyLB7PVDk (1 MB)
- with node_modules folder: Qma9ScApwBtuL7dpdSk7jpBFTxbqRdiR921WjyP75SU7bT (100 MB)
- v3.8.10: QmauhggzKDo9A9je5y2K6WRAp32JmGRFCw2ZFTiSnW8yot
## License
[MIT](LICENSE.md)
+14
View File
@@ -0,0 +1,14 @@
# Triggers a webhook to update the staging server at staging.fullstack.cash.
#!/bin/bash
#echo $DEPLOY_SECRET
echo "Deploy to production...."
export DATA="{\"ref\":\"$DEPLOY_SECRET\"}"
#echo $DATA
curl -X POST http://fullstack.cash:9000/hooks/bch-js -H "Content-Type: application/json" -d $DATA
echo "...Finished deploying to production."
+1 -2
View File
@@ -1,4 +1,3 @@
**Note:** This directory is being deprecated. Examples have been moved to this
repository:
Examples have been moved to this repository:
https://github.com/Permissionless-Software-Foundation/bch-js-examples
+213 -220
View File
@@ -1,6 +1,6 @@
{
"name": "@chris.troutner/bch-js",
"version": "3.0.0",
"name": "@psf/bch-js",
"version": "3.4.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1697,13 +1697,13 @@
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs="
},
"compare-func": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.4.tgz",
"integrity": "sha512-sq2sWtrqKPkEXAC8tEJA1+BqAH9GbFkGBtUOqrUX57VSfwp8xyktctk+uLoRy5eccTdxzDcVIztlYDpKs3Jv1Q==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
"integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
"dev": true,
"requires": {
"array-ify": "^1.0.0",
"dot-prop": "^3.0.0"
"dot-prop": "^5.1.0"
}
},
"component-bind": {
@@ -1777,22 +1777,49 @@
"integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo="
},
"conventional-changelog-angular": {
"version": "5.0.10",
"resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.10.tgz",
"integrity": "sha512-k7RPPRs0vp8+BtPsM9uDxRl6KcgqtCJmzRD1wRtgqmhQ96g8ifBGo9O/TZBG23jqlXS/rg8BKRDELxfnQQGiaA==",
"version": "5.0.11",
"resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.11.tgz",
"integrity": "sha512-nSLypht/1yEflhuTogC03i7DX7sOrXGsRn14g131Potqi6cbGbGEE9PSDEHKldabB6N76HiSyw9Ph+kLmC04Qw==",
"dev": true,
"requires": {
"compare-func": "^1.3.1",
"compare-func": "^2.0.0",
"q": "^1.5.1"
},
"dependencies": {
"compare-func": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz",
"integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==",
"dev": true,
"requires": {
"array-ify": "^1.0.0",
"dot-prop": "^5.1.0"
}
},
"dot-prop": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
"integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
"dev": true,
"requires": {
"is-obj": "^2.0.0"
}
},
"is-obj": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
"dev": true
}
}
},
"conventional-changelog-writer": {
"version": "4.0.16",
"resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.16.tgz",
"integrity": "sha512-jmU1sDJDZpm/dkuFxBeRXvyNcJQeKhGtVcFFkwTphUAzyYWcwz2j36Wcv+Mv2hU3tpvLMkysOPXJTLO55AUrYQ==",
"version": "4.0.17",
"resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.17.tgz",
"integrity": "sha512-IKQuK3bib/n032KWaSb8YlBFds+aLmzENtnKtxJy3+HqDq5kohu3g/UdNbIHeJWygfnEbZjnCKFxAW0y7ArZAw==",
"dev": true,
"requires": {
"compare-func": "^1.3.1",
"compare-func": "^2.0.0",
"conventional-commits-filter": "^2.0.6",
"dateformat": "^3.0.0",
"handlebars": "^4.7.6",
@@ -2168,12 +2195,12 @@
}
},
"dot-prop": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz",
"integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=",
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
"integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
"dev": true,
"requires": {
"is-obj": "^1.0.0"
"is-obj": "^2.0.0"
}
},
"dotignore": {
@@ -2255,9 +2282,9 @@
}
},
"elliptic": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
"integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
"requires": {
"bn.js": "^4.4.0",
"brorand": "^1.0.1",
@@ -4041,9 +4068,9 @@
"integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw=="
},
"is-obj": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
"integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
"dev": true
},
"is-plain-obj": {
@@ -5100,9 +5127,9 @@
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"neo-async": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
"integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"dev": true
},
"nerf-dart": {
@@ -5274,9 +5301,9 @@
"dev": true
},
"npm": {
"version": "6.14.6",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.6.tgz",
"integrity": "sha512-axnz6iHFK6WPE0js/+mRp+4IOwpHn5tJEw5KB6FiCU764zmffrhsYHbSHi2kKqNkRBt53XasXjngZfBD3FQzrQ==",
"version": "6.14.7",
"resolved": "https://registry.npmjs.org/npm/-/npm-6.14.7.tgz",
"integrity": "sha512-swhsdpNpyXg4GbM6LpOQ6qaloQuIKizZ+Zh6JPXJQc59ka49100Js0WvZx594iaKSoFgkFq2s8uXFHS3/Xy2WQ==",
"dev": true,
"requires": {
"JSONStream": "^1.3.5",
@@ -5285,7 +5312,7 @@
"ansistyles": "~0.1.3",
"aproba": "^2.0.0",
"archy": "~1.0.0",
"bin-links": "^1.1.7",
"bin-links": "^1.1.8",
"bluebird": "^3.5.5",
"byte-size": "^5.0.1",
"cacache": "^12.0.3",
@@ -5306,7 +5333,7 @@
"find-npm-prefix": "^1.0.2",
"fs-vacuum": "~1.2.10",
"fs-write-stream-atomic": "~1.0.10",
"gentle-fs": "^2.3.0",
"gentle-fs": "^2.3.1",
"glob": "^7.1.6",
"graceful-fs": "^4.2.4",
"has-unicode": "~2.0.1",
@@ -5321,14 +5348,14 @@
"is-cidr": "^3.0.0",
"json-parse-better-errors": "^1.0.2",
"lazy-property": "~1.0.0",
"libcipm": "^4.0.7",
"libcipm": "^4.0.8",
"libnpm": "^3.0.1",
"libnpmaccess": "^3.0.2",
"libnpmhook": "^5.0.3",
"libnpmorg": "^1.0.1",
"libnpmsearch": "^2.0.2",
"libnpmteam": "^1.0.2",
"libnpx": "^10.2.2",
"libnpx": "^10.2.4",
"lock-verify": "^2.1.0",
"lockfile": "^1.0.4",
"lodash._baseindexof": "*",
@@ -5350,10 +5377,10 @@
"node-gyp": "^5.1.0",
"nopt": "^4.0.3",
"normalize-package-data": "^2.5.0",
"npm-audit-report": "^1.3.2",
"npm-audit-report": "^1.3.3",
"npm-cache-filename": "~1.0.2",
"npm-install-checks": "^3.0.2",
"npm-lifecycle": "^3.1.4",
"npm-lifecycle": "^3.1.5",
"npm-package-arg": "^6.1.1",
"npm-packlist": "^1.4.8",
"npm-pick-manifest": "^3.0.2",
@@ -5567,7 +5594,7 @@
}
},
"bin-links": {
"version": "1.1.7",
"version": "1.1.8",
"bundled": true,
"dev": true,
"requires": {
@@ -5722,26 +5749,41 @@
}
},
"cliui": {
"version": "4.1.0",
"version": "5.0.0",
"bundled": true,
"dev": true,
"requires": {
"string-width": "^2.1.1",
"strip-ansi": "^4.0.0",
"wrap-ansi": "^2.0.0"
"string-width": "^3.1.0",
"strip-ansi": "^5.2.0",
"wrap-ansi": "^5.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "3.0.0",
"version": "4.1.0",
"bundled": true,
"dev": true
},
"strip-ansi": {
"version": "4.0.0",
"is-fullwidth-code-point": {
"version": "2.0.0",
"bundled": true,
"dev": true
},
"string-width": {
"version": "3.1.0",
"bundled": true,
"dev": true,
"requires": {
"ansi-regex": "^3.0.0"
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"bundled": true,
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
@@ -6103,6 +6145,11 @@
"bundled": true,
"dev": true
},
"emoji-regex": {
"version": "7.0.3",
"bundled": true,
"dev": true
},
"encoding": {
"version": "0.1.12",
"bundled": true,
@@ -6228,14 +6275,6 @@
"bundled": true,
"dev": true
},
"find-up": {
"version": "2.1.0",
"bundled": true,
"dev": true,
"requires": {
"locate-path": "^2.0.0"
}
},
"flush-write-stream": {
"version": "1.0.3",
"bundled": true,
@@ -6434,7 +6473,7 @@
"dev": true
},
"gentle-fs": {
"version": "2.3.0",
"version": "2.3.1",
"bundled": true,
"dev": true,
"requires": {
@@ -6464,7 +6503,7 @@
}
},
"get-caller-file": {
"version": "1.0.3",
"version": "2.0.5",
"bundled": true,
"dev": true
},
@@ -6688,11 +6727,6 @@
"validate-npm-package-name": "^3.0.0"
}
},
"invert-kv": {
"version": "2.0.0",
"bundled": true,
"dev": true
},
"ip": {
"version": "1.1.5",
"bundled": true,
@@ -6877,16 +6911,8 @@
"bundled": true,
"dev": true
},
"lcid": {
"version": "2.0.0",
"bundled": true,
"dev": true,
"requires": {
"invert-kv": "^2.0.0"
}
},
"libcipm": {
"version": "4.0.7",
"version": "4.0.8",
"bundled": true,
"dev": true,
"requires": {
@@ -6896,7 +6922,7 @@
"find-npm-prefix": "^1.0.2",
"graceful-fs": "^4.1.11",
"ini": "^1.3.5",
"lock-verify": "^2.0.2",
"lock-verify": "^2.1.0",
"mkdirp": "^0.5.1",
"npm-lifecycle": "^3.0.0",
"npm-logical-tree": "^1.2.1",
@@ -7055,7 +7081,7 @@
}
},
"libnpx": {
"version": "10.2.2",
"version": "10.2.4",
"bundled": true,
"dev": true,
"requires": {
@@ -7066,16 +7092,7 @@
"update-notifier": "^2.3.0",
"which": "^1.3.0",
"y18n": "^4.0.0",
"yargs": "^11.0.0"
}
},
"locate-path": {
"version": "2.0.0",
"bundled": true,
"dev": true,
"requires": {
"p-locate": "^2.0.0",
"path-exists": "^3.0.0"
"yargs": "^14.2.3"
}
},
"lock-verify": {
@@ -7206,36 +7223,11 @@
"ssri": "^6.0.0"
}
},
"map-age-cleaner": {
"version": "0.1.3",
"bundled": true,
"dev": true,
"requires": {
"p-defer": "^1.0.0"
}
},
"meant": {
"version": "1.0.1",
"bundled": true,
"dev": true
},
"mem": {
"version": "4.3.0",
"bundled": true,
"dev": true,
"requires": {
"map-age-cleaner": "^0.1.1",
"mimic-fn": "^2.0.0",
"p-is-promise": "^2.0.0"
},
"dependencies": {
"mimic-fn": {
"version": "2.1.0",
"bundled": true,
"dev": true
}
}
},
"mime-db": {
"version": "1.35.0",
"bundled": true,
@@ -7338,11 +7330,6 @@
"bundled": true,
"dev": true
},
"nice-try": {
"version": "1.0.5",
"bundled": true,
"dev": true
},
"node-fetch-npm": {
"version": "2.0.2",
"bundled": true,
@@ -7402,7 +7389,7 @@
}
},
"npm-audit-report": {
"version": "1.3.2",
"version": "1.3.3",
"bundled": true,
"dev": true,
"requires": {
@@ -7432,7 +7419,7 @@
}
},
"npm-lifecycle": {
"version": "3.1.4",
"version": "3.1.5",
"bundled": true,
"dev": true,
"requires": {
@@ -7589,44 +7576,6 @@
"bundled": true,
"dev": true
},
"os-locale": {
"version": "3.1.0",
"bundled": true,
"dev": true,
"requires": {
"execa": "^1.0.0",
"lcid": "^2.0.0",
"mem": "^4.0.0"
},
"dependencies": {
"cross-spawn": {
"version": "6.0.5",
"bundled": true,
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
}
},
"execa": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
}
}
}
},
"os-tmpdir": {
"version": "1.0.2",
"bundled": true,
@@ -7641,42 +7590,11 @@
"os-tmpdir": "^1.0.0"
}
},
"p-defer": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"p-finally": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"p-is-promise": {
"version": "2.1.0",
"bundled": true,
"dev": true
},
"p-limit": {
"version": "1.2.0",
"bundled": true,
"dev": true,
"requires": {
"p-try": "^1.0.0"
}
},
"p-locate": {
"version": "2.0.0",
"bundled": true,
"dev": true,
"requires": {
"p-limit": "^1.1.0"
}
},
"p-try": {
"version": "1.0.0",
"bundled": true,
"dev": true
},
"package-json": {
"version": "4.0.1",
"bundled": true,
@@ -8073,7 +7991,7 @@
"dev": true
},
"require-main-filename": {
"version": "1.0.1",
"version": "2.0.0",
"bundled": true,
"dev": true
},
@@ -8723,22 +8641,41 @@
}
},
"wrap-ansi": {
"version": "2.1.0",
"version": "5.1.0",
"bundled": true,
"dev": true,
"requires": {
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1"
"ansi-styles": "^3.2.0",
"string-width": "^3.0.0",
"strip-ansi": "^5.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"bundled": true,
"dev": true
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"bundled": true,
"dev": true
},
"string-width": {
"version": "1.0.2",
"version": "3.1.0",
"bundled": true,
"dev": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"bundled": true,
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
@@ -8779,37 +8716,105 @@
"dev": true
},
"yargs": {
"version": "11.1.1",
"version": "14.2.3",
"bundled": true,
"dev": true,
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.1.1",
"find-up": "^2.1.0",
"get-caller-file": "^1.0.1",
"os-locale": "^3.1.0",
"cliui": "^5.0.0",
"decamelize": "^1.2.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^1.0.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^3.2.1",
"yargs-parser": "^9.0.2"
"y18n": "^4.0.0",
"yargs-parser": "^15.0.1"
},
"dependencies": {
"y18n": {
"version": "3.2.1",
"ansi-regex": {
"version": "4.1.0",
"bundled": true,
"dev": true
},
"find-up": {
"version": "3.0.0",
"bundled": true,
"dev": true,
"requires": {
"locate-path": "^3.0.0"
}
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"bundled": true,
"dev": true
},
"locate-path": {
"version": "3.0.0",
"bundled": true,
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "2.3.0",
"bundled": true,
"dev": true,
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"bundled": true,
"dev": true,
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"bundled": true,
"dev": true
},
"string-width": {
"version": "3.1.0",
"bundled": true,
"dev": true,
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"bundled": true,
"dev": true,
"requires": {
"ansi-regex": "^4.1.0"
}
}
}
},
"yargs-parser": {
"version": "9.0.2",
"version": "15.0.1",
"bundled": true,
"dev": true,
"requires": {
"camelcase": "^4.1.0"
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"dependencies": {
"camelcase": {
"version": "5.3.1",
"bundled": true,
"dev": true
}
}
}
}
@@ -11481,23 +11486,11 @@
"integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
},
"uglify-js": {
"version": "3.9.4",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.9.4.tgz",
"integrity": "sha512-8RZBJq5smLOa7KslsNsVcSH+KOXf1uDU8yqLeNuVKwmT0T3FA0ZoXlinQfRad7SDcbZZRZE4ov+2v71EnxNyCA==",
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.10.0.tgz",
"integrity": "sha512-Esj5HG5WAyrLIdYU74Z3JdG2PxdIusvj6IWHMtlyESxc7kcDz7zYlYjpnSokn1UbpV0d/QX9fan7gkCNd/9BQA==",
"dev": true,
"optional": true,
"requires": {
"commander": "~2.20.3"
},
"dependencies": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true,
"optional": true
}
}
"optional": true
},
"uniq": {
"version": "1.0.1",
+12 -11
View File
@@ -1,7 +1,7 @@
{
"name": "@chris.troutner/bch-js",
"version": "3.0.0",
"description": "Bitcoin Cash JavaScript Library based on BITBOX",
"name": "@psf/bch-js",
"version": "3.4.0",
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
"author": "Chris Troutner <chris.troutner@gmail.com>",
"contributors": [
"Gabriel Cardona <gabriel@bitcoin.com>"
@@ -9,23 +9,24 @@
"main": "src/bch-js",
"scripts": {
"test": "nyc mocha --timeout 30000 test/unit/",
"test:integration": "mocha --timeout 30000 test/integration/",
"test:integration:testnet": "mocha --timeout 30000 test/integration/testnet/",
"test:integration": "bash test/integration/test-bchn-integration.sh",
"test:integration:bchn": "bash test/integration/test-bchn-integration.sh",
"test:integration:abc": "RESTURL=https://abc.fullstack.cash/v3/ IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/",
"test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/testnet/",
"test:integration:local": "RESTURL=http://localhost:3000/v3/ mocha --timeout 30000 test/integration",
"test:integration:testnet:local": "RESTURL=http://localhost:4000/v3/ mocha --timeout 30000 test/integration/testnet",
"test:integration:free": "bash test/integration/test-free-tier.sh",
"test:integration:free:testnet": "bash test/integration/testnet/test-free-tier.sh",
"test:temp": "nyc mocha --timeout 30000 test/unit/ipfs.js",
"test:integration:local:bchn": "RESTURL=http://localhost:3000/v3/ bash test/integration/test-free-tier-bchn.sh",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v3/ mocha --timeout 30000 test/integration/testnet",
"test:temp": "RESTURL=http://localhost:3000/v3/ mocha --timeout 30000 -g '#price' test/integration/",
"test:integration:api": "RESTURL=https://api.fullstack.cash/v3/ mocha --timeout 30000 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"
},
"license": "MIT",
"homepage": "https://github.com/christroutner/bch-js",
"homepage": "https://github.com/Permissionless-Software-Foundation/bch-js",
"repository": {
"type": "git",
"url": "git+https://github.com/christroutner/bch-js.git"
"url": "git+https://github.com/Permissionless-Software-Foundation/bch-js.git"
},
"dependencies": {
"@uppy/core": "^1.10.4",
+34 -17
View File
@@ -11,10 +11,27 @@ class Address {
this.restURL = tmp.restURL
this.apiToken = tmp.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}`
}
}
}
}
/**
* @api Address.toLegacyAddress() toLegacyAddress() - Convert to Legacy Address
* @api Address.toLegacyAddress() toLegacyAddress()
* @apiName toLegacyAddress
* @apiGroup Address
* @apiDescription Convert cashaddr to legacy address format
@@ -73,7 +90,7 @@ class Address {
}
/**
* @api Address.toCashAddress() toCashAddress() - Convert to bitcoincash: format
* @api Address.toCashAddress() toCashAddress()
* @apiName toCashAddress
* @apiGroup Address
* @apiDescription Convert legacy to cashAddress format
@@ -120,7 +137,7 @@ class Address {
}
/**
* @api Address.hash160ToLegacy() hash160ToLegacy() - Convert hash160 to legacy address.
* @api Address.hash160ToLegacy() hash160ToLegacy()
* @apiName hash160ToLegacy
* @apiGroup Address
* @apiDescription Convert hash160 to legacy address.
@@ -146,7 +163,7 @@ class Address {
}
/**
* @api Address.hash160ToCash() hash160ToCash() - Convert hash160 to cash address.
* @api Address.hash160ToCash() hash160ToCash()
* @apiName hash160ToCash
* @apiGroup Address
* @apiDescription Convert hash160 to cash address.
@@ -255,7 +272,7 @@ class Address {
}
/**
* @api Address.isLegacyAddress() isLegacyAddress() - Detect if legacy address.
* @api Address.isLegacyAddress() isLegacyAddress()
* @apiName isLegacyAddress
* @apiGroup Address
* @apiDescription Detect if legacy base58check encoded address.
@@ -291,7 +308,7 @@ class Address {
}
/**
* @api Address.isCashAddress() isCashAddress() - Detect if cashAddr address.
* @api Address.isCashAddress() isCashAddress()
* @apiName isCashAddress
* @apiGroup Address
* @apiDescription Detect if cashAddr encoded address.
@@ -326,7 +343,7 @@ class Address {
}
/**
* @api Address.isHash160() isHash160() - Detect if an addess is a hash160.
* @api Address.isHash160() isHash160()
* @apiName isHash160
* @apiGroup Address
* @apiDescription Detect if an addess is a hash160.
@@ -345,7 +362,7 @@ class Address {
}
/**
* @api Address.isMainnetAddress() isMainnetAddress() - Detect if mainnet address.
* @api Address.isMainnetAddress() isMainnetAddress()
* @apiName isMainnetAddress
* @apiGroup Address
* @apiDescription Detect if mainnet address .
@@ -384,7 +401,7 @@ class Address {
}
/**
* @api Address.isTestnetAddress() isTestnetAddress() - Detect if testnet address.
* @api Address.isTestnetAddress() isTestnetAddress()
* @apiName isTestnetAddress
* @apiGroup Address
* @apiDescription Detect if testnet address.
@@ -422,7 +439,7 @@ class Address {
}
/**
* @api Address.isRegTestAddress() isRegTestAddress() - Detect if regtest address.
* @api Address.isRegTestAddress() isRegTestAddress()
* @apiName isRegTestAddress
* @apiGroup Address
* @apiDescription Detect if regtest address.
@@ -461,7 +478,7 @@ class Address {
}
/**
* @api Address.isP2PKHAddress() isP2PKHAddress() - Detect if p2pkh address.
* @api Address.isP2PKHAddress() isP2PKHAddress()
* @apiName isP2PKHAddress
* @apiGroup Address
* @apiDescription Detect if p2pkh address.
@@ -498,7 +515,7 @@ class Address {
}
/**
* @api Address.isP2SHAddress() isP2SHAddress() - Detect if p2sh address.
* @api Address.isP2SHAddress() isP2SHAddress()
* @apiName isP2SHAddress
* @apiGroup Address
* @apiDescription Detect if p2sh address.
@@ -534,7 +551,7 @@ class Address {
}
/**
* @api Address.detectAddressFormat() detectAddressFormat() - Detect address format.
* @api Address.detectAddressFormat() detectAddressFormat()
* @apiName detectAddressFormat
* @apiGroup Address
* @apiDescription Detect address format.
@@ -572,7 +589,7 @@ class Address {
}
/**
* @api Address.detectAddressNetwork() detectAddressNetwork() - Detect address network.
* @api Address.detectAddressNetwork() detectAddressNetwork()
* @apiName detectAddressNetwork
* @apiGroup Address
* @apiDescription Detect address network.
@@ -622,7 +639,7 @@ class Address {
}
/**
* @api Address.detectAddressType() detectAddressType() - Detect address type.
* @api Address.detectAddressType() detectAddressType()
* @apiName detectAddressType
* @apiGroup Address
* @apiDescription Detect address type.
@@ -660,7 +677,7 @@ class Address {
}
/**
* @api Address.fromXPub() fromXPub() - Generates an address (xpub).
* @api Address.fromXPub() fromXPub()
* @apiName fromXPub
* @apiGroup Address
* @apiDescription Generates an address for an extended public key (xpub).
@@ -698,7 +715,7 @@ class Address {
}
/**
* @api Address.fromOutputScript() fromOutputScript() - Detect an addess from an OutputScript..
* @api Address.fromOutputScript() fromOutputScript()
* @apiName fromOutputScript
* @apiGroup Address
* @apiDescription Detect an addess from an OutputScript..
+21 -3
View File
@@ -55,9 +55,27 @@ class BCHJS {
else if (process.env.BCHJSTOKEN && process.env.BCHJSTOKEN !== "")
this.apiToken = process.env.BCHJSTOKEN
// Retrieve the Basic Authentication password.
this.authPass = "" // default value.
if (config && config.authPass && config.authPass !== "")
this.authPass = config.authPass
else if (process.env.BCHJSAUTHPASS && process.env.BCHJSAUTHPASS !== "")
this.authPass = process.env.BCHJSAUTHPASS
// Generate a Basic Authentication token from an auth password
this.authToken = ""
if (this.authPass) {
// console.log(`bch-js initialized with authPass: ${this.authPass}`)
// Generate the header for Basic Authentication.
const combined = `fullstackcash:${this.authPass}`
var base64Credential = Buffer.from(combined).toString("base64")
this.authToken = `Basic ${base64Credential}`
}
const libConfig = {
restURL: this.restURL,
apiToken: this.apiToken
apiToken: this.apiToken,
authToken: this.authToken
}
// console.log(`apiToken: ${this.apiToken}`)
@@ -69,7 +87,7 @@ class BCHJS {
this.OpenBazaar = new OpenBazaar(libConfig)
// Bitcoin.com Ninsight indexer
this.Ninsight = new Ninsight()
this.Ninsight = new Ninsight(config)
// ElectrumX indexer
this.Electrumx = new Electrumx(libConfig)
@@ -90,7 +108,7 @@ class BCHJS {
this.Generating = new Generating(libConfig)
this.HDNode = new HDNode(this.Address)
this.Mnemonic = new Mnemonic(this.Address)
this.Price = new Price()
this.Price = new Price(libConfig)
this.Script = new Script()
this.TransactionBuilder = TransactionBuilder
this.TransactionBuilder.setAddress(this.Address)
+12 -12
View File
@@ -15,7 +15,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.toSatoshi() toSatoshi() - Converting Bitcoin Cash units to satoshi units.
* @api BitcoinCash.toSatoshi() toSatoshi()
* @apiName toSatoshi
* @apiGroup BitcoinCash
* @apiDescription
@@ -48,7 +48,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.toBitcoinCash() toBitcoinCash() - Converting satoshi units to Bitcoin Cash units.
* @api BitcoinCash.toBitcoinCash() toBitcoinCash()
* @apiName toBitcoinCash
* @apiGroup BitcoinCash
* @apiDescription
@@ -81,7 +81,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.toBits() toBits() - Converting satoshi units to Bits denomination.
* @api BitcoinCash.toBits() toBits()
* @apiName toBits
* @apiGroup BitcoinCash
* @apiDescription
@@ -133,7 +133,7 @@ class BitcoinCash {
// }
/**
* @api BitcoinCash.signMessageWithPrivKey() signMessageWithPrivKey() - Sign message with private key.
* @api BitcoinCash.signMessageWithPrivKey() signMessageWithPrivKey()
* @apiName signMessageWithPrivKey
* @apiGroup BitcoinCash
* @apiDescription
@@ -165,7 +165,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.verifyMessage() verifyMessage() - Verify message.
* @api BitcoinCash.verifyMessage() verifyMessage()
* @apiName verifyMessage
* @apiGroup BitcoinCash
* @apiDescription
@@ -189,7 +189,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.encodeBase58Check() encodeBase58Check() - Encodes hex string as base58Check.
* @api BitcoinCash.encodeBase58Check() encodeBase58Check()
* @apiName encodeBase58Check
* @apiGroup BitcoinCash
* @apiDescription
@@ -227,7 +227,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.decodeBase58Check() decodeBase58Check() - Decodes base58Check encoded string to hex.
* @api BitcoinCash.decodeBase58Check() decodeBase58Check()
* @apiName decodeBase58Check
* @apiGroup BitcoinCash
* @apiDescription
@@ -265,7 +265,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.encodeBIP21() encodeBIP21() - Encodes address and options as BIP21 uri.
* @api BitcoinCash.encodeBIP21() encodeBIP21()
* @apiName encodeBIP21
* @apiGroup BitcoinCash
* @apiDescription
@@ -306,7 +306,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.decodeBIP21() decodeBIP21() - Decodes BIP21 uri.
* @api BitcoinCash.decodeBIP21() decodeBIP21()
* @apiName decodeBIP21
* @apiGroup BitcoinCash
* @apiDescription
@@ -340,7 +340,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.getByteCount() getByteCount() - Get byte count of transaction.
* @api BitcoinCash.getByteCount() getByteCount()
* @apiName getByteCount
* @apiGroup BitcoinCash
* @apiDescription
@@ -458,7 +458,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.encryptBIP38() encryptBIP38() - BIP38 encrypt privkey WIFs.
* @api BitcoinCash.encryptBIP38() encryptBIP38()
* @apiName encryptBIP38
* @apiGroup BitcoinCash
* @apiDescription
@@ -486,7 +486,7 @@ class BitcoinCash {
}
/**
* @api BitcoinCash.decryptBIP38() decryptBIP38() - BIP38 encrypt privkey WIFs.
* @api BitcoinCash.decryptBIP38() decryptBIP38()
* @apiName decryptBIP38
* @apiGroup BitcoinCash
* @apiDescription
+18 -243
View File
@@ -1,6 +1,10 @@
/*
This library interacts with the REST API endpoints in bch-api that communicate
with the Blockbook API.
CT 11/10/20
These endpoints will be soon be deprecated. Blockbook is being fazed out in
favor of Fulcrum.
*/
const axios = require("axios")
@@ -11,184 +15,28 @@ class Blockbook {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// console.log(`Blockbook apiToken: ${this.apiToken}`)
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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 Blockbook.balance() balance() - Balance about an address.
* @apiName Blockbook Balance
* @apiGroup Blockbook
* @apiDescription Return Balance about an address.
*
* @apiExample Example usage:
* (async () => {
* try {
* let balance = await bchjs.Blockbook.balance('bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c');
* console.log(balance)
* } catch(error) {
* console.error(error)
* }
* })()
*
* //{
* // "page": 1,
* // "totalPages": 1,
* // "itemsOnPage": 1000,
* // "address": "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c",
* // "balance": "6000000",
* // "totalReceived": "10185868",
* // "totalSent": "4185868",
* // "unconfirmedBalance": "0",
* // "unconfirmedTxs": 0,
* // "txs": 41,
* // "txids": [
* // "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
* // "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
* // "2f902dec880568511cefa87b9dd761563edeba9c8ba784dc9fca2f7c8c4e6f97",
* // "eea57285462dd70dadcd431fc814857b3f81fe4d0a059a8c02c12fd7d33c02d1",
* // "282b3b296b6aed7122586ed69f7a57d35584eaf94a4d1b1ad7d1b05d36cb79d1",
* // "ac444896b3e32d17824fa6573eed3b89768c5c9085b7a71f3ba88e9d5ba67355",
* // "a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1",
* // "5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e",
* // "54edaa42ff3d6559884a84ebb9bf5ef255635902f5f23b4854245d6b093d41d4",
* // "2b0825188e909410a20a6fbdc58ff5ccf368844273f93f551222c91e6d0fa888",
* // "7a12ea2c83d0c8a5d0f643974b0f04bc19be185c9011ed8fc33255a61d3198bb",
* // "bd6aeeb0748251bb5dba7252ac766fb208c9909d7663c099568225d3bc998a7f",
* // "7898bb1a8c5d933f9c2b24270522e8705b897c2f8d5b1c3477a6b952f2fe22ae",
* // "8d35668a6838de8faa8dff5d8dedeb114bd8ffe6ae73d926264bc8328b2c46b5",
* // "741b4692a14088438ab142cf0865fcbf3977aa7dbd2aafa9bf5f45e75e5e199a",
* // "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e",
* // "376de807f63253584c3edb80f92832ea90e8fdd7d8e68b2602e230d1b015e311",
* // "eb5d0902e4a303f38223ecebb06bbf14da8a07d1ebb2d77a89dfb9e00e286c10",
* // "11205c557af139f382c43f1f09f223cf28c64d939951fd569e5a30eee6178ccd",
* // "ae05a78086b8d64db26d3047de3d6959a68f2a1ed3017ddf927e2c3b20a80b31",
* // "7e0fc3cac7504d45b0f2ce68e807e592f0edf17914ab618a4cb4d93403d11c98",
* // "1cafaacdfb85b0c33f496bf2c03c4f2ede508479bbad56ae99164dfd823a823f",
* // "788add6a5cd961bdf7ca6145a3112462e0e51523d84a4a48047407a795433947",
* // "49f09b616824a05eb14ae50c9fbc5d6a0c414d66a7d0b217aa75340c00ed8b85",
* // "00f075805adf2a34563ecca33071d660a4c1e91d7d7045282c54554ee4844739",
* // "8d048fd00cf375ba9bbea43730da1ce86fc3cb4026e1dd0e751c07fa50a652c6",
* // "ac0e82ea84f93444602a99199dd80793f79a8ece5ac86156d2fff34f0bad44b2",
* // "342f6815845797e0748f4716c923a0dc9b87de649b69925b36422f6d2dc23b7f",
* // "0839a16b2411a1220c64c4d32a4c9b1e77ac5f47558921cf7c0a2adf25e0eb41",
* // "62f42995711cb0308dac4203c5deaa2985f120559131e66aebc5164426fb6cee",
* // "2ada093fe13c9c04da9582bb304923ce08312eaec92560b9128cdb9e93c5f52f",
* // "e0aadd861a06993e39af932bb0b9ad69e7b37ef5843a13c6724789e1c94f3513",
* // "925490ce60334aca204d82a61b634d89fe4cc9de429c7b6cb5ec420df3e3be5e",
* // "60cb69f29c150378abe21d157858713f82c4e2122867597a2573474763a9e94e",
* // "369a589173969c1c882cfb2d82b1e0ec90076ec827ff9d0f32ffc115690c93c3",
* // "43324ef3f5fdd55b645ba14de8c0667be9b223d60b3d1dcd76cf8fdeb0fd32e4",
* // "d2985c9b1c5c18fc2f6a963b7cb850606c4a18fcb85619057211ce3c8bcec696",
* // "dded59fe377517e52918deae8912a096658ebf5ae61992d39953c8bc3932a11b",
* // "2dc053f55a666a3d2a08b1c680b704d62a55506d14ad884add87edcc56b9277d",
* // "544c15ce35c0f2e808d28f29d6587f1ec9276233e29856b7f2938cf0daef0026",
* // "81039b1d7b855b133f359f9dc65f776bd105650153a941675fedc504228ddbd3"
* // ]
* //}
*
*(async () => {
* try {
* let balance = await bchjs.Blockbook.balance(['bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c','1BFHGm4HzqgXXyNX8n7DsQno5DAC4iLMRA']);
* console.log(balance)
* } catch(error) {
* console.error(error)
* }
* })()
*
* //[
* // {
* // "page": 1,
* // "totalPages": 1,
* // "itemsOnPage": 1000,
* // "address": "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c",
* // "balance": "6000000",
* // "totalReceived": "10185868",
* // "totalSent": "4185868",
* // "unconfirmedBalance": "0",
* // "unconfirmedTxs": 0,
* // "txs": 41,
* // "txids": [
* // "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
* // "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
* // "2f902dec880568511cefa87b9dd761563edeba9c8ba784dc9fca2f7c8c4e6f97",
* // "eea57285462dd70dadcd431fc814857b3f81fe4d0a059a8c02c12fd7d33c02d1",
* // "282b3b296b6aed7122586ed69f7a57d35584eaf94a4d1b1ad7d1b05d36cb79d1",
* // "ac444896b3e32d17824fa6573eed3b89768c5c9085b7a71f3ba88e9d5ba67355",
* // "a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1",
* // "5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e",
* // "54edaa42ff3d6559884a84ebb9bf5ef255635902f5f23b4854245d6b093d41d4",
* // "2b0825188e909410a20a6fbdc58ff5ccf368844273f93f551222c91e6d0fa888",
* // "7a12ea2c83d0c8a5d0f643974b0f04bc19be185c9011ed8fc33255a61d3198bb",
* // "bd6aeeb0748251bb5dba7252ac766fb208c9909d7663c099568225d3bc998a7f",
* // "7898bb1a8c5d933f9c2b24270522e8705b897c2f8d5b1c3477a6b952f2fe22ae",
* // "8d35668a6838de8faa8dff5d8dedeb114bd8ffe6ae73d926264bc8328b2c46b5",
* // "741b4692a14088438ab142cf0865fcbf3977aa7dbd2aafa9bf5f45e75e5e199a",
* // "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e",
* // "376de807f63253584c3edb80f92832ea90e8fdd7d8e68b2602e230d1b015e311",
* // "eb5d0902e4a303f38223ecebb06bbf14da8a07d1ebb2d77a89dfb9e00e286c10",
* // "11205c557af139f382c43f1f09f223cf28c64d939951fd569e5a30eee6178ccd",
* // "ae05a78086b8d64db26d3047de3d6959a68f2a1ed3017ddf927e2c3b20a80b31",
* // "7e0fc3cac7504d45b0f2ce68e807e592f0edf17914ab618a4cb4d93403d11c98",
* // "1cafaacdfb85b0c33f496bf2c03c4f2ede508479bbad56ae99164dfd823a823f",
* // "788add6a5cd961bdf7ca6145a3112462e0e51523d84a4a48047407a795433947",
* // "49f09b616824a05eb14ae50c9fbc5d6a0c414d66a7d0b217aa75340c00ed8b85",
* // "00f075805adf2a34563ecca33071d660a4c1e91d7d7045282c54554ee4844739",
* // "8d048fd00cf375ba9bbea43730da1ce86fc3cb4026e1dd0e751c07fa50a652c6",
* // "ac0e82ea84f93444602a99199dd80793f79a8ece5ac86156d2fff34f0bad44b2",
* // "342f6815845797e0748f4716c923a0dc9b87de649b69925b36422f6d2dc23b7f",
* // "0839a16b2411a1220c64c4d32a4c9b1e77ac5f47558921cf7c0a2adf25e0eb41",
* // "62f42995711cb0308dac4203c5deaa2985f120559131e66aebc5164426fb6cee",
* // "2ada093fe13c9c04da9582bb304923ce08312eaec92560b9128cdb9e93c5f52f",
* // "e0aadd861a06993e39af932bb0b9ad69e7b37ef5843a13c6724789e1c94f3513",
* // "925490ce60334aca204d82a61b634d89fe4cc9de429c7b6cb5ec420df3e3be5e",
* // "60cb69f29c150378abe21d157858713f82c4e2122867597a2573474763a9e94e",
* // "369a589173969c1c882cfb2d82b1e0ec90076ec827ff9d0f32ffc115690c93c3",
* // "43324ef3f5fdd55b645ba14de8c0667be9b223d60b3d1dcd76cf8fdeb0fd32e4",
* // "d2985c9b1c5c18fc2f6a963b7cb850606c4a18fcb85619057211ce3c8bcec696",
* // "dded59fe377517e52918deae8912a096658ebf5ae61992d39953c8bc3932a11b",
* // "2dc053f55a666a3d2a08b1c680b704d62a55506d14ad884add87edcc56b9277d",
* // "544c15ce35c0f2e808d28f29d6587f1ec9276233e29856b7f2938cf0daef0026",
* // "81039b1d7b855b133f359f9dc65f776bd105650153a941675fedc504228ddbd3"
* // ]
* // },
* // {
* // "page": 1,
* // "totalPages": 1,
* // "itemsOnPage": 1000,
* // "address": "bitcoincash:qpcxf2sv9hjw08nvpgffpamfus9nmksm3chv5zqtnz",
* // "balance": "0",
* // "totalReceived": "36781097",
* // "totalSent": "36781097",
* // "unconfirmedBalance": "0",
* // "unconfirmedTxs": 0,
* // "txs": 11,
* // "txids": [
* // "11205c557af139f382c43f1f09f223cf28c64d939951fd569e5a30eee6178ccd",
* // "ae05a78086b8d64db26d3047de3d6959a68f2a1ed3017ddf927e2c3b20a80b31",
* // "7e0fc3cac7504d45b0f2ce68e807e592f0edf17914ab618a4cb4d93403d11c98",
* // "60cb69f29c150378abe21d157858713f82c4e2122867597a2573474763a9e94e",
* // "f737485aaee3c10b13013fa109bb6294b099246134ca9885f4cc332dbc6c9bb4",
* // "decd5b9c0c959e4e543182093e8f7f8bc7a6ecd96a8a062daaeff3667f8feca7",
* // "94e69a627a34ae27fca81d15fff4323a7ce1f7c275c7485762ce018221017632",
* // "e67c70787af7f3506263c9eda007f3d2d24bd750ff95b5c50a120d9118dfd807",
* // "8e5e00704a147d54028f94d52df7730e821b9c6cd4bd29494e5636f49c199d6a",
* // "15102827c108566ea5daf725c09079c1a3f42ef99d1eb68ea8c584f7b16ab87a",
* // "cc27be8846276612dfce5924b7be96556212f0f0e62bd17641732175edb9911e"
* // ]
* // }
* //
*
*/
async balance(address) {
try {
// Handle single address.
@@ -220,62 +68,6 @@ class Blockbook {
}
}
/**
* @api Blockbook.utxo() utxo() - Get list of uxto for address.
* @apiName Blockbook Utxo
* @apiGroup Blockbook
* @apiDescription Return list of uxto for address.
*
* @apiExample Example usage:
* (async () => {
* try {
* let utxo = await bchjs.Blockbook.utxo('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf');
* console.log(utxo);
* } catch(error) {
* console.error(error)
* }
* })()
*
* [{
* "txid": "7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41",
* "vout": 0,
* "value": "1000",
* "height": 604392,
* "confirmations": 25530,
* "satoshis": 1000
* }]
*
*
* (async () => {
* try {
* let utxo = await bchjs.Blockbook.utxo([
* "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
* "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"
* ]);
* console.log(utxo);
* } catch(error) {
* console.error(error)
* }
* })()
*
* [
* [{
* "txid": "7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41",
* "vout": 0,
* "value": "1000",
* "height": 604392,
* "confirmations": 25530,
* "satoshis": 1000
* }],
* [{
* "txid": "d5228d2cdc77fbe5a9aa79f19b0933b6802f9f0067f42847fc4fe343664723e5",
* "vout": 0,
* "value": "6000",
* "confirmations": 0,
* "satoshis": 6000
* }]
* ]
*/
async utxo(address) {
try {
// Handle single address.
@@ -306,23 +98,6 @@ class Blockbook {
}
}
/**
* @api Blockbook.tx() tx() - Get details on a transaction.
* @apiName Blockbook Tx
* @apiGroup Blockbook
* @apiDescription Get details on a transaction.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txDetails = await bchjs.Blockbook.tx(`5fe9b74056319a8c87f45cc745030715a6180758b94938dbf90d639d55652392`);
* console.log(txDetails);
* } catch(error) {
* console.error(error)
* }
* })()
*
*/
async tx(txid) {
try {
// Handle single txid.
+28 -18
View File
@@ -11,11 +11,21 @@ class Blockchain {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
@@ -23,7 +33,7 @@ class Blockchain {
}
/**
* @api Blockchain.getBestBlockHash() getBestBlockHash() - Returns the hash of the best (tip) block in the longest blockchain.
* @api Blockchain.getBestBlockHash() getBestBlockHash()
* @apiName getBestBlockHash
* @apiGroup Blockchain
* @apiDescription
@@ -54,7 +64,7 @@ class Blockchain {
}
/**
* @api Blockchain.getBlock() getBlock() - Return information about block hash.
* @api Blockchain.getBlock() getBlock()
* @apiName getBlock
* @apiGroup Blockchain
* @apiDescription
@@ -102,7 +112,7 @@ class Blockchain {
}
/**
* @api Blockchain.getBlockchainInfo() getBlockchainInfo() - Returns an object containing various state info regarding blockchain processing.
* @api Blockchain.getBlockchainInfo() getBlockchainInfo()
* @apiName getBlockchainInfo
* @apiGroup Blockchain
* @apiDescription
@@ -152,7 +162,7 @@ class Blockchain {
}
/**
* @api Blockchain.getBlockCount() getBlockCount() - Returns the number of blocks in the longest blockchain.
* @api Blockchain.getBlockCount() getBlockCount()
* @apiName getBlockCount
* @apiGroup Blockchain
* @apiDescription
@@ -183,7 +193,7 @@ class Blockchain {
}
/**
* @api Blockchain.getBlockHash() getBlockHash() - Returns hash of block in best-block-chain at height provided.
* @api Blockchain.getBlockHash() getBlockHash()
* @apiName getBlockHash
* @apiGroup Blockchain
* @apiDescription
@@ -216,7 +226,7 @@ class Blockchain {
}
/**
* @api Blockchain.getBlockHeader() getBlockHeader() - Return information about blockheader hash.
* @api Blockchain.getBlockHeader() getBlockHeader()
* @apiName getBlockHeader
* @apiGroup Blockchain
* @apiDescription
@@ -281,7 +291,7 @@ class Blockchain {
}
/**
* @api Blockchain.getChainTips() getChainTips() - Return information about all known tips in the block tree.
* @api Blockchain.getChainTips() getChainTips()
* @apiName getChainTips
* @apiGroup Blockchain
* @apiDescription
@@ -324,7 +334,7 @@ class Blockchain {
}
/**
* @api Blockchain.getDifficulty() getDifficulty() - Returns the proof-of-work difficulty as a multiple of the minimum difficulty.
* @api Blockchain.getDifficulty() getDifficulty()
* @apiName getDifficulty
* @apiGroup Blockchain
* @apiDescription
@@ -387,7 +397,7 @@ class Blockchain {
}
/**
* @api Blockchain.getMempoolEntry() getMempoolEntry() - Returns mempool data for given transaction.
* @api Blockchain.getMempoolEntry() getMempoolEntry()
* @apiName getMempoolEntry
* @apiGroup Blockchain
* @apiDescription
@@ -499,7 +509,7 @@ class Blockchain {
}
/**
* @api Blockchain.getMempoolInfo() getMempoolInfo() - Returns details on the active state of the TX memory pool.
* @api Blockchain.getMempoolInfo() getMempoolInfo()
* @apiName getMempoolInfo
* @apiGroup Blockchain
* @apiDescription
@@ -535,7 +545,7 @@ class Blockchain {
}
/**
* @api Blockchain.getRawMempool() getRawMempool() - Returns all transaction ids in memory pool as a json array of string transaction ids.
* @api Blockchain.getRawMempool() getRawMempool()
* @apiName getRawMempool
* @apiGroup Blockchain
* @apiDescription
@@ -582,7 +592,7 @@ class Blockchain {
}
/**
* @api Blockchain.getTxOut() getTxOut() - Returns details about an unspent transaction output.
* @api Blockchain.getTxOut() getTxOut()
* @apiName getTxOut
* @apiGroup Blockchain
* @apiDescription
@@ -634,7 +644,7 @@ class Blockchain {
}
/**
* @api Blockchain.getTxOutProof() getTxOutProof() - Returns a hex-encoded proof that "txid" was included in a block.
* @api Blockchain.getTxOutProof() getTxOutProof()
* @apiName getTxOutProof
* @apiGroup Blockchain
* @apiDescription
@@ -740,7 +750,7 @@ class Blockchain {
}
/**
* @api Blockchain.verifyTxOutProof() verifyTxOutProof() - Verifies that a proof points to a transaction in a block.
* @api Blockchain.verifyTxOutProof() verifyTxOutProof()
* @apiName verifyTxOutProof
* @apiGroup Blockchain
* @apiDescription
+15 -5
View File
@@ -10,11 +10,21 @@ class Control {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
@@ -22,7 +32,7 @@ class Control {
}
/**
* @api Control.getNetworkInfo() getNetworkInfo() - Get Network info
* @api Control.getNetworkInfo() getNetworkInfo()
* @apiName getNetworkInfo
* @apiGroup Control
* @apiDescription Returns an object containing various network info.
+5 -5
View File
@@ -3,7 +3,7 @@ const Bitcoin = require("bitcoincashjs-lib")
class Crypto {
/**
* @api Crypto.sha256() sha256() - Utility for creating sha256 hash.
* @api Crypto.sha256() sha256()
* @apiName sha256
* @apiGroup Crypto
* @apiDescription Utility for creating sha256 hash digests of data
@@ -31,7 +31,7 @@ class Crypto {
}
/**
* @api Crypto.ripemd160() ripemd160()-Utility for creating ripemd160 hash.
* @api Crypto.ripemd160() ripemd160()
* @apiName ripemd160
* @apiGroup Crypto
* @apiDescription Utility for creating ripemd160 hash digests of data
@@ -57,7 +57,7 @@ class Crypto {
}
/**
* @api Crypto.hash256() hash256() - Utility for creating double sha256 hash.
* @api Crypto.hash256() hash256()
* @apiName hash256
* @apiGroup Crypto
* @apiDescription Utility for creating double sha256 hash digests of data.
@@ -83,7 +83,7 @@ class Crypto {
}
/**
* @api Crypto.hash160() hash160() - Utility for creating ripemd160(sha256()) hash.
* @api Crypto.hash160() hash160()
* @apiName hash160
* @apiGroup Crypto
* @apiDescription Utility for creating ripemd160(sha256()) hash digests of data.
@@ -109,7 +109,7 @@ class Crypto {
}
/**
* @api Crypto.randomBytes() randomBytes() - Generates cryptographically strong pseudo-random data.
* @api Crypto.randomBytes() randomBytes()
* @apiName randomBytes
* @apiGroup Crypto
* @apiDescription Generates cryptographically strong pseudo-random data. The size argument is a number indicating the number of bytes to generate.
+6 -6
View File
@@ -6,7 +6,7 @@ class ECPair {
ECPair._address = address
}
/**
* @api Ecpair.fromWIF() fromWIF() - Generates an ECPair from a private key in wallet import format (WIF).
* @api Ecpair.fromWIF() fromWIF()
* @apiName fromWIF
* @apiGroup ECPair
* @apiDescription Generates an ECPair from a private key in wallet import format (WIF). Follow these steps to go from a private key to a WIF. This method only works with a compressed private key.
@@ -35,7 +35,7 @@ class ECPair {
return Bitcoin.ECPair.fromWIF(privateKeyWIF, bitcoincashBitcoinJSLib)
}
/**
* @api Ecpair.toWIF() toWIF() - Gets a private key in wallet import format from an ECPair.
* @api Ecpair.toWIF() toWIF()
* @apiName toWIF
* @apiGroup ECPair
* @apiDescription Gets a private key in wallet import format from an ECPair.
@@ -69,7 +69,7 @@ class ECPair {
return ecpair.verify(buffer, signature)
}
/**
* @api Ecpair.fromPublicKey() fromPublicKey() - Generates an ECPair from a public key buffer.
* @api Ecpair.fromPublicKey() fromPublicKey()
* @apiName fromPublicKey
* @apiGroup ECPair
* @apiDescription Generates an ECPair from a public key buffer.
@@ -87,7 +87,7 @@ class ECPair {
return Bitcoin.ECPair.fromPublicKeyBuffer(pubkeyBuffer)
}
/**
* @api Ecpair.toPublicKey() toPublicKey() - Get the public key of an ECPair as a buffer.
* @api Ecpair.toPublicKey() toPublicKey()
* @apiName toPublicKey
* @apiGroup ECPair
* @apiDescription Get the public key of an ECPair as a buffer.
@@ -109,7 +109,7 @@ class ECPair {
return ecpair.getPublicKeyBuffer()
}
/**
* @api Ecpair.toLegacyAddress() toLegacyAddress() - Get legacy address of ECPair.
* @api Ecpair.toLegacyAddress() toLegacyAddress()
* @apiName toLegacyAddress
* @apiGroup ECPair
* @apiDescription Get legacy address of ECPair.
@@ -135,7 +135,7 @@ class ECPair {
return ecpair.getAddress()
}
/**
* @api Ecpair.toCashAddress() toCashAddress() - Get cash address of ECPair.
* @api Ecpair.toCashAddress() toCashAddress()
* @apiName toCashAddress
* @apiGroup ECPair
* @apiDescription Get cash address of ECPair.
+307 -8
View File
@@ -11,12 +11,21 @@ class ElectrumX {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`,
timeout: 15000
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}`
}
}
}
@@ -24,7 +33,7 @@ class ElectrumX {
}
/**
* @api Electrumx.utxo() utxo() - Get a list of uxto for an address.
* @api Electrumx.utxo() utxo()
* @apiName ElectrumX Utxo
* @apiGroup ElectrumX
* @apiDescription Return a list of uxtos for an address.
@@ -119,7 +128,7 @@ class ElectrumX {
}
/**
* @api Electrumx.balance() balance() - Get the balance for an address.
* @api Electrumx.balance() balance()
* @apiName ElectrumX Balance
* @apiGroup ElectrumX
* @apiDescription Return a list of balances for an address.
@@ -203,7 +212,7 @@ class ElectrumX {
}
/**
* @api Electrumx.transactions() transactions() - Get the transaction history for an address.
* @api Electrumx.transactions() transactions()
* @apiName ElectrumX Transactions
* @apiGroup ElectrumX
* @apiDescription Return a transaction history for an address.
@@ -299,6 +308,296 @@ class ElectrumX {
else throw error
}
}
/**
* @api Electrumx.unconfirmed() unconfirmed()
* @apiName ElectrumX Unconfirmed
* @apiGroup ElectrumX
* @apiDescription Return a list of unconfirmed uxtos (mempool) for an address.
*
* @apiExample Example usage:
* (async () => {
* try {
* let mempool = await bchjs.Electrumx.unconfirmed('bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9');
* console.log(mempool);
* } catch(error) {
* console.error(error)
* }
* })()
*
* mempool = {
* "success": true,
* "utxos": [
* {
* "height": 602405,
* "tx_hash": "2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7",
* "fee": 24310
* }
* ]
* }
*
* (async () => {
* try {
* let mempool = await bchjs.Electrumx.unconfirmed(['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v']);
* console.log(mempool);
* } catch(error) {
* console.error(error)
* }
* })()
*
* mempool = {
* "success": true,
* "utxos": [
* {
* "utxos": [
* {
* "height": 604392,
* "tx_hash": "7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41",
* "fee": 24310
* },
* {
* "height": 630834,
* "tx_hash": "4fe60a51e0d8f5134bfd8e5f872d6e502d7f01b28a6afebb27f4438a4f638d53",
* "fee": 3000
* }
* ],
* "address": "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
* },
* {
* "utxos": [],
* "address": "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
* }
* ]
* }
*
*/
async unconfirmed(address) {
try {
// Handle single address.
if (typeof address === "string") {
const response = await axios.get(
`${this.restURL}electrumx/unconfirmed/${address}`,
_this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}electrumx/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 Electrumx.blockHeader() blockHeader()
* @apiName ElectrumX Block headers
* @apiGroup ElectrumX
* @apiDescription Return block headers for a given height
*
* @apiExample Example usage:
* (async () => {
* try {
* let headers = await bchjs.Electrumx.blockHeaders(42);
* console.log(headers);
* } catch(error) {
* console.error(error)
* }
* })()
*
* headers = {
* "success": true,
* "headers": [
* "010000008b52bbd72c2f49569059f559c1b1794de5192e4f7d6d2b03c7482bad0000000083e4f8a9d502ed0c419075c1abb5d56f878a2e9079e5612bfb76a2dc37d9c42741dd6849ffff001d2b909dd6",
* "01000000f528fac1bcb685d0cd6c792320af0300a5ce15d687c7149548904e31000000004e8985a786d864f21e9cbb7cbdf4bc9265fe681b7a0893ac55a8e919ce035c2f85de6849ffff001d385ccb7c"
* ]
* }
*
* (async () => {
* try {
* let headers = await bchjs.Electrumx.blockHeaders(42, 1);
* console.log(headers);
* } catch(error) {
* console.error(error)
* }
* })()
*
* headers = {
* "success": true,
* "headers": [
* "010000008b52bbd72c2f49569059f559c1b1794de5192e4f7d6d2b03c7482bad0000000083e4f8a9d502ed0c419075c1abb5d56f878a2e9079e5612bfb76a2dc37d9c42741dd6849ffff001d2b909dd6"
* ]
* }
*
*/
async blockHeader(height, count = 1) {
try {
const response = await axios.get(
`${this.restURL}electrumx/block/headers/${height}?count=${count}`,
_this.axiosOptions
)
return response.data
} catch (error) {
// console.log("error: ", error)
if (error.response && error.response.data) {
if (error.response && error.response.data)
throw new Error(error.response.data.error)
else throw error.response.data
} else {
throw error
}
}
}
/**
* @api Electrumx.txData() txData()
* @apiName ElectrumX txData
* @apiGroup ElectrumX
* @apiDescription Returns an object with transaction details of the TXID
*
* @apiExample Example usage:
* (async () => {
* try {
* let result = await bchjs.Electrumx.txData('4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251')
* console.log(result);
* } catch(error) {
* console.error(error)
* }
* })()
*
* result = {
* "success": true,
* "details": {
* "blockhash": "0000000000000000002aaf94953da3b487317508ebd1003a1d75d6d6ec2e75cc",
* "blocktime": 1578327094,
* "confirmations": 31861,
* "hash": "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
* ...
* "vin": [
* {
* "scriptSig": {
* ...
* "vout": [
* {
* "n": 0,
* "scriptPubKey": {
* "addresses": [
* "bitcoincash: pqvfecpwxvj53ayqfwkxtjaxsgpvnklcyg8xewk9hl"
* ],
* }
* ...
* }
*
* (async () => {
* try {
* let result = await bchjs.Electrumx.txData(['4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251', '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'])
* console.log(result);
* } catch(error) {
* console.error(error)
* }
* })()
*
* result = {
* "transactions": [
* {
* "txid": "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
* "details": {
* "blockhash": "0000000000000000002aaf94953da3b487317508ebd1003a1d75d6d6ec2e75cc",
* "blocktime": 1578327094,
* "confirmations": 31861,
* "hash": "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
* ...
* }
* },
* {
* "txid": "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
* "details": {
* "blockhash": "0000000000000000002aaf94953da3b487317508ebd1003a1d75d6d6ec2e75cc",
* "blocktime": 1578327094,
* ...
* }
* ]
* }
*/
async txData(txid) {
try {
// Handle single transaction.
if (typeof txid === "string") {
const response = await axios.get(
`${this.restURL}electrumx/tx/data/${txid}`,
_this.axiosOptions
)
return response.data
} else if (Array.isArray(txid)) {
const response = await axios.post(
`${this.restURL}electrumx/tx/data`,
{
txids: txid
},
_this.axiosOptions
)
return response.data
}
throw new Error(`Input txId must be a string or array of strings.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api Electrumx.broadcast() broadcast()
* @apiName ElectrumX Broadcast
* @apiGroup ElectrumX
* @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure.
*
* (async () => {
* try {
* const txHex = "020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
* let result = await bchjs.Electrumx.broadcast(txHex)
* console.log(result);
* } catch(error) {
* console.error(error)
* }
* })()
*
* result = {
* "success": true,
* "txid": "..."
* }
*/
async broadcast(txHex) {
try {
if (typeof txHex === "string") {
const response = await axios.post(
`${this.restURL}electrumx/tx/broadcast`,
{ txHex },
_this.axiosOptions
)
return response.data
}
throw new Error(`Input txHex must be a string.`)
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = ElectrumX
+14 -5
View File
@@ -11,12 +11,21 @@ class Encryption {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.axios = axios
// console.log(`Blockbook apiToken: ${this.apiToken}`)
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+14 -4
View File
@@ -6,11 +6,21 @@ class Generating {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+21 -21
View File
@@ -9,7 +9,7 @@ class HDNode {
}
/**
* @api HDNode.fromSeed() fromSeed() - Create HDNode from Seed Buffer.
* @api HDNode.fromSeed() fromSeed()
* @apiName fromSeed
* @apiGroup HDNode
* @apiDescription
@@ -46,7 +46,7 @@ class HDNode {
}
/**
* @api HDNode.toLegacyAddress() toLegacyAddress() - Get legacy address of HDNode.
* @api HDNode.toLegacyAddress() toLegacyAddress()
* @apiName toLegacyAddress
* @apiGroup HDNode
* @apiDescription
@@ -80,7 +80,7 @@ class HDNode {
}
/**
* @api HDNode.toCashAddress() toCashAddress() - Get cash address of HDNode
* @api HDNode.toCashAddress() toCashAddress()
* @apiName toCashAddress
* @apiGroup HDNode
* @apiDescription
@@ -114,9 +114,9 @@ class HDNode {
}
/**
* @api SLP.HDNode.toSLPAddress() toSLPAddress() - Get slp address of HDNode.
* @api SLP.HDNode.toSLPAddress() toSLPAddress()
* @apiName toSLPAddress
* @apiGroup SLP
* @apiGroup HDNode
* @apiDescription Get slp address of HDNode.
*
* @apiExample Example usage:
@@ -148,7 +148,7 @@ class HDNode {
}
/**
* @api HDNode.toWIF() toWIF() - Get private key in wallet import format (WIF) of HDNode.
* @api HDNode.toWIF() toWIF()
* @apiName toWIF
* @apiGroup HDNode
* @apiDescription
@@ -182,7 +182,7 @@ class HDNode {
}
/**
* @api HDNode.toXPub() toXPub() - Get extended public key of HDNode.
* @api HDNode.toXPub() toXPub()
* @apiName toXPub
* @apiGroup HDNode
* @apiDescription
@@ -216,7 +216,7 @@ class HDNode {
}
/**
* @api HDNode.toXPriv() toXPriv() - Get extended private key of HDNode.
* @api HDNode.toXPriv() toXPriv()
* @apiName toXPriv
* @apiGroup HDNode
* @apiDescription
@@ -250,7 +250,7 @@ class HDNode {
}
/**
* @api HDNode.toKeyPair() toKeyPair() - Get the ECPair of an HDNode.
* @api HDNode.toKeyPair() toKeyPair()
* @apiName toKeyPair
* @apiGroup HDNode
* @apiDescription
@@ -282,7 +282,7 @@ class HDNode {
}
/**
* @api HDNode.toPublicKey() toPublicKey() - Get the public key of an HDNode as a buffer.
* @api HDNode.toPublicKey() toPublicKey()
* @apiName toPublicKey
* @apiGroup HDNode
* @apiDescription
@@ -316,7 +316,7 @@ class HDNode {
}
/**
* @api HDNode.fromXPriv() fromXPriv() - Generate HDNode from extended private key.
* @api HDNode.fromXPriv() fromXPriv()
* @apiName fromXPriv
* @apiGroup HDNode
* @apiDescription
@@ -339,7 +339,7 @@ class HDNode {
}
/**
* @api HDNode.fromXPub() fromXPub() - Generate HDNode from extended public key.
* @api HDNode.fromXPub() fromXPub()
* @apiName fromXPub
* @apiGroup HDNode
* @apiDescription
@@ -362,7 +362,7 @@ class HDNode {
}
/**
* @api HDNode.derivePath() derivePath() - Derive child HDNode from path.
* @api HDNode.derivePath() derivePath()
* @apiName derivePath
* @apiGroup HDNode
* @apiDescription
@@ -383,7 +383,7 @@ class HDNode {
}
/**
* @api HDNode.derive() derive() - Derive non hardened child HDNode.
* @api HDNode.derive() derive()
* @apiName derive
* @apiGroup HDNode
* @apiDescription
@@ -404,7 +404,7 @@ class HDNode {
}
/**
* @api HDNode.deriveHardened() deriveHardened() - Derive hardened child HDNode.
* @api HDNode.deriveHardened() deriveHardened()
* @apiName deriveHardened
* @apiGroup HDNode
* @apiDescription
@@ -425,7 +425,7 @@ class HDNode {
}
/**
* @api HDNode.sign() sign() - Sign 32 byte hash encoded as a buffer.
* @api HDNode.sign() sign()
* @apiName sign
* @apiGroup HDNode
* @apiDescription
@@ -455,7 +455,7 @@ class HDNode {
}
/**
* @api HDNode.verify() verify() - Verify signed 32 byte hash encoded as a buffer.
* @api HDNode.verify() verify()
* @apiName verify
* @apiGroup HDNode
* @apiDescription
@@ -499,7 +499,7 @@ class HDNode {
}
/**
* @api HDNode.isPublic() isPublic() - Check if an HDNode can only derive public keys and children.
* @api HDNode.isPublic() isPublic()
* @apiName isPublic
* @apiGroup HDNode
* @apiDescription
@@ -535,7 +535,7 @@ class HDNode {
}
/**
* @api HDNode.isPrivate() isPrivate() - Check if an HDNode can derive both public and private keys and children.
* @api HDNode.isPrivate() isPrivate()
* @apiName isPrivate
* @apiGroup HDNode
* @apiDescription
@@ -571,7 +571,7 @@ class HDNode {
}
/**
* @api HDNode.toIdentifier() toIdentifier() - hash160 of Nodes public key.
* @api HDNode.toIdentifier() toIdentifier()
* @apiName toIdentifier
* @apiGroup HDNode
* @apiDescription
@@ -607,7 +607,7 @@ class HDNode {
}
/**
* @api HDNode.createAccount() createAccount() - Create BIP32 account.
* @api HDNode.createAccount() createAccount()
* @apiName createAccount
* @apiGroup HDNode
* @apiDescription
+177 -8
View File
@@ -53,8 +53,8 @@ class IPFS {
}
/**
* @api IPFS.createFileModel() createFileModel() - Create a file model
* @apiName createFileModel()
* @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,
@@ -64,7 +64,7 @@ class IPFS {
* (async () => {
* try {
* const path = `${__dirname}/ipfs.js`
* let fileData = await bchjs.IPFS.uploadFile(path);
* let fileData = await bchjs.IPFS.createFileModelServer(path);
* console.log(fileData);
* } catch(error) {
* console.error(error)
@@ -90,7 +90,7 @@ class IPFS {
* "__v": 0
* }
*/
async createFileModel(path) {
async createFileModelServer(path) {
try {
// Ensure the file exists.
if (!_this.fs.existsSync(path))
@@ -131,7 +131,7 @@ class IPFS {
}
/**
* @api IPFS.uploadFile() uploadFile() - Upload a file to IPFS
* @api IPFS.uploadFileServer() uploadFileServer()
* @apiName uploadFile()
* @apiGroup IPFS
* @apiDescription Upload a file to the FullStack.cash IPFS server. If
@@ -142,7 +142,7 @@ class IPFS {
* (async () => {
* try {
* const path = `${__dirname}/ipfs.js`
* let fileData = await bchjs.IPFS.uploadFile(path, "5ec562319bfacc745e8d8a52");
* let fileData = await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52");
* console.log(fileData);
* } catch(error) {
* console.error(error)
@@ -157,7 +157,7 @@ class IPFS {
* "fileExtension": "js"
* }
*/
async uploadFile(path, modelId) {
async uploadFileServer(path, modelId) {
try {
// Ensure the file exists.
if (!_this.fs.existsSync(path))
@@ -219,7 +219,7 @@ class IPFS {
}
/**
* @api IPFS.getStatus() getStatus() - Check status of file upload
* @api IPFS.getStatus() getStatus()
* @apiName getStatus()
* @apiGroup IPFS
* @apiDescription Gets the status of the uploaded file. Will return an object
@@ -270,6 +270,175 @@ class IPFS {
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
+14 -4
View File
@@ -6,11 +6,21 @@ class Mining {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
+8 -8
View File
@@ -10,7 +10,7 @@ class Mnemonic {
}
/**
* @api Mnemonic.generate() generate() - Generate BIP39 mnemonic from entropy.
* @api Mnemonic.generate() generate()
* @apiName generate
* @apiGroup Mnemonic
* @apiDescription
@@ -50,7 +50,7 @@ class Mnemonic {
}
/**
* @api Mnemonic.fromEntropy() fromEntropy() - Create mnemonic from entropy.
* @api Mnemonic.fromEntropy() fromEntropy()
* @apiName fromEntropy
* @apiGroup Mnemonic
* @apiDescription
@@ -104,7 +104,7 @@ class Mnemonic {
}
/**
* @api Mnemonic.toEntropy() toEntropy() - Turn mnemonic to entropy.
* @api Mnemonic.toEntropy() toEntropy()
* @apiName toEntropy
* @apiGroup Mnemonic
* @apiDescription
@@ -141,7 +141,7 @@ class Mnemonic {
}
/**
* @api Mnemonic.validate() validate() - Validate mnemonic.
* @api Mnemonic.validate() validate()
* @apiName validate
* @apiGroup Mnemonic
* @apiDescription
@@ -181,7 +181,7 @@ class Mnemonic {
}
/**
* @api Mnemonic.toSeed() toSeed() - Create root seed from mnemonic
* @api Mnemonic.toSeed() toSeed()
* @apiName toSeed
* @apiGroup Mnemonic
* @apiDescription
@@ -208,7 +208,7 @@ class Mnemonic {
}
/**
* @api Mnemonic.wordLists() wordLists() - Return mnemonic word lists.
* @api Mnemonic.wordLists() wordLists()
* @apiName wordLists
* @apiGroup Mnemonic
* @apiDescription
@@ -234,7 +234,7 @@ class Mnemonic {
}
/**
* @api Mnemonic.toKeypairs() toKeypairs() - Returns an array of privateKeyWIF/publicAddress pairs.
* @api Mnemonic.toKeypairs() toKeypairs()
* @apiName toKeypairs
* @apiGroup Mnemonic
* @apiDescription
@@ -290,7 +290,7 @@ class Mnemonic {
}
/**
* @api Mnemonic.findNearestWord() findNearestWord() - Returns nearest matching word from provided word list.
* @api Mnemonic.findNearestWord() findNearestWord()
* @apiName findNearestWord
* @apiGroup Mnemonic
* @apiDescription
+249 -11
View File
@@ -13,7 +13,11 @@ class Ninsight {
// this.apiToken = config.apiToken
// this.ninsightURL = `https://bch-explorer.api.bitcoin.com/v1/`
this.ninsightURL = `https://rest.bitcoin.com/v2`
if (config) {
this.ninsightURL = config.ninsightURL
? config.ninsightURL
: `https://rest.bitcoin.com/v2`
}
// Add JWT token to the authorization header.
this.axiosOptions = {
@@ -27,7 +31,7 @@ class Ninsight {
}
/**
* @api Ninsight.utxo() utxo() - Get list of uxto for address.
* @api Ninsight.utxo() utxo()
* @apiName Ninsight Utxo
* @apiGroup Ninsight
* @apiDescription Return list of uxto for address.
@@ -46,14 +50,16 @@ class Ninsight {
* // {
* // "txid": "d31dc2cf66fe4d3d3ae18e1065def58a64920746b1702b52f060e5edeea9883b",
* // "vout": 1,
* // "value": "1000000",
* // "amount": 0.03608203,
* // "satoshis": 3608203,
* // "height": 585570,
* // "confirmations": 10392
* // },
* // {
* // "txid": "41e9a118765ecf7a1ba4487c0863e23dba343cc5880381a72f0365ac2546c5fa",
* // "vout": 0,
* // "value": "1000000",
* // "amount": 0.03608203,
* // "satoshis": 3608203,
* // "height": 577125,
* // "confirmations": 18837
* // }
@@ -62,15 +68,247 @@ class Ninsight {
*/
async utxo(address) {
try {
if (typeof address !== "string")
throw new Error(`address needs to be a string.`)
// Handle single address.
if (typeof address === "string") {
const response = await axios.post(
`${this.ninsightURL}/address/utxo`,
{
addresses: [address]
},
_this.axiosOptions
)
return response.data
const response = await axios.get(
`${this.ninsightURL}/address/utxo/${address}`,
_this.axiosOptions
)
// 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
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
+3 -3
View File
@@ -23,7 +23,7 @@ class OpenBazaar {
}
/**
* @api OpenBazaar.balance() balance() - Balance about an address.
* @api OpenBazaar.balance() balance()
* @apiName OpenBazaar Balance
* @apiGroup OpenBazaar
* @apiDescription Return Balance about an address.
@@ -83,7 +83,7 @@ class OpenBazaar {
}
/**
* @api OpenBazaar.utxo() utxo() - Get list of uxto for address.
* @api OpenBazaar.utxo() utxo()
* @apiName OpenBazaar Utxo
* @apiGroup OpenBazaar
* @apiDescription Return list of uxto for address.
@@ -135,7 +135,7 @@ class OpenBazaar {
}
/**
* @api OpenBazaar.tx() tx() - Get details on a transaction.
* @api OpenBazaar.tx() tx()
* @apiName OpenBazaar Tx
* @apiGroup OpenBazaar
* @apiDescription Get details on a transaction.
+149 -22
View File
@@ -1,32 +1,159 @@
const axios = require("axios")
/**
* @api price.current() current() - Get current price.
* @apiName Price.
* @apiGroup Price
* @apiDescription Return current price of BCH in multiple currencies.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.current('usd');
* console.log(current);
* } catch(error) {
* console.error(error)
* }
*})()
*
* // 26681
*/
let _this
class Price {
constructor(config) {
_this = this
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}`
}
}
}
this.axios = axios
}
// This endpoint is deprecated. Documentation removed.
async current(currency = "usd") {
try {
const response = await axios.get(
const response = await this.axios.get(
`https://index-api.bitcoin.com/api/v0/cash/price/${currency.toLowerCase()}`
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.price
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.getUsd() getUsd()
* @apiName Price getUsd()
* @apiGroup Price
* @apiDescription Return current price of BCH in USD.
* This endpoint gets the USD price of BCH from the Coinbase 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.getUsd();
* console.log(current);
* } catch(err) {
* console.err(err)
* }
*})()
*
* // 266.81
*/
async getUsd() {
try {
const response = await this.axios.get(
`${this.restURL}price/usd`,
_this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.usd
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.rates() rates()
* @apiName Price rates()
* @apiGroup Price
* @apiDescription Return current price of BCH in several different currencies.
* This endpoint gets the price of BCH from the Coinbase API in many different
* currencies. 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.rates();
* console.log(current);
* } catch(err) {
* console.err(err)
* }
*})()
*
* {
* AED: "915.049218",
* AFN: "19144.48874646",
* ALGO: "826.6633482661356600405",
* ...
* ZRX: "644.844402797695193656",
* ZWL: "80215.03"
* }
*/
async rates() {
try {
const response = await this.axios.get(
`${this.restURL}price/rates`,
_this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.getBchaUsd() getBchaUsd()
* @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.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getBchaUsd();
* console.log(current);
* } catch(err) {
* console.err(err)
* }
*})()
*
* // 18.81
*/
async getBchaUsd() {
try {
const response = await this.axios.get(
`${this.restURL}price/bchausd`,
_this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.usd
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
}
+20 -14
View File
@@ -6,11 +6,21 @@ class RawTransactions {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
@@ -18,7 +28,7 @@ class RawTransactions {
}
/**
* @api RawTransactions.decodeRawTransaction() decodeRawTransaction() - Return an Array of JSON objects representing the serialized, hex-encoded transactions.
* @api RawTransactions.decodeRawTransaction() decodeRawTransaction()
* @apiName decodeRawTransaction
* @apiGroup RawTransactions
* @apiDescription
@@ -115,7 +125,7 @@ class RawTransactions {
}
/**
* @api RawTransactions.decodeScript() decodeScript() - Decode hex-encoded scripts.
* @api RawTransactions.decodeScript() decodeScript()
* @apiName decodeScript
* @apiGroup RawTransactions
* @apiDescription
@@ -182,7 +192,7 @@ class RawTransactions {
}
/**
* @api RawTransactions.getRawTransaction() getRawTransaction() - Return the raw transaction data.
* @api RawTransactions.getRawTransaction() getRawTransaction()
* @apiName getRawTransaction
* @apiGroup RawTransactions
* @apiDescription
@@ -262,9 +272,7 @@ class RawTransactions {
txids: txid,
verbose: verbose
},
headers: {
authorization: `Token ${this.apiToken}`
}
headers: _this.axiosOptions.headers
}
const response = await axios(options)
@@ -279,7 +287,7 @@ class RawTransactions {
}
/**
* @api RawTransactions.sendRawTransaction() sendRawTransaction() - Submits raw transaction (serialized, hex-encoded) to local node and network.
* @api RawTransactions.sendRawTransaction() sendRawTransaction()
* @apiName sendRawTransaction
* @apiGroup RawTransactions
* @apiDescription
@@ -337,9 +345,7 @@ class RawTransactions {
data: {
hexes: hex
},
headers: {
authorization: `Token ${this.apiToken}`
}
headers: _this.axiosOptions.headers
}
const response = await axios(options)
+25 -15
View File
@@ -4,17 +4,27 @@ class Schnorr {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
}
/**
* @api Schnorr.sign() sign() - Sign a 32-byte message with the private key, returning a 64-byte signature.
* @api Schnorr.sign() sign()
* @apiName sign
* @apiGroup Schnorr
* @apiDescription
@@ -41,7 +51,7 @@ class Schnorr {
}
/**
* @api Schnorr.verify() verify() - Verify a 64-byte signature of a 32-byte message against the public key.
* @api Schnorr.verify() verify()
* @apiName verify
* @apiGroup Schnorr
* @apiDescription
@@ -73,7 +83,7 @@ class Schnorr {
}
/**
* @api Schnorr.batchVerify() batchVerify() - Verify a list of 64-byte signatures as a batch operation.
* @api Schnorr.batchVerify() batchVerify()
* @apiName batchVerify
* @apiGroup Schnorr
* @apiDescription
@@ -135,7 +145,7 @@ class Schnorr {
}
/**
* @api Schnorr.nonInteractive() nonInteractive() - Aggregates multiple signatures of different private keys
* @api Schnorr.nonInteractive() nonInteractive()
* @apiName nonInteractive
* @apiGroup Schnorr
* @apiDescription
@@ -184,7 +194,7 @@ class Schnorr {
}
/**
* @api Schnorr.computeEll() computeEll() - Generate ell which is the hash over all public keys participating in a session.
* @api Schnorr.computeEll() computeEll()
* @apiName computeEll
* @apiGroup Schnorr
* @apiDescription
@@ -258,7 +268,7 @@ class Schnorr {
}
/**
* @api Schnorr.publicKeyCombine() publicKeyCombine() - Creates a special rogue-key-resistant.
* @api Schnorr.publicKeyCombine() publicKeyCombine()
* @apiName publicKeyCombine
* @apiGroup Schnorr
* @apiDescription
@@ -276,7 +286,7 @@ class Schnorr {
}
/**
* @api Schnorr.sessionInitialize() sessionInitialize() - Creates a signing session.
* @api Schnorr.sessionInitialize() sessionInitialize()
* @apiName sessionInitialize
* @apiGroup Schnorr
* @apiDescription
@@ -315,7 +325,7 @@ class Schnorr {
}
/**
* @api Schnorr.sessionNonceCombine() sessionNonceCombine() - Combines multiple nonces R_i into the combined nonce R.
* @api Schnorr.sessionNonceCombine() sessionNonceCombine()
* @apiName sessionNonceCombine
* @apiGroup Schnorr
* @apiDescription
@@ -360,7 +370,7 @@ class Schnorr {
}
/**
* @api Schnorr.partialSign() partialSign() - Creates a partial signature s_i for a participant.
* @api Schnorr.partialSign() partialSign()
* @apiName partialSign
* @apiGroup Schnorr
* @apiDescription
@@ -392,7 +402,7 @@ class Schnorr {
}
/**
* @api Schnorr.partialSignatureVerify() partialSignatureVerify() - Verifies a partial signature s_i against the participant's public key P_i.
* @api Schnorr.partialSignatureVerify() partialSignatureVerify()
* @apiName partialSignatureVerify
* @apiGroup Schnorr
* @apiDescription
@@ -445,7 +455,7 @@ class Schnorr {
}
/**
* @api Schnorr.partialSignaturesCombine() partialSignaturesCombine() - Combines multiple partial signatures into a Schnorr signature.
* @api Schnorr.partialSignaturesCombine() partialSignaturesCombine()
* @apiName partialSignaturesCombine
* @apiGroup Schnorr
* @apiDescription
+11 -11
View File
@@ -14,7 +14,7 @@ class Address extends BCHJSAddress {
}
/**
* @api SLP.Address.toSLPAddress() toSLPAddress() - Converting to slpAddress format.
* @api SLP.Address.toSLPAddress() toSLPAddress()
* @apiName toSLPAddress
* @apiGroup SLP
* @apiDescription Converting legacy or cashaddr to slpAddress format.
@@ -60,7 +60,7 @@ class Address extends BCHJSAddress {
}
/**
* @api SLP.Address.toCashAddress() toCashAddress() - Converting to cashAddress format.
* @api SLP.Address.toCashAddress() toCashAddress()
* @apiName toCashAddress
* @apiGroup SLP
* @apiDescription Converting legacy or slpaddr to cashAddress format.
@@ -106,7 +106,7 @@ class Address extends BCHJSAddress {
}
/**
* @api SLP.Address.toLegacyAddress() toLegacyAddress() - Converting to legacy address format.
* @api SLP.Address.toLegacyAddress() toLegacyAddress()
* @apiName toLegacyAddress
* @apiGroup SLP
* @apiDescription Converting cashaddr or slpaddr to legacy address format.
@@ -164,7 +164,7 @@ class Address extends BCHJSAddress {
}
/**
* @api SLP.Address.isSLPAddress() isSLPAddress() - Detect if slpAddr encoded address.
* @api SLP.Address.isSLPAddress() isSLPAddress()
* @apiName isSLPAddress
* @apiGroup SLP
* @apiDescription Detect if slpAddr encoded address.
@@ -201,7 +201,7 @@ class Address extends BCHJSAddress {
}
/**
* @api SLP.Address.isMainnetAddress() isMainnetAddress() - Detect if mainnet address.
* @api SLP.Address.isMainnetAddress() isMainnetAddress()
* @apiName isMainnetAddress
* @apiGroup SLP
* @apiDescription Detect if mainnet address.
@@ -255,7 +255,7 @@ class Address extends BCHJSAddress {
}
/**
* @api SLP.Address.isTestnetAddress() isTestnetAddress() - Detect if testnet address.
* @api SLP.Address.isTestnetAddress() isTestnetAddress()
* @apiName isTestnetAddress
* @apiGroup SLP
* @apiDescription Detect if testnet address.
@@ -307,7 +307,7 @@ class Address extends BCHJSAddress {
return bchAddress.isTestnetAddress(cashAddr)
}
/**
* @api SLP.Address.isP2PKHAddress() isP2PKHAddress() - Detect if p2pkh address.
* @api SLP.Address.isP2PKHAddress() isP2PKHAddress()
* @apiName isP2PKHAddress
* @apiGroup SLP
* @apiDescription Detect if p2pkh address.
@@ -359,7 +359,7 @@ class Address extends BCHJSAddress {
return bchAddress.isP2PKHAddress(cashAddr)
}
/**
* @api SLP.Address.isP2SHAddress() isP2SHAddress() - Detect if p2sh address.
* @api SLP.Address.isP2SHAddress() isP2SHAddress()
* @apiName isP2SHAddress
* @apiGroup SLP
* @apiDescription Detect if p2sh address.
@@ -411,7 +411,7 @@ class Address extends BCHJSAddress {
return bchAddress.isP2SHAddress(cashAddr)
}
/**
* @api SLP.Address.detectAddressFormat() detectAddressFormat() - Detect address format.
* @api SLP.Address.detectAddressFormat() detectAddressFormat()
* @apiName detectAddressFormat
* @apiGroup SLP
* @apiDescription Detect address format.
@@ -464,7 +464,7 @@ class Address extends BCHJSAddress {
return bchAddress.detectAddressFormat(address)
}
/**
* @api SLP.Address.detectAddressNetwork() detectAddressNetwork() - Detect address network.
* @api SLP.Address.detectAddressNetwork() detectAddressNetwork()
* @apiName detectAddressNetwork
* @apiGroup SLP
* @apiDescription Detect address network.
@@ -516,7 +516,7 @@ class Address extends BCHJSAddress {
return bchAddress.detectAddressNetwork(cashAddr)
}
/**
* @api SLP.Address.detectAddressType() detectAddressType() - Detect address type.
* @api SLP.Address.detectAddressType() detectAddressType()
* @apiName detectAddressType
* @apiGroup SLP
* @apiDescription Detect address type.
+1 -1
View File
@@ -13,7 +13,7 @@ class ECPair extends BCHJSECPair {
}
*/
/**
* @api SLP.ECPair.toSLPAddress() toSLPAddress() - Get slp address of ECPair.
* @api SLP.ECPair.toSLPAddress() toSLPAddress()
* @apiName toSLPAddress
* @apiGroup SLP
* @apiDescription Get slp address of ECPair.
+20 -11
View File
@@ -18,22 +18,31 @@ const Utils = require("./utils")
// SLP is a superset of BITBOX
class SLP {
constructor(config) {
const tmp = {}
if (!config || !config.restURL) {
tmp.restURL = `https://api.fullstack.cash/v3/`
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 {
tmp.restURL = config.restURL
tmp.apiToken = config.apiToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
}
this.restURL = tmp.restURL
this.apiToken = tmp.apiToken
this.Address = new Address(tmp)
this.Address = new Address(config)
this.ECPair = ECPair
this.TokenType1 = new TokenType1(tmp)
this.TokenType1 = new TokenType1(config)
this.NFT1 = new NFT1(this.restURL)
this.Utils = new Utils(tmp)
this.Utils = new Utils(config)
}
}
+18 -7
View File
@@ -18,17 +18,28 @@ class TokenType1 {
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}`
}
}
}
addy = new Address(config)
this.Script = new Script()
this.axios = axios
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
// Instantiate the transaction builder.
TransactionBuilder.setAddress(addy)
@@ -426,7 +437,7 @@ class TokenType1 {
return slpSendObj
} catch (err) {
console.log(err)
// console.log(err)
throw err
}
}
+336 -104
View File
@@ -1,10 +1,10 @@
const axios = require("axios")
const slpParser = require("slp-parser")
const Script = require("../script")
const scriptLib = new Script()
// const Script = require("../script")
// const scriptLib = new Script()
const BigNumber = require("bignumber.js")
// const BigNumber = require("bignumber.js")
let _this
@@ -13,11 +13,21 @@ class Utils {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.slpParser = slpParser
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
@@ -194,7 +204,7 @@ class Utils {
path = `${this.restURL}slp/list`
}
//console.log(`path: ${path}`)
// console.log(`path: ${path}`)
try {
let response
@@ -316,7 +326,7 @@ class Utils {
return response.data
}
throw new Error(`Input address must be a string or array of strings.`)
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
throw error
@@ -370,84 +380,6 @@ class Utils {
}
}
/**
* @api SLP.Utils.balance() balance()
* @apiName balance
* @apiGroup SLP Utils
* @apiDescription Return single balance for an address by token id.
*
* @apiExample Example usage:
*
* // single balance for SLP Address
* (async () => {
* try {
* let balance = await bchjs.SLP.Utils.balance(
* "simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk",
* "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
* );
* console.log(balance);
* } catch (error) {
* console.error(error);
* }
* })();
*
* // returns
* // { tokenId:
* // 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
* // balance: '617',
* // decimalCount: 8 }
*
* // single balance for Cash Address
* (async () => {
* try {
* let balance = await bchjs.SLP.Utils.balance(
* "bitcoincash:qr5agtachyxvrwxu76vzszan5pnvuzy8dumh7ynrwg",
* "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
* );
* console.log(balance);
* } catch (error) {
* console.error(error);
* }
* })();
*
* // returns
* // { tokenId:
* // 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
* // balance: '617',
* // decimalCount: 8 }
*
* // single balance for Legacy Address
* (async () => {
* try {
* let balance = await bchjs.SLP.Utils.balance(
* "1DwQqpWc8pzaRydCmiJsPdoqCzmjSQQbp8",
* "467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5"
* );
* console.log(balance);
* } catch (error) {
* console.error(error);
* }
* })();
*
* // returns
* // { tokenId:
* // '467969e067f5612863d0bf2daaa70dede2c6be03abb6fd401c5ef6e1e1f1f5c5',
* // balance: '1234',
* // decimalCount: 2 }
*/
// Retrieve a balance for a specific address and token ID
async balance(address, tokenId) {
const path = `${this.restURL}slp/balance/${address}/${tokenId}`
try {
const response = await axios.get(path, _this.axiosOptions)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
throw error
}
}
/**
* @api SLP.Utils.validateTxid() validateTxid()
* @apiName validateTxid
@@ -533,6 +465,66 @@ class Utils {
}
}
/**
* @api SLP.Utils.validateTxid2() validateTxid2()
* @apiName validateTxid2
* @apiGroup SLP Utils
* @apiDescription Validate that txid is an SLP transaction.
*
* This second validatoin version uses the slp-validate slp library. It is
* much slower and less efficient than SLPDB and is prone to time-outs for
* tokens with large DAGs. However, it operates independently of SLPDB and
* is a great second validation option, particularly when SLPDB returns 'null'
* values.
*
* Due to the inefficiency of this call, only a single TXID can be input at a
* time. This call will throw an error if the input is an array.
*
* @apiExample Example usage:
*
* // validate single SLP txid
* (async () => {
* try {
* let validated = await bchjs.SLP.Utils.validateTxid(
* "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
* );
* console.log(validated);
* } catch (error) {
* console.error(error);
* }
* })();
*
* // returns
* [ { txid:
* 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
* valid: true } ]
*/
async validateTxid2(txid) {
try {
// console.log(`txid: ${JSON.stringify(txid, null, 2)}`)
if (
!txid ||
txid === "" ||
typeof txid !== "string" ||
txid.length !== 64
)
throw new Error("txid must be 64 character string.")
const path = `${this.restURL}slp/validateTxid2/${txid}`
const response = await axios.get(path, _this.axiosOptions)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
if (error.error && error.error.indexOf("Network error") > -1)
throw new Error("slp-validate timed out")
throw error
}
}
/**
* @api SLP.Utils.tokenStats() tokenStats()
* @apiName tokenStats
@@ -718,7 +710,7 @@ class Utils {
typeof txid !== "string" ||
txid.length !== 64
)
throw new Error(`txid string must be included.`)
throw new Error("txid string must be included.")
// console.log(`this.restURL: ${this.restURL}`)
const path = `${this.restURL}slp/txDetails/${txid}`
@@ -740,6 +732,9 @@ class Utils {
* Similar to decodeOpReturn(), except decodeOpReturn2() uses the slp-parser
* library maintained by JT Freeman. Outputs have slightly different format.
*
* If optional associative array parameter cache is used, will cache and
* reuse responses for the same input.
*
* Throws an error if given a non-SLP txid.
*
* In a future version of bch-js, this method will replace the origonal
@@ -772,11 +767,26 @@ class Utils {
* }
*/
// Reimplementation of decodeOpReturn() using slp-parser.
async decodeOpReturn(txid) {
async decodeOpReturn(txid, cache = null) {
// The cache object is an in-memory cache (JS Object) that can be passed
// into this function. It helps if multiple vouts from the same TXID are
// being evaluated. In that case, it can significantly reduce the number
// of API calls.
// To use: add the output of this function to the cache object:
// cache[txid] = returnValue
// Then pass that cache object back into this function every time its called.
if (cache) {
if (!(cache instanceof Object))
throw new Error("decodeOpReturn cache parameter must be Object")
const cachedVal = cache[txid]
if (cachedVal) return cachedVal
}
try {
// Validate the txid input.
if (!txid || txid === "" || typeof txid !== "string")
throw new Error(`txid string must be included.`)
throw new Error("txid string must be included.")
// Retrieve the transaction object from the full node.
const path = `${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=true`
@@ -824,8 +834,11 @@ class Utils {
}
// console.log(`tokenData: ${JSON.stringify(tokenData, null, 2)}`)
if (cache) cache[txid] = tokenData
return tokenData
} catch (error) {
// console.log('decodeOpReturn error: ', error)
if (error.response && error.response.data) throw error.response.data
throw error
}
@@ -844,18 +857,20 @@ class Utils {
* set to false.
* If the UTXO is part of an SLP transaction, it will return the UTXO object
* with additional SLP information attached. An `isValid` property will be included.
* If its value is true, the UTXO is a valid SLP UTXO. If the value is null,
* then SLPDB has not yet processed that txid and validity has not been confirmed.
* If its value is true, the UTXO is a valid SLP UTXO.
* If the isValid value is null,
* then SLPDB has not yet processed that txid and validity has not been confirmed,
* or a 429 rate-limit error was enountered during the processing of the request.
*
* @apiExample Example usage:
*
* (async () => {
* try {
* const utxos = await bchjs.Blockbook.utxos(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
* const utxos = await bchjs.Electrumx.utxo(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
*
* const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
*
* console.log(`utxoInfo: ${JSON.stringify(isSLPUtxo,null,2)}`)
* console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
* } catch (error) {
* console.error(error)
* }
@@ -888,8 +903,12 @@ class Utils {
// CT 5/31/20: Refactored to use slp-parse library.
async tokenUtxoDetails(utxos) {
try {
// utxo list may have duplicate tx_hash, varying tx_pos
// only need to call decodeOpReturn once for those
const decodeOpReturnCache = {}
const cachedTxValidation = {}
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error(`Input must be an array.`)
if (!Array.isArray(utxos)) throw new Error("Input must be an array.")
// Loop through each element in the array and validate the input before
// further processing.
@@ -945,15 +964,33 @@ class Utils {
// If there is no OP_RETURN, mark the UTXO as false.
let slpData = false
try {
slpData = await this.decodeOpReturn(utxo.txid)
slpData = await this.decodeOpReturn(utxo.txid, decodeOpReturnCache)
// console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
} catch (err) {
// console.log(`error: `, err)
// console.log(`error from decodeOpReturn(${utxo.txid}): `, err)
// An error will be thrown if the txid is not SLP.
// Mark as false and continue the loop.
// outAry.push(false)
utxo.isValid = false
outAry.push(utxo)
// If error is for some other reason, like a 429 error, mark utxo as 'null'
// to display the unknown state.
if (
!err.message ||
(err.message.indexOf("scriptpubkey not op_return") === -1 &&
err.message.indexOf("lokad id") === -1)
) {
// console.log(`error from decodeOpReturn(${utxo.txid}): `, err)
utxo.isValid = null
outAry.push(utxo)
// If error is thrown because there is no OP_RETURN, then it's not
// an SLP UTXO.
// Mark as false and continue the loop.
} else {
utxo.isValid = false
outAry.push(utxo)
}
// Halt the execution of the loop and increase to the next index.
continue
}
@@ -1013,7 +1050,10 @@ class Utils {
// If UTXO passes validation, then return formatted token data.
else {
const genesisData = await this.decodeOpReturn(slpData.tokenId)
const genesisData = await this.decodeOpReturn(
slpData.tokenId,
decodeOpReturnCache
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// Minting Baton
@@ -1061,7 +1101,10 @@ class Utils {
// If UTXO passes validation, then return formatted token data.
else {
const genesisData = await this.decodeOpReturn(slpData.tokenId)
const genesisData = await this.decodeOpReturn(
slpData.tokenId,
decodeOpReturnCache
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
@@ -1088,7 +1131,11 @@ class Utils {
// Finally, validate the SLP txid with SLPDB.
if (outAry[i].tokenType) {
const isValid = await this.validateTxid(utxo.txid)
var isValid = cachedTxValidation[utxo.txid]
if (isValid == null) {
isValid = await this.validateTxid(utxo.txid)
cachedTxValidation[utxo.txid] = isValid
}
// console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`)
outAry[i].isValid = isValid[0].valid
@@ -1101,6 +1148,191 @@ class Utils {
throw error
}
}
/**
* @api SLP.Utils.hydrateUtxos() hydrateUtxos()
* @apiName hydrateUtxos
* @apiGroup SLP Utils
* @apiDescription Hydrate a UTXO with SLP token metadata.
*
* The same as tokenUtxoDetails(), but uses bch-api to do the heavy lifting,
* which greatly reduces the number of API calls.
*
* Expects an array of UTXO objects as input. Returns an array of equal size.
* Returns UTXO data hydrated with token information.
* If the
* UTXO does not belong to a SLP transaction, it will return an `isValid` property
* set to false.
* If the UTXO is part of an SLP transaction, it will return the UTXO object
* with additional SLP information attached. An `isValid` property will be included.
* If its value is true, the UTXO is a valid SLP UTXO. \
* If the isValid value is null,
* then SLPDB has not yet processed that txid and validity has not been confirmed,
* or a 429 rate-limit error was enountered during the processing of the request.
*
* @apiExample Example usage:
*
* (async () => {
* try {
* const utxos = await bchjs.Electrumx.utxo([
* "bitcoincash:qq6mvsm7l92d77zpymmltvaw09p5uzghyuyx7spygg",
* "bitcoincash:qpjdrs8qruzh8xvusdfmutjx62awcepnhyperm3g89",
* "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk"
* ])
*
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
*
* console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
* } catch (error) {
* console.error(error)
* }
* })()
*
* // returns
* {
* "slpUtxos": [
* {
* "utxos": [
* {
* "height": 654522,
* "tx_hash": "516e763932061f9e868652d727045b714db1ecac459e84cd52b5b4cb39572ecc",
* "tx_pos": 0,
* "value": 6000,
* "satoshis": 6000,
* "txid": "516e763932061f9e868652d727045b714db1ecac459e84cd52b5b4cb39572ecc",
* "vout": 0,
* "isValid": false
* }
* ],
* "address": "bitcoincash:qq6mvsm7l92d77zpymmltvaw09p5uzghyuyx7spygg"
* },
* {
* "utxos": [
* {
* "height": 654522,
* "tx_hash": "8ec01d851d9df9fb4b4331275e2ff680257c224100d0081cec6fbeedf982f738",
* "tx_pos": 1,
* "value": 546,
* "satoshis": 546,
* "txid": "8ec01d851d9df9fb4b4331275e2ff680257c224100d0081cec6fbeedf982f738",
* "vout": 1,
* "utxoType": "token",
* "transactionType": "send",
* "tokenId": "a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2",
* "tokenTicker": "TROUT",
* "tokenName": "Trout's test token",
* "tokenDocumentUrl": "troutsblog.com",
* "tokenDocumentHash": "",
* "decimals": 2,
* "tokenType": 1,
* "tokenQty": 10,
* "isValid": true
* }
* ],
* "address": "bitcoincash:qpjdrs8qruzh8xvusdfmutjx62awcepnhyperm3g89"
* },
* {
* "utxos": [
* {
* "height": 654522,
* "tx_hash": "072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
* "tx_pos": 0,
* "value": 6999,
* "satoshis": 6999,
* "txid": "072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
* "vout": 0,
* "isValid": false
* },
* {
* "height": 654522,
* "tx_hash": "a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
* "tx_pos": 1,
* "value": 546,
* "satoshis": 546,
* "txid": "a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
* "vout": 1,
* "utxoType": "token",
* "transactionType": "send",
* "tokenId": "6201f3efe486c577433622817b99645e1d473cd3882378f9a0efc128ab839a82",
* "tokenTicker": "VALENTINE",
* "tokenName": "Valentine day token",
* "tokenDocumentUrl": "fullstack.cash",
* "tokenDocumentHash": "",
* "decimals": 2,
* "tokenType": 1,
* "tokenQty": 5,
* "isValid": true
* }
* ],
* "address": "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk"
* }
* ]
* }
*
* (async () => {
* try {
* const utxos = [
* {
* utxos: [
* {
* txid: "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
* vout: 3,
* value: "6816",
* height: 606848,
* confirmations: 13,
* satoshis: 6816
* }
* ]
* }
* ]
*
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos)
*
* console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
* } catch (error) {
* console.error(error)
* }
* })()
*
* // returns
* {
* "slpUtxos": [
* {
* "utxos": [
* {
* "txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
* "vout": 3,
* "value": "6816",
* "height": 606848,
* "confirmations": 13,
* "satoshis": 6816,
* "isValid": false
* }
* ]
* }
* ]
*/
// Same as tokenUtxoDetails(), but reduces API calls by having bch-api server
// do the heavy lifting.
async hydrateUtxos(utxos) {
try {
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error("Input must be an array.")
const response = await axios.post(
`${this.restURL}slp/hydrateUtxos`,
{
utxos: utxos
},
_this.axiosOptions
)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = Utils
+5 -5
View File
@@ -37,7 +37,7 @@ class TransactionBuilder {
}
/**
* @api Transaction-Builder.addInput() addInput() - Add input to transaction.
* @api Transaction-Builder.addInput() addInput()
* @apiName AddInput
* @apiGroup TransactionBuilder
* @apiDescription Add input to transaction.
@@ -66,7 +66,7 @@ class TransactionBuilder {
this.p2shInput = true
}
/**
* @api Transaction-Builder.addOutput() addOutput() - Add output to transaction.
* @api Transaction-Builder.addOutput() addOutput()
* @apiName AddOutput
* @apiGroup TransactionBuilder
* @apiDescription Add output to transaction.
@@ -90,7 +90,7 @@ class TransactionBuilder {
}
}
/**
* @api Transaction-Builder.setLockTime() setLockTime() - Set locktime.
* @api Transaction-Builder.setLockTime() setLockTime()
* @apiName SetLockTime
* @apiGroup TransactionBuilder
* @apiDescription Set locktime.
@@ -108,7 +108,7 @@ class TransactionBuilder {
this.transaction.setLockTime(locktime)
}
/**
* @api Transaction-Builder.sign() sign() - Sign transaction..
* @api Transaction-Builder.sign() sign()
* @apiName Sign.
* @apiGroup TransactionBuilder
* @apiDescription Sign transaction. It creates the unlocking script needed to spend an input. Each input has its own script and thus 'sign' must be called for each input even if the keyPair is the same..
@@ -145,7 +145,7 @@ class TransactionBuilder {
)
}
/**
* @api Transaction-Builder.build() build() - Build transaction.
* @api Transaction-Builder.build() build()
* @apiName Build.
* @apiGroup TransactionBuilder
* @apiDescription Build transaction.
+15 -5
View File
@@ -6,11 +6,21 @@ class Util {
constructor(config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
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}`
}
}
}
@@ -18,7 +28,7 @@ class Util {
}
/**
* @api util.validateAddress() validateAddress() - Get information about the given bitcoin address.
* @api util.validateAddress() validateAddress()
* @apiName Validate Address.
* @apiGroup Util
* @apiDescription Return information about the given bitcoin address.
+12 -5
View File
@@ -4,11 +4,18 @@
To Run Test:
- Update the RESTURL for bch-api you want to test against.
- Ensure the BCHJSTOKEN environment variable is set to an empty string.
- If working with bch-api locally, eliminate the local IP address from the
whitelist in bch-api/src/middleware/route-ratelimit.js
Run this test with this command:
mocha --timeout=30000 anonymous-rate-limits.js
*/
const assert = require("chai").assert
const RESTURL = `https://api.fullstack.cash/v3/`
// const RESTURL = `https://abc.fullstack.cash/v3/`
const RESTURL = `https://bchn.fullstack.cash/v3/`
// const RESTURL = `http://localhost:3000/v3/`
const BCHJS = require("../../../src/bch-js")
@@ -23,7 +30,7 @@ describe("#anonymous rate limits", () => {
it("should allow an anonymous call to an indexer", async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
const result = await bchjs.Blockbook.balance(addr)
const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "balance")
@@ -31,11 +38,11 @@ describe("#anonymous rate limits", () => {
it("should throw error when rate limit exceeded", async () => {
try {
for (let i = 0; i < 5; i++) await bchjs.Control.getNetworkInfo()
for (let i = 0; i < 35; i++) await bchjs.Control.getNetworkInfo()
assert.equal(true, false, "unexpected result")
assert.fail("Unexpected result")
} catch (err) {
assert.include(err.error, "Too many requests")
}
}).timeout(5000)
}).timeout(20000)
})
@@ -0,0 +1,56 @@
/*
Mocha test that verifies that the bch-api server is enforcing its
access rules for Basic Authentication.
To Run Test:
- Update the restURL for bch-api you want to test against.
- Update the PRO_PASS value with a current PRO_PASSES string used by bch-api.
*/
const assert = require("chai").assert
const PRO_PASS = "testpassword"
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v3/`,
restURL: `https://abc.fullstack.cash/v3/`,
// restURL: `http://localhost:3000/v3/`,
authPass: PRO_PASS
})
describe("#Basic Authentication rate limits", () => {
it("should allow more than 20 RPM to full node", async () => {
for (let i = 0; i < 22; i++) {
const result = await bchjs.Control.getNetworkInfo()
if (i === 5) {
// console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 3 calls allowed")
}
if (i === 15) {
// console.log(`validating 5th call: ${i}`)
assert.property(result, "version", "more than 10 calls allowed")
}
}
}).timeout(45000)
it("should allow more than 20 RPM to an indexer", async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
for (let i = 0; i < 22; i++) {
const result = await bchjs.Blockbook.balance(addr)
if (i === 5) {
// console.log(`validating 5th call: ${i}`)
assert.property(result, "balance", "more than 3 calls allowed")
}
if (i === 15) {
// console.log(`validating 5th call: ${i}`)
assert.property(result, "balance", "more than 10 calls allowed")
}
}
}).timeout(45000)
})
+16 -14
View File
@@ -1,4 +1,8 @@
/*
CT 11/11/2020:
There is no longer a free tier. These tests have been deprecated.
--------
Mocha test that verifies that the bch-api server is enforcing its
FREE access rules
@@ -10,7 +14,7 @@
const assert = require("chai").assert
const JWT_TOKEN =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2YyMDIyMWMxMDAxMmFkOTQwMSIsImVtYWlsIjoiZGVtb0BkZW1vLmNvbSIsImFwaUxldmVsIjoxMCwicmF0ZUxpbWl0IjozLCJpYXQiOjE1OTA1Mjk2NzgsImV4cCI6MTU5MzEyMTY3OH0.4q5MFDCcVx0FusivJPiEIj3w8nhFG7ZV5IhJS7faUlc"
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2YyMDIyMWMxMDAxMmFkOTQwMSIsImVtYWlsIjoiZGVtb0BkZW1vLmNvbSIsImFwaUxldmVsIjoxMCwicmF0ZUxpbWl0IjozLCJpYXQiOjE2MDQ4NTQ2OTEsImV4cCI6MTYwNzQ0NjY5MX0.iwse0z0KDKHx9graCxcOwj6lSlfKQAb1zLhmjvygvts"
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({
@@ -28,19 +32,6 @@ describe("#free rate limits", () => {
assert.property(result, "balance")
}).timeout(5000)
// it("should throw error when rate limit exceeded 3 RPM for indexer endpoints", async () => {
// const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
//
// try {
// for (let i = 0; i < 5; i++) await bchjs.Blockbook.balance(addr)
//
// assert.equal(true, false, "unexpected result")
// } catch (err) {
// // console.log(`err: `, err)
// assert.include(err.error, "Too many requests")
// }
// }).timeout(10000)
it("should allow up to 10 RPM to indexer, then throw error", async () => {
try {
const addr = "bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0"
@@ -87,4 +78,15 @@ describe("#free rate limits", () => {
assert.notInclude(err.error, 3)
}
}).timeout(20000)
it("should throw error when rate limit exceeded 20 RPM for indexer endpoints", async () => {
try {
for (let i = 0; i < 22; i++) await bchjs.Control.getNetworkInfo()
assert.fail("unexpected result")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.error, "Too many requests")
}
}).timeout(20000)
})
+27 -20
View File
@@ -1,20 +1,25 @@
/*
CT 11/11/2020:
There is no longer a free tier. These tests have been deprecated.
----
Mocha test that verifies that the bch-api server is enforcing its
full-node access rules
To Run Test:
- Update the restURL for bch-api you want to test against.
- Update the JWT_TOKEN value with a current full-node-level JWT token.
- Update the JWT_TOKEN value with a paid tier JWT token.
*/
const assert = require("chai").assert
const JWT_TOKEN =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYmY4MjA1YTYwODliMjliYTlhZjc1OSIsImlhdCI6MTU3MjgzMTgzOSwiZXhwIjoxNTc1NDIzODM5fQ.sFFS4AF04U2aUwmKkBIfnMQIUoygzV9UMVzzQuwctpY"
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M"
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({
restURL: `https://api.bchjs.cash/v3/`,
// restURL: `https://bchn.fullstack.cash/v3/`,
restURL: `https:/abc.fullstack.cash/v3/`,
// restURL: `http://localhost:3000/v3/`,
apiToken: JWT_TOKEN
})
@@ -22,27 +27,28 @@ const bchjs = new BCHJS({
describe("#full node rate limits", () => {
it("should allow an free call to an indexer", async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
const result = await bchjs.Blockbook.balance(addr)
const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "balance")
}).timeout(5000)
it("should throw error when rate limit exceeded 3 RPM for indexer endpoints", async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
// CT 11/8/20: New rate limits make this test invalid.
// it("should throw error when rate limit exceeded 20 RPM for indexer endpoints", async () => {
// const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
//
// try {
// for (let i = 0; i < 22; i++) await bchjs.Electrumx.balance(addr)
//
// assert.fail("unexpected result")
// } catch (err) {
// // console.log(`err: `, err)
// assert.include(err.error, "Too many requests")
// }
// }).timeout(10000)
try {
for (let i = 0; i < 5; i++) await bchjs.Blockbook.balance(addr)
assert.equal(true, false, "unexpected result")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.error, "Too many requests")
}
}).timeout(10000)
it("should allow more than 10 RPM to full node", async () => {
for (let i = 0; i < 20; i++) {
it("should allow more than 20 RPM to full node", async () => {
for (let i = 0; i < 22; i++) {
const result = await bchjs.Control.getNetworkInfo()
if (i === 5) {
@@ -55,10 +61,11 @@ describe("#full node rate limits", () => {
assert.property(result, "version", "more than 10 calls allowed")
}
}
}).timeout(10000)
}).timeout(20000)
it("should throw error for more than 100 RPM to fullnode", async () => {
try {
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`)
@@ -66,5 +73,5 @@ describe("#full node rate limits", () => {
assert.include(err.error, "Too many requests")
assert.include(err.error, 100)
}
}).timeout(30000)
}).timeout(45000)
})
+11 -10
View File
@@ -10,18 +10,19 @@
const assert = require("chai").assert
const JWT_TOKEN =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTU5MDQzMjIxNywiZXhwIjoxNTkzMDI0MjE3fQ.R0Euhutd5FHCxLlSzjq1TYsGjI6iua_AguWIeLK2xCU"
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M"
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({
restURL: `https://api.fullstack.cash/v3/`,
// restURL: `https://bchn.fullstack.cash/v3/`,
restURL: `https://abc.fullstack.cash/v3/`,
// restURL: `http://localhost:3000/v3/`,
apiToken: JWT_TOKEN
})
describe("#full node rate limits", () => {
it("should allow more than 10 RPM to full node", async () => {
for (let i = 0; i < 20; i++) {
describe("#Indexer rate limits", () => {
it("should allow more than 20 RPM to full node", async () => {
for (let i = 0; i < 22; i++) {
const result = await bchjs.Control.getNetworkInfo()
if (i === 5) {
@@ -34,13 +35,13 @@ describe("#full node rate limits", () => {
assert.property(result, "version", "more than 10 calls allowed")
}
}
}).timeout(30000)
}).timeout(45000)
it("should allow more than 10 RPM to an indexer", async () => {
it("should allow more than 20 RPM to an indexer", async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
for (let i = 0; i < 20; i++) {
const result = await bchjs.Blockbook.balance(addr)
for (let i = 0; i < 22; i++) {
const result = await bchjs.Electrumx.balance(addr)
if (i === 5) {
// console.log(`validating 5th call: ${i}`)
@@ -52,5 +53,5 @@ describe("#full node rate limits", () => {
assert.property(result, "balance", "more than 10 calls allowed")
}
}
}).timeout(30000)
}).timeout(45000)
})
+331
View File
@@ -0,0 +1,331 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
TODO
- getMempoolEntry() only works on TXs in the mempool, so it needs to be part
of an e2e test to be properly tested.
*/
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS()
// Inspect utility used for debugging.
const util = require("util")
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe(`#blockchain`, () => {
describe(`#getBestBlockHash`, () => {
it(`should GET best block hash`, async () => {
const result = await bchjs.Blockchain.getBestBlockHash()
//console.log(`result: ${util.inspect(result)}`)
assert.isString(result)
assert.equal(result.length, 64, "Specific hash length")
})
})
describe("#getBlockHeader", () => {
it(`should GET block header for a single hash`, async () => {
const hash =
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"
const result = await bchjs.Blockchain.getBlockHeader(hash)
assert.hasAllKeys(result, [
"hash",
"confirmations",
"height",
"version",
"versionHex",
"merkleroot",
"time",
"mediantime",
"nonce",
"bits",
"difficulty",
"chainwork",
"previousblockhash",
"nextblockhash",
"nTx"
])
})
it(`should GET block headers for an array of hashes`, async () => {
const hash = [
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201",
"00000000000000000568f0a96bf4348847bc84e455cbfec389f27311037a20f3"
]
const result = await bchjs.Blockchain.getBlockHeader(hash)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"hash",
"confirmations",
"height",
"version",
"versionHex",
"merkleroot",
"time",
"mediantime",
"nonce",
"bits",
"difficulty",
"chainwork",
"previousblockhash",
"nextblockhash",
"nTx"
])
})
it(`should throw an error for improper input`, async () => {
try {
const hash = 12345
await bchjs.Blockchain.getBlockHeader(hash)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input hash must be a string or array of strings`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const data = []
for (let i = 0; i < 25; i++) {
data.push(
"000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"
)
}
const result = await bchjs.Blockchain.getBlockHeader(data)
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")
}
})
})
describe("#getMempoolEntry", () => {
/*
// To run this test, the txid must be unconfirmed.
const txid =
"defea04c38ee00cf73ad402984714ed22dc0dd99b2ae5cb50d791d94343ba79b"
it(`should GET single mempool entry`, async () => {
const result = await bchjs.Blockchain.getMempoolEntry(txid)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
"size",
"fee",
"modifiedfee",
"time",
"height",
"startingpriority",
"currentpriority",
"descendantcount",
"descendantsize",
"descendantfees",
"ancestorcount",
"ancestorsize",
"ancestorfees",
"depends"
])
})
it(`should get an array of mempool entries`, async () => {
const result = await bchjs.Blockchain.getMempoolEntry([txid, txid])
console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"size",
"fee",
"modifiedfee",
"time",
"height",
"startingpriority",
"currentpriority",
"descendantcount",
"descendantsize",
"descendantfees",
"ancestorcount",
"ancestorsize",
"ancestorfees",
"depends"
])
})
*/
it(`should throw an error if txid is not in mempool`, async () => {
try {
const txid =
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
await bchjs.Blockchain.getMempoolEntry(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: ${util.inspect(err)}`)
assert.hasAnyKeys(err, ["error"])
assert.include(err.error, `Transaction not in mempool`)
}
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.getMempoolEntry(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
}
})
})
describe(`#getTxOutProof`, () => {
it(`should get single tx out proof`, async () => {
const txid =
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
const result = await bchjs.Blockchain.getTxOutProof(txid)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result)
})
it(`should get an array of tx out proofs`, async () => {
const txid = [
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7",
"fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33"
]
const result = await bchjs.Blockchain.getTxOutProof(txid)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.getTxOutProof(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
}
})
})
describe(`#verifyTxOutProof`, () => {
const mockTxOutProof =
"0000002086a4a3161f9ba2174883ec0b93acceac3b2f37b36ed1f90000000000000000009cb02406d1094ecf3e0b4c0ca7c585125e721147c39daf6b48c90b512741e13a12333e5cb38705180f441d8c7100000008fee9b60f1edb57e5712839186277ed39e0a004a32be9096ee47472efde8eae62f789f9d7a9f59d0ea7093dea1e0c65ff0b953f1d8cf3d47f92e732ca0295f603c272d5f4a63509f7a887f2549d78af7444aa0ecbb4f66d9cbe13bc6a89f59e05a199df8325d490818ffefe6b6321d32d7496a68580459836c0183f89082fc1b491cc91b23ecdcaa4c347bf599a62904d61f1c15b400ebbd5c90149010c139d9c1e31b774b796977393a238080ab477e1d240d0c4f155d36f519668f49bae6bd8cd5b8e40522edf76faa09cca6188d83ff13af6967cc6a569d1a5e9aeb1fdb7f531ddd2d0cbb81879741d5f38166ac1932136264366a4065cc96a42e41f96294f02df01"
it(`should verify a single proof`, async () => {
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
assert.equal(
result[0],
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
)
})
it(`should verify an array of proofs`, async () => {
const proofs = [mockTxOutProof, mockTxOutProof]
const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
assert.equal(
result[0],
"03f69502ca32e7927fd4f38c1d3f950bff650c1eea3d09a70e9df5a9d7f989f7"
)
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.verifyTxOutProof(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const data = []
for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
const result = await bchjs.Blockchain.verifyTxOutProof(data)
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")
}
})
})
describe("#getTxOut", () => {
it("should get information on an unspent tx", async () => {
const result = await bchjs.Blockchain.getTxOut(
"62a3ea958a463a372bc0caf2c374a7f60be9c624be63a0db8db78f05809df6d8",
0,
true
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, [
"bestblock",
"confirmations",
"value",
"scriptPubKey",
"coinbase"
])
})
it("should get information on a spent tx", async () => {
const result = await bchjs.Blockchain.getTxOut(
"87380e52d151856b23173d6d8a3db01b984c6b50f77ea045a5a1cf4f54497871",
0,
true
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, null)
})
})
})
+19
View File
@@ -0,0 +1,19 @@
/*
Integration tests for bchjs control library.
*/
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS()
describe(`#control`, () => {
describe(`#getNetworkInfo`, () => {
it("should get info on the full node", async () => {
const result = await bchjs.Control.getNetworkInfo()
console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "version")
})
})
})
+356
View File
@@ -0,0 +1,356 @@
const chai = require("chai")
const assert = chai.assert
const sinon = require("sinon")
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS()
describe(`#ElectrumX`, () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
describe(`#utxo`, () => {
it(`should GET utxos for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "height")
assert.property(result.utxos[0], "tx_hash")
assert.property(result.utxos[0], "tx_pos")
assert.property(result.utxos[0], "value")
})
it(`should POST request for UTXOs for an array of addresses`, async () => {
const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
]
const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos")
assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address")
assert.property(result.utxos[0].utxos[0], "height")
assert.property(result.utxos[0].utxos[0], "tx_hash")
assert.property(result.utxos[0].utxos[0], "tx_pos")
assert.property(result.utxos[0].utxos[0], "value")
})
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
const result = await bchjs.Electrumx.utxo(addr)
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")
}
})
})
describe(`#balance`, () => {
it(`should GET balance for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "balance")
assert.property(result.balance, "confirmed")
assert.property(result.balance, "unconfirmed")
})
it(`should POST request for balances for an array of addresses`, async () => {
const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
]
const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "balances")
assert.isArray(result.balances)
assert.property(result.balances[0], "address")
assert.property(result.balances[0], "balance")
assert.property(result.balances[0].balance, "confirmed")
assert.property(result.balances[0].balance, "unconfirmed")
})
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
const result = await bchjs.Electrumx.balance(addr)
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")
}
})
})
describe(`#transactions`, () => {
it(`should GET transaction history for a single address`, async () => {
const addr = "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "transactions")
assert.isArray(result.transactions)
assert.property(result.transactions[0], "height")
assert.property(result.transactions[0], "tx_hash")
})
it(`should POST request for transaction history for an array of addresses`, async () => {
const addr = [
"bitcoincash:qrl2nlsaayk6ekxn80pq0ks32dya8xfclyktem2mqj",
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
]
const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "transactions")
assert.isArray(result.transactions)
assert.property(result.transactions[0], "address")
assert.property(result.transactions[0], "transactions")
assert.isArray(result.transactions[0].transactions)
assert.property(result.transactions[0].transactions[0], "height")
assert.property(result.transactions[0].transactions[0], "tx_hash")
})
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0")
const result = await bchjs.Electrumx.transactions(addr)
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")
}
})
})
describe(`#unconfirmed`, () => {
// These tests won't work because unconfirmed transactions are transient in nature.
/*
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
const addr = "bitcoincash:qqy6qwk3wpne95hhv8uzwr4cn8m7c06cqgchl77dnv"
const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "height")
assert.property(result.utxos[0], "tx_hash")
assert.property(result.utxos[0], "fee")
})
it(`should POST request for unconfirmed UTXOs (mempool) for an array of addresses`, async () => {
const addr = [
"bitcoincash:qqy6qwk3wpne95hhv8uzwr4cn8m7c06cqgchl77dnv",
"bitcoincash:qpyrtsl9msdu2klgfpcmn2v5r22w9rk24g8pal74ts"
]
const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos")
assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address")
assert.property(result.utxos[0].utxos[0], "height")
assert.property(result.utxos[0].utxos[0], "tx_hash")
assert.property(result.utxos[0].utxos[0], "fee")
})
*/
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
await bchjs.Electrumx.unconfirmed(addr)
// 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")
}
})
})
describe(`#blockHeader`, () => {
it(`should GET block headers for given height and count`, async () => {
const height = 42
const count = 2
const result = await bchjs.Electrumx.blockHeader(height, count)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "headers")
assert.isArray(result.headers)
assert.equal(result.headers.length, 2)
})
it(`should GET block headers for given height with default count = 1`, async () => {
const height = 42
const result = await bchjs.Electrumx.blockHeader(height)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "headers")
assert.isArray(result.headers)
assert.equal(result.headers.length, 1)
})
})
describe(`#txData`, () => {
it(`should GET details for a single transaction`, async () => {
const txid =
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "details")
assert.isObject(result.details)
assert.property(result.details, "blockhash")
assert.property(result.details, "hash")
assert.property(result.details, "hex")
assert.property(result.details, "vin")
assert.property(result.details, "vout")
assert.equal(result.details.hash, txid)
})
it(`should POST details for an array of transactions`, async () => {
const txids = [
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
]
const result = await bchjs.Electrumx.txData(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "transactions")
assert.isArray(result.transactions)
assert.property(result.transactions[0], "txid")
assert.property(result.transactions[0], "details")
assert.property(result.transactions[0].details, "blockhash")
assert.property(result.transactions[0].details, "hash")
assert.property(result.transactions[0].details, "hex")
assert.property(result.transactions[0].details, "vin")
assert.property(result.transactions[0].details, "vout")
})
it(`should throw error on array size rate limit`, async () => {
try {
const txids = []
for (let i = 0; i < 25; i++) {
txids.push(
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
)
}
await bchjs.Electrumx.txData(txids)
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")
}
})
})
describe(`#broadcast`, () => {
it(`should broadcast a single transaction`, async () => {
const txHex =
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
try {
await bchjs.Electrumx.broadcast(txHex)
} catch (err) {
assert.property(err, "success")
assert.equal(err.success, false)
assert.include(
err.error,
"the transaction was rejected by network rules"
)
}
})
})
})
+31
View File
@@ -0,0 +1,31 @@
const assert = require("chai").assert
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS()
describe("#Encryption", () => {
describe("#getPubKey", () => {
it("should get a public key", async () => {
const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq"
const result = await bchjs.encryption.getPubKey(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "publicKey")
})
it("should report when public key can not be found", async () => {
const addr = "bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh"
const result = await bchjs.encryption.getPubKey(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, false)
assert.property(result, "publicKey")
assert.equal(result.publicKey, "not found")
})
})
})
+129
View File
@@ -0,0 +1,129 @@
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(() => (sandbox = sinon.createSandbox()))
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], "isCoinBase")
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")
})
})
})
+117
View File
@@ -0,0 +1,117 @@
/*
tests for OpenBazaar library.
*/
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS()
describe(`#OpenBazaar`, () => {
describe(`#Balance`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.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 GET balance for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.OpenBazaar.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, [
"page",
"totalPages",
"itemsOnPage",
"addrStr",
"balance",
"totalReceived",
"totalSent",
"unconfirmedBalance",
"unconfirmedTxApperances",
"txApperances",
"transactions"
])
assert.isArray(result.transactions)
})
})
describe(`#utxo`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.utxo(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 GET utxos for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.OpenBazaar.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAllKeys(result[0], [
"txid",
"vout",
"amount",
"height",
"confirmations",
"satoshis"
])
})
})
describe(`#tx`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.tx(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(err.message, `Input txid must be a string`)
}
})
it(`should GET transactions for a single address`, async () => {
const addr =
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7"
const result = await bchjs.OpenBazaar.tx(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, [
"txid",
"version",
"vin",
"vout",
"blockhash",
"blockheight",
"confirmations",
"blocktime",
"valueOut",
"valueIn",
"fees",
"hex",
"time"
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
})
})
+42
View File
@@ -0,0 +1,42 @@
const assert = require("chai").assert
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS()
describe("#price", () => {
describe("#current", () => {
describe("#single currency", () => {
it("should get current price for single currency", async () => {
const result = await bchjs.Price.current("usd")
assert.notEqual(0, result)
})
})
})
describe("#getUsd", () => {
it("should get the USD price of BCH", async () => {
const result = await bchjs.Price.getUsd()
// console.log(result)
assert.isNumber(result)
})
})
describe("#getBchaUsd", () => {
it("should get the USD price of BCHA", async () => {
const result = await bchjs.Price.getBchaUsd()
console.log(result)
assert.isNumber(result)
})
})
describe("#rates", () => {
it("should get the price of BCH in several currencies", async () => {
const result = await bchjs.Price.rates()
// console.log(result)
assert.property(result, "USD")
assert.property(result, "CAD")
})
})
})
+329
View File
@@ -0,0 +1,329 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
TODO
*/
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS()
// Inspect utility used for debugging.
const util = require("util")
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe("#rawtransaction", () => {
describe("#decodeRawTransaction", () => {
it("should decode tx for a single hex", async () => {
const hex =
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
const result = await bchjs.RawTransactions.decodeRawTransaction(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout"
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
it("should decode an array of tx hexes", async () => {
const hexes = [
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000",
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
]
const result = await bchjs.RawTransactions.decodeRawTransaction(hexes)
//console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout"
])
assert.isArray(result[0].vin)
assert.isArray(result[0].vout)
})
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.decodeRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const data = []
for (let i = 0; i < 25; i++) {
data.push(
"0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000"
)
}
const result = await bchjs.RawTransactions.decodeRawTransaction(data)
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")
}
})
})
describe("#getRawTransaction", () => {
it("should decode a single txid, with concise output", async () => {
const txid =
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
const verbose = false
const result = await bchjs.RawTransactions.getRawTransaction(
txid,
verbose
)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result)
})
it("should decode a single txid, with verbose output", async () => {
const txid =
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
const verbose = true
const result = await bchjs.RawTransactions.getRawTransaction(
txid,
verbose
)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
"hex",
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout",
"blockhash",
"confirmations",
"time",
"blocktime"
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
it("should decode an array of txids, with a concise output", async () => {
const txid = [
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1",
"b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f"
]
const verbose = false
const result = await bchjs.RawTransactions.getRawTransaction(
txid,
verbose
)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
})
it("should decode an array of txids, with a verbose output", async () => {
const txid = [
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1",
"b25d24fbb42d84812ed2cb55797f10fdec41afc7906ab563d1ec8c8676a2037f"
]
const verbose = true
const result = await bchjs.RawTransactions.getRawTransaction(
txid,
verbose
)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"hex",
"txid",
"hash",
"size",
"version",
"locktime",
"vin",
"vout",
"blockhash",
"confirmations",
"time",
"blocktime"
])
assert.isArray(result[0].vin)
assert.isArray(result[0].vout)
})
it(`should throw error on array size rate limit`, async () => {
try {
const dataMock =
"23213453b4642a73b4fc30d3112d72549ca153a8707255b14373b59e43558de1"
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.RawTransactions.getRawTransaction(data)
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")
}
})
})
describe("#decodeScript", () => {
it("should decode script for a single hex", async () => {
const hex =
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
const result = await bchjs.RawTransactions.decodeScript(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["asm", "type", "p2sh"])
})
// CT 2/20/19 - Waiting for this PR to be merged complete the test:
// https://github.com/Bitcoin-com/rest.bitcoin.com/pull/312
/*
it("should decode an array of tx hexes", async () => {
const hexes = [
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16",
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
]
const result = await bchjs.RawTransactions.decodeScript(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
})
*/
/*
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.decodeRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
*/
})
/*
Testing sentRawTransaction isn't really possible with an integration test,
as the endpoint really needs an e2e test to be properly tested. The tests
below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently.
*/
describe("sendRawTransaction", () => {
it("should send a single transaction hex", async () => {
try {
const hex =
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
await bchjs.RawTransactions.sendRawTransaction(hex)
//console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.fail("Unexpected result!")
} catch (err) {
//console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
}
})
it("should send an array of tx hexes", async () => {
try {
const hexes = [
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000",
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.fail("Unexpected result!")
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
}
})
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.sendRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input hex must be a string or array of strings`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const dataMock =
"01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000"
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.RawTransactions.sendRawTransaction(data)
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")
}
})
})
})
+700
View File
@@ -0,0 +1,700 @@
/*
Integration tests for the bchjs covering SLP tokens.
*/
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.
await sleep(1000)
bchjs = new BCHJS()
})
describe("#util", () => {
describe("#list", () => {
it(`should get information on the Spice token`, async () => {
const tokenId = `4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf`
const result = await bchjs.SLP.Utils.list(tokenId)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
"decimals",
"timestamp",
"timestamp_unix",
"versionType",
"documentUri",
"symbol",
"name",
"containsBaton",
"id",
"documentHash",
"initialTokenQty",
"blockCreated",
"blockLastActiveSend",
"blockLastActiveMint",
"txnsSinceGenesis",
"validAddress",
"totalMinted",
"totalBurned",
"circulatingSupply",
"mintingBatonStatus"
])
})
})
// describe("#decodeOpReturn", () => {
// it("should decode the OP_RETURN for a SEND txid", async () => {
// const txid =
// "266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1"
//
// const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.hasAllKeys(result, [
// "tokenType",
// "transactionType",
// "tokenId",
// "spendData"
// ])
// })
//
// it("should decode the OP_RETURN for a GENESIS txid", async () => {
// const txid =
// "497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
//
// const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.hasAllKeys(result, [
// "tokenType",
// "transactionType",
// "ticker",
// "name",
// "documentUrl",
// "documentHash",
// "decimals",
// "mintBatonVout",
// "initialQty",
// "tokensSentTo",
// "batonHolder"
// ])
// })
//
// it("should decode the OP_RETURN for a MINT txid", async () => {
// const txid =
// "65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4"
//
// const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.hasAllKeys(result, [
// "tokenType",
// "transactionType",
// "tokenId",
// "mintBatonVout",
// "batonStillExists",
// "quantity",
// "tokensSentTo",
// "batonHolder"
// ])
// })
//
// it("should throw an error for a non-SLP transaction", async () => {
// try {
// const txid =
// "3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec"
//
// await bchjs.SLP.Utils.decodeOpReturn(txid)
//
// assert.equal(true, false, "Unexpected result")
// } catch (err) {
// // console.log(`err: `, err)
// assert.include(err.message, "Not an OP_RETURN")
// }
// })
// })
describe("#decodeOpReturn", () => {
it("should decode the OP_RETURN for a SEND txid", async () => {
const txid =
"266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1"
const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["tokenType", "txType", "tokenId", "amounts"])
assert.equal(
result.tokenId,
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
)
// Verify outputs
assert.equal(result.amounts.length, 2)
assert.equal(result.amounts[0], "100000000")
assert.equal(result.amounts[1], "99883300000000")
})
it("should decode the OP_RETURN for a GENESIS txid", async () => {
const txid =
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, [
"tokenType",
"txType",
"tokenId",
"ticker",
"name",
"documentUri",
"documentHash",
"decimals",
"mintBatonVout",
"qty"
])
assert.equal(
result.tokenId,
"497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7"
)
assert.equal(result.txType, "GENESIS")
assert.equal(result.ticker, "TOK-CH")
assert.equal(result.name, "TokyoCash")
})
it("should decode the OP_RETURN for a MINT txid", async () => {
const txid =
"65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4"
const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, [
"tokenType",
"txType",
"tokenId",
"mintBatonVout",
"qty"
])
})
it("should throw an error for a non-SLP transaction", async () => {
try {
const txid =
"3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec"
await bchjs.SLP.Utils.decodeOpReturn(txid)
assert.equal(true, false, "Unexpected result")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, "scriptpubkey not op_return")
}
})
// Note: This TX is interpreted as valid by the original decodeOpReturn().
// Fixing this issue and related issues was the reason for creating the
// decodeOpReturn2() method using the slp-parser library.
it("should throw error for invalid SLP transaction", async () => {
try {
const txid =
"a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415"
await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, "amount string size not 8 bytes")
}
})
})
describe("#tokenUtxoDetails", () => {
// // This captures an important corner-case. When an SLP token is created, the
// // change UTXO will contain the same SLP txid, but it is not an SLP UTXO.
it("should return details on minting baton from genesis transaction", async () => {
const utxos = [
{
txid:
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
vout: 3,
amount: 0.00002015,
satoshis: 2015,
height: 594892,
confirmations: 5
},
{
txid:
"bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90",
vout: 2,
amount: 0.00000546,
satoshis: 546,
height: 594892,
confirmations: 5
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false, "Change UTXO marked as false.")
assert.property(data[1], "txid")
assert.property(data[1], "vout")
assert.property(data[1], "amount")
assert.property(data[1], "satoshis")
assert.property(data[1], "height")
assert.property(data[1], "confirmations")
assert.property(data[1], "utxoType")
assert.property(data[1], "tokenId")
assert.property(data[1], "tokenTicker")
assert.property(data[1], "tokenName")
assert.property(data[1], "tokenDocumentUrl")
assert.property(data[1], "tokenDocumentHash")
assert.property(data[1], "decimals")
assert.property(data[1], "isValid")
assert.equal(data[1].isValid, true)
})
it("should return details for a MINT token utxo", async () => {
// Mock the call to REST API
const utxos = [
{
txid:
"cf4b922d1e1aa56b52d752d4206e1448ea76c3ebe69b3b97d8f8f65413bd5c76",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 600297,
confirmations: 76
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data[0], "txid")
assert.property(data[0], "vout")
assert.property(data[0], "amount")
assert.property(data[0], "satoshis")
assert.property(data[0], "height")
assert.property(data[0], "confirmations")
assert.property(data[0], "utxoType")
assert.property(data[0], "transactionType")
assert.property(data[0], "tokenId")
assert.property(data[0], "tokenTicker")
assert.property(data[0], "tokenName")
assert.property(data[0], "tokenDocumentUrl")
assert.property(data[0], "tokenDocumentHash")
assert.property(data[0], "decimals")
assert.property(data[0], "mintBatonVout")
assert.property(data[0], "tokenQty")
assert.property(data[0], "isValid")
assert.equal(data[0].isValid, true)
})
it("should return details for a simple SEND SLP token utxo", async () => {
const utxos = [
{
txid:
"fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data[0], "txid")
assert.property(data[0], "vout")
assert.property(data[0], "amount")
assert.property(data[0], "satoshis")
assert.property(data[0], "height")
assert.property(data[0], "confirmations")
assert.property(data[0], "utxoType")
assert.property(data[0], "tokenId")
assert.property(data[0], "tokenTicker")
assert.property(data[0], "tokenName")
assert.property(data[0], "tokenDocumentUrl")
assert.property(data[0], "tokenDocumentHash")
assert.property(data[0], "decimals")
assert.property(data[0], "tokenQty")
assert.property(data[0], "isValid")
assert.equal(data[0].isValid, true)
})
it("should handle BCH and SLP utxos in the same TX", async () => {
const utxos = [
{
txid:
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
vout: 3,
value: "6816",
height: 606848,
confirmations: 13,
satoshis: 6816
},
{
txid:
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
vout: 2,
value: "546",
height: 606848,
confirmations: 13,
satoshis: 546
}
]
const result = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 2)
assert.equal(result[0].isValid, false)
assert.equal(result[1].isValid, true)
})
it("should handle problematic utxos", async () => {
const utxos = [
{
txid:
"0e3a217fc22612002031d317b4cecd9b692b66b52951a67b23c43041aefa3959",
vout: 0,
amount: 0.00018362,
satoshis: 18362,
height: 613483,
confirmations: 124
},
{
txid:
"67fd3c7c3a6eb0fea9ab311b91039545086220f7eeeefa367fa28e6e43009f19",
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 612075,
confirmations: 1532
}
]
const result = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 2)
assert.equal(result[0].isValid, false)
assert.equal(result[1].isValid, true)
})
it("should return false for BCH-only UTXOs", async () => {
const utxos = [
{
txid:
"a937f792c7c9eb23b4f344ce5c233d1ac0909217d0a504d71e6b1e4efb864a3b",
vout: 0,
amount: 0.00001,
satoshis: 1000,
confirmations: 0,
ts: 1578424704
},
{
txid:
"53fd141c2e999e080a5860887441a2c45e9cbe262027e2bd2ac998fc76e43c44",
vout: 0,
amount: 0.00001,
satoshis: 1000,
confirmations: 0,
ts: 1578424634
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data)
assert.equal(data[0].isValid, false)
assert.equal(data[1].isValid, false)
})
it("should handle a dust attack", async () => {
// it("#dustattack", async () => {
const utxos = [
{
height: 655965,
tx_hash:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
vout: 151,
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
hdIndex: 11
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
})
describe("#balancesForAddress", () => {
it(`should throw an error if input is not a string or array of strings`, async () => {
try {
const address = 1234
await bchjs.SLP.Utils.balancesForAddress(address)
assert.equal(true, false, "Uh oh. Code path should not end here.")
} catch (err) {
//console.log(`Error: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings`
)
}
})
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
const balances = await bchjs.SLP.Utils.balancesForAddress(
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9"
)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.hasAllKeys(balances[0], [
"tokenId",
"balanceString",
"balance",
"decimalCount",
"slpAddress"
])
})
it(`should fetch balances for multiple addresses`, async () => {
const addresses = [
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9",
"simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57"
]
const balances = await bchjs.SLP.Utils.balancesForAddress(addresses)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.isArray(balances[0])
assert.hasAllKeys(balances[0][0], [
"tokenId",
"balanceString",
"balance",
"decimalCount",
"slpAddress"
])
})
})
describe("#hydrateUtxos", () => {
it("should throw an error if input is not an array", async () => {
try {
const utxos = 1234
await bchjs.SLP.Utils.hydrateUtxos(utxos)
assert.equal(true, false, "Uh oh. Code path should not end here.")
} catch (err) {
// console.log(`Error: `, err)
assert.include(err.message, `Input must be an array.`)
}
})
it("should hydrate UTXOs", async () => {
const utxos = [
{
utxos: [
{
txid:
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
vout: 3,
value: "6816",
height: 606848,
confirmations: 13,
satoshis: 6816
},
{
txid:
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
vout: 2,
value: "546",
height: 606848,
confirmations: 13,
satoshis: 546
}
]
}
]
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 1)
assert.equal(result.slpUtxos[0].utxos.length, 2)
// Test the non-slp UTXO.
assert.property(result.slpUtxos[0].utxos[0], "txid")
assert.property(result.slpUtxos[0].utxos[0], "vout")
assert.property(result.slpUtxos[0].utxos[0], "value")
assert.property(result.slpUtxos[0].utxos[0], "height")
assert.property(result.slpUtxos[0].utxos[0], "confirmations")
assert.property(result.slpUtxos[0].utxos[0], "satoshis")
assert.property(result.slpUtxos[0].utxos[0], "isValid")
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
// Test the slp UTXO.
assert.property(result.slpUtxos[0].utxos[1], "txid")
assert.property(result.slpUtxos[0].utxos[1], "vout")
assert.property(result.slpUtxos[0].utxos[1], "value")
assert.property(result.slpUtxos[0].utxos[1], "height")
assert.property(result.slpUtxos[0].utxos[1], "confirmations")
assert.property(result.slpUtxos[0].utxos[1], "satoshis")
assert.property(result.slpUtxos[0].utxos[1], "isValid")
assert.equal(result.slpUtxos[0].utxos[1].isValid, true)
assert.property(result.slpUtxos[0].utxos[1], "transactionType")
assert.property(result.slpUtxos[0].utxos[1], "tokenId")
assert.property(result.slpUtxos[0].utxos[1], "tokenTicker")
assert.property(result.slpUtxos[0].utxos[1], "tokenName")
assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentUrl")
assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentHash")
assert.property(result.slpUtxos[0].utxos[1], "decimals")
assert.property(result.slpUtxos[0].utxos[1], "tokenType")
assert.property(result.slpUtxos[0].utxos[1], "tokenQty")
})
it("should process data directly from Electrumx", async () => {
const addrs = [
"bitcoincash:qqnt53cfw990u6y38xgezm0lfa8hknw0wueezcpagp",
"bitcoincash:qrh3qgtax6adegzc7zxm6m4sgrv9wq28yqnfn33ce5",
"bitcoincash:qqcaee5w4ws77n8s2gzy6fwtma6uxd7ctq8553e495"
]
const utxos = await bchjs.Electrumx.utxo(addrs)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 3)
assert.equal(result.slpUtxos[0].utxos.length, 1)
assert.equal(result.slpUtxos[1].utxos.length, 1)
assert.equal(result.slpUtxos[2].utxos.length, 2)
try {
// Test the expected values.
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
assert.equal(result.slpUtxos[1].utxos[0].isValid, true)
assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, "TROUT")
assert.equal(result.slpUtxos[2].utxos[0].isValid, false)
assert.equal(result.slpUtxos[2].utxos[1].isValid, true)
assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, "VALENTINE")
} catch (err) {
console.error(
'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"'
)
console.log("Error: ", err)
}
})
})
describe("#validateTxid2", () => {
it("should invalidate a known invalid TXID", async () => {
const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "txid")
assert.equal(result.txid, txid)
assert.property(result, "isValid")
assert.equal(result.isValid, false)
})
it("should validate a known valid TXID", async () => {
const txid =
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "txid")
assert.equal(result.txid, txid)
assert.property(result, "isValid")
assert.equal(result.isValid, true)
})
})
})
describe("#tokentype1", () => {
describe("#getHexOpReturn", () => {
it("should return OP_RETURN object ", async () => {
const tokenUtxos = [
{
tokenId:
"0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1",
decimals: 0,
tokenQty: 2
}
]
const sendQty = 1.5
const result = await bchjs.SLP.TokenType1.getHexOpReturn(
tokenUtxos,
sendQty
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "script")
assert.isString(result.script)
assert.property(result, "outputs")
assert.isNumber(result.outputs)
})
})
})
})
// Promise-based sleep function
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+109
View File
@@ -0,0 +1,109 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
*/
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS()
// Inspect utility used for debugging.
const util = require("util")
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe(`#util`, () => {
describe(`#validateAddress`, () => {
it(`should return false for testnet addr on mainnet`, async () => {
const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["isvalid"])
assert.equal(result.isvalid, false)
})
it(`should return false for bad address`, async () => {
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu`
const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ["isvalid"])
assert.equal(result.isvalid, false)
})
it(`should validate valid address`, async () => {
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
"isvalid",
"address",
"scriptPubKey",
//"ismine",
//"iswatchonly",
"isscript"
])
assert.equal(result.isvalid, true)
})
it(`should validate an array of addresses`, async () => {
const address = [
`bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`,
`bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
]
const result = await bchjs.Util.validateAddress(address)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"isvalid",
"address",
"scriptPubKey",
//"ismine",
//"iswatchonly",
"isscript"
])
})
it(`should throw an error for improper single input`, async () => {
try {
const address = 15432
await bchjs.Util.validateAddress(address)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const dataMock = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.Util.validateAddress(data)
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")
}
})
})
})
+8 -30
View File
@@ -18,6 +18,10 @@ util.inspect.defaultOptions = {
}
describe(`#Blockbook`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#Balance`, () => {
it(`should GET balance for a single address`, async () => {
const addr = "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
@@ -67,21 +71,6 @@ describe(`#Blockbook`, () => {
assert.isArray(result[0].txids)
})
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.Blockbook.balance(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 throw error on array size rate limit`, async () => {
try {
const addr = []
@@ -136,21 +125,6 @@ describe(`#Blockbook`, () => {
])
})
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.Blockbook.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 throw error on array size rate limit`, async () => {
try {
const addr = []
@@ -168,3 +142,7 @@ describe(`#Blockbook`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+8 -56
View File
@@ -21,6 +21,10 @@ util.inspect.defaultOptions = {
}
describe(`#blockchain`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#getBestBlockHash`, () => {
it(`should GET best block hash`, async () => {
const result = await bchjs.Blockchain.getBestBlockHash()
@@ -85,20 +89,6 @@ describe(`#blockchain`, () => {
])
})
it(`should throw an error for improper input`, async () => {
try {
const hash = 12345
await bchjs.Blockchain.getBlockHeader(hash)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input hash must be a string or array of strings`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const data = []
@@ -185,20 +175,6 @@ describe(`#blockchain`, () => {
assert.include(err.error, `Transaction not in mempool`)
}
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.getMempoolEntry(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
}
})
})
describe(`#getTxOutProof`, () => {
@@ -224,20 +200,6 @@ describe(`#blockchain`, () => {
assert.isArray(result)
assert.isString(result[0])
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.getTxOutProof(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
}
})
})
describe(`#verifyTxOutProof`, () => {
@@ -269,20 +231,6 @@ describe(`#blockchain`, () => {
)
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.verifyTxOutProof(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const data = []
@@ -329,3 +277,7 @@ describe(`#blockchain`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+8
View File
@@ -8,6 +8,10 @@ const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
describe(`#control`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#getNetworkInfo`, () => {
it("should get info on the full node", async () => {
const result = await bchjs.Control.getNetworkInfo()
@@ -17,3 +21,7 @@ describe(`#control`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+186 -1
View File
@@ -7,7 +7,13 @@ const bchjs = new BCHJS()
describe(`#ElectrumX`, () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
beforeEach(async () => {
sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
afterEach(() => sandbox.restore())
describe(`#utxo`, () => {
@@ -178,4 +184,183 @@ describe(`#ElectrumX`, () => {
}
})
})
describe(`#unconfirmed`, () => {
// These tests won't work because unconfirmed transactions are transient in nature.
/*
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
const addr = "bitcoincash:qqy6qwk3wpne95hhv8uzwr4cn8m7c06cqgchl77dnv"
const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "height")
assert.property(result.utxos[0], "tx_hash")
assert.property(result.utxos[0], "fee")
})
it(`should POST request for unconfirmed UTXOs (mempool) for an array of addresses`, async () => {
const addr = [
"bitcoincash:qqy6qwk3wpne95hhv8uzwr4cn8m7c06cqgchl77dnv",
"bitcoincash:qpyrtsl9msdu2klgfpcmn2v5r22w9rk24g8pal74ts"
]
const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos")
assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address")
assert.property(result.utxos[0].utxos[0], "height")
assert.property(result.utxos[0].utxos[0], "tx_hash")
assert.property(result.utxos[0].utxos[0], "fee")
})
*/
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf")
await bchjs.Electrumx.unconfirmed(addr)
// 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")
}
})
})
describe(`#blockHeader`, () => {
it(`should GET block headers for given height and count`, async () => {
const height = 42
const count = 2
const result = await bchjs.Electrumx.blockHeader(height, count)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "headers")
assert.isArray(result.headers)
assert.equal(result.headers.length, 2)
})
it(`should GET block headers for given height with default count = 1`, async () => {
const height = 42
const result = await bchjs.Electrumx.blockHeader(height)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "headers")
assert.isArray(result.headers)
assert.equal(result.headers.length, 1)
})
})
describe(`#txData`, () => {
it(`should GET details for a single transaction`, async () => {
const txid =
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "details")
assert.isObject(result.details)
assert.property(result.details, "blockhash")
assert.property(result.details, "hash")
assert.property(result.details, "hex")
assert.property(result.details, "vin")
assert.property(result.details, "vout")
assert.equal(result.details.hash, txid)
})
it(`should POST details for an array of transactions`, async () => {
const txids = [
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
]
const result = await bchjs.Electrumx.txData(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "transactions")
assert.isArray(result.transactions)
assert.property(result.transactions[0], "txid")
assert.property(result.transactions[0], "details")
assert.property(result.transactions[0].details, "blockhash")
assert.property(result.transactions[0].details, "hash")
assert.property(result.transactions[0].details, "hex")
assert.property(result.transactions[0].details, "vin")
assert.property(result.transactions[0].details, "vout")
})
it(`should throw error on array size rate limit`, async () => {
try {
const txids = []
for (let i = 0; i < 25; i++) {
txids.push(
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
)
}
await bchjs.Electrumx.txData(txids)
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")
}
})
})
describe(`#broadcast`, () => {
it(`should broadcast a single transaction`, async () => {
const txHex =
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
try {
await bchjs.Electrumx.broadcast(txHex)
} catch (err) {
assert.property(err, "success")
assert.equal(err.success, false)
assert.include(
err.error,
"the transaction was rejected by network rules"
)
}
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+8
View File
@@ -4,6 +4,10 @@ const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
describe("#Encryption", () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe("#getPubKey", () => {
it("should get a public key", async () => {
const addr = "bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq"
@@ -29,3 +33,7 @@ describe("#Encryption", () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+116 -15
View File
@@ -3,11 +3,17 @@ const assert = chai.assert
const sinon = require("sinon")
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
const bchjs = new BCHJS({ ninsightURL: "https://rest.bitcoin.com/v2" })
describe(`#Ninsight`, () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
beforeEach(async () => {
sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
afterEach(() => sandbox.restore())
describe(`#utxo`, () => {
@@ -17,21 +23,116 @@ describe(`#Ninsight`, () => {
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.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.utxos)
assert.isArray(result[0].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")
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))
}
+8 -36
View File
@@ -8,19 +8,11 @@ const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
describe(`#OpenBazaar`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#Balance`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.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 GET balance for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
@@ -45,18 +37,6 @@ describe(`#OpenBazaar`, () => {
})
describe(`#utxo`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.utxo(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 GET utxos for a single address`, async () => {
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
@@ -76,18 +56,6 @@ describe(`#OpenBazaar`, () => {
})
describe(`#tx`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.tx(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(err.message, `Input txid must be a string`)
}
})
it(`should GET transactions for a single address`, async () => {
const addr =
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7"
@@ -115,3 +83,7 @@ describe(`#OpenBazaar`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+37 -1
View File
@@ -1,8 +1,12 @@
const assert = require("assert")
const assert = require("chai").assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
describe("#price", () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe("#current", () => {
describe("#single currency", () => {
it("should get current price for single currency", async () => {
@@ -11,4 +15,36 @@ describe("#price", () => {
})
})
})
describe("#getUsd", () => {
it("should get the USD price of BCH", async () => {
const result = await bchjs.Price.getUsd()
// console.log(result)
assert.isNumber(result)
})
})
describe("#getBchaUsd", () => {
it("should get the USD price of BCHA", async () => {
const result = await bchjs.Price.getBchaUsd()
console.log(result)
assert.isNumber(result)
})
})
describe("#rates", () => {
it("should get the price of BCH in several currencies", async () => {
const result = await bchjs.Price.rates()
// console.log(result)
assert.property(result, "USD")
assert.property(result, "CAD")
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+10 -48
View File
@@ -19,6 +19,10 @@ util.inspect.defaultOptions = {
}
describe("#rawtransaction", () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe("#decodeRawTransaction", () => {
it("should decode tx for a single hex", async () => {
const hex =
@@ -63,21 +67,6 @@ describe("#rawtransaction", () => {
assert.isArray(result[0].vout)
})
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.decodeRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const data = []
@@ -232,22 +221,6 @@ describe("#rawtransaction", () => {
const result = await bchjs.RawTransactions.decodeScript(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
})
*/
/*
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.decodeRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
*/
})
@@ -271,7 +244,7 @@ describe("#rawtransaction", () => {
//console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
}
})
@@ -288,22 +261,7 @@ describe("#rawtransaction", () => {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
}
})
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.sendRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input hex must be a string or array of strings`
)
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
}
})
@@ -325,3 +283,7 @@ describe("#rawtransaction", () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+160 -18
View File
@@ -6,7 +6,7 @@ const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
let bchjs
// Inspect utility used for debugging.
const util = require("util")
@@ -24,7 +24,9 @@ describe(`#SLP`, () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
bchjs = new BCHJS()
})
describe("#util", () => {
@@ -437,25 +439,33 @@ describe(`#SLP`, () => {
assert.equal(data[0].isValid, false)
assert.equal(data[1].isValid, false)
})
it("should handle a dust attack", async () => {
// it("#dustattack", async () => {
const utxos = [
{
height: 655965,
tx_hash:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
vout: 151,
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
hdIndex: 11
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
})
describe("#balancesForAddress", () => {
it(`should throw an error if input is not a string or array of strings`, async () => {
try {
const address = 1234
await bchjs.SLP.Utils.balancesForAddress(address)
assert.equal(true, false, "Uh oh. Code path should not end here.")
} catch (err) {
//console.log(`Error: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings`
)
}
})
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
const balances = await bchjs.SLP.Utils.balancesForAddress(
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9"
@@ -492,6 +502,138 @@ describe(`#SLP`, () => {
])
})
})
describe("#hydrateUtxos", () => {
it("should hydrate UTXOs", async () => {
const utxos = [
{
utxos: [
{
txid:
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
vout: 3,
value: "6816",
height: 606848,
confirmations: 13,
satoshis: 6816
},
{
txid:
"d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56",
vout: 2,
value: "546",
height: 606848,
confirmations: 13,
satoshis: 546
}
]
}
]
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 1)
assert.equal(result.slpUtxos[0].utxos.length, 2)
// Test the non-slp UTXO.
assert.property(result.slpUtxos[0].utxos[0], "txid")
assert.property(result.slpUtxos[0].utxos[0], "vout")
assert.property(result.slpUtxos[0].utxos[0], "value")
assert.property(result.slpUtxos[0].utxos[0], "height")
assert.property(result.slpUtxos[0].utxos[0], "confirmations")
assert.property(result.slpUtxos[0].utxos[0], "satoshis")
assert.property(result.slpUtxos[0].utxos[0], "isValid")
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
// Test the slp UTXO.
assert.property(result.slpUtxos[0].utxos[1], "txid")
assert.property(result.slpUtxos[0].utxos[1], "vout")
assert.property(result.slpUtxos[0].utxos[1], "value")
assert.property(result.slpUtxos[0].utxos[1], "height")
assert.property(result.slpUtxos[0].utxos[1], "confirmations")
assert.property(result.slpUtxos[0].utxos[1], "satoshis")
assert.property(result.slpUtxos[0].utxos[1], "isValid")
assert.equal(result.slpUtxos[0].utxos[1].isValid, true)
assert.property(result.slpUtxos[0].utxos[1], "transactionType")
assert.property(result.slpUtxos[0].utxos[1], "tokenId")
assert.property(result.slpUtxos[0].utxos[1], "tokenTicker")
assert.property(result.slpUtxos[0].utxos[1], "tokenName")
assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentUrl")
assert.property(result.slpUtxos[0].utxos[1], "tokenDocumentHash")
assert.property(result.slpUtxos[0].utxos[1], "decimals")
assert.property(result.slpUtxos[0].utxos[1], "tokenType")
assert.property(result.slpUtxos[0].utxos[1], "tokenQty")
})
it("should process data directly from Electrumx", async () => {
const addrs = [
"bitcoincash:qqnt53cfw990u6y38xgezm0lfa8hknw0wueezcpagp",
"bitcoincash:qrh3qgtax6adegzc7zxm6m4sgrv9wq28yqnfn33ce5",
"bitcoincash:qqcaee5w4ws77n8s2gzy6fwtma6uxd7ctq8553e495"
]
const utxos = await bchjs.Electrumx.utxo(addrs)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 3)
assert.equal(result.slpUtxos[0].utxos.length, 1)
assert.equal(result.slpUtxos[1].utxos.length, 1)
assert.equal(result.slpUtxos[2].utxos.length, 2)
try {
// Test the expected values.
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
assert.equal(result.slpUtxos[1].utxos[0].isValid, true)
assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, "TROUT")
assert.equal(result.slpUtxos[2].utxos[0].isValid, false)
assert.equal(result.slpUtxos[2].utxos[1].isValid, true)
assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, "VALENTINE")
} catch (err) {
console.error(
'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"'
)
console.log("Error: ", err)
}
})
})
describe("#validateTxid2", () => {
it("should invalidate a known invalid TXID", async () => {
const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "txid")
assert.equal(result.txid, txid)
assert.property(result, "isValid")
assert.equal(result.isValid, false)
})
it("should validate a known valid TXID", async () => {
const txid =
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "txid")
assert.equal(result.txid, txid)
assert.property(result, "isValid")
assert.equal(result.isValid, true)
})
})
})
describe("#tokentype1", () => {
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
export RESTURL=https://bchn.fullstack.cash/v3/
#export RESTURL=http://localhost:3000/v3/
#export IS_USING_FREE_TIER=true
cd test/integration/bchn/
mocha --timeout 30000 blockchain.js control.js encryption.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
export RESTURL=https://free-main.fullstack.cash/v3/
#export RESTURL=http://localhost:3000/v3/
cd test/integration/
mocha --timeout 30000 blockchain.js control.js electrumx.js ninsight.js openbazaar.js price.js rawtransaction.js slp.js util.js
+10 -2
View File
@@ -8,7 +8,7 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://tapi.fullstack.cash/v3/`
: `https://testnet3.fullstack.cash/v3/`
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
@@ -26,6 +26,10 @@ util.inspect.defaultOptions = {
}
describe(`#Blockbook`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#Balance`, () => {
it(`should GET balance for a single address`, async () => {
const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
@@ -165,7 +169,7 @@ describe(`#Blockbook`, () => {
for (let i = 0; i < 25; i++)
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
const result = await bchjs.Blockbook.utxo(addr)
await bchjs.Blockbook.utxo(addr)
//console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!")
@@ -176,3 +180,7 @@ describe(`#Blockbook`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+29 -77
View File
@@ -12,7 +12,7 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://tapi.fullstack.cash/v3/`
: "https://testnet3.fullstack.cash/v3/"
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
@@ -27,11 +27,15 @@ util.inspect.defaultOptions = {
depth: 3
}
describe(`#blockchain`, () => {
describe(`#getBestBlockHash`, () => {
it(`should GET best block hash`, async () => {
describe("#blockchain", () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe("#getBestBlockHash", () => {
it("should GET best block hash", async () => {
const result = await bchjs.Blockchain.getBestBlockHash()
//console.log(`result: ${util.inspect(result)}`)
// console.log(`result: ${util.inspect(result)}`)
assert.isString(result)
assert.equal(result.length, 64, "Specific hash length")
@@ -39,7 +43,7 @@ describe(`#blockchain`, () => {
})
describe("#getBlockHeader", () => {
it(`should GET block header for a single hash`, async () => {
it("should GET block header for a single hash", async () => {
const hash =
"000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf"
@@ -64,7 +68,7 @@ describe(`#blockchain`, () => {
])
})
it(`should GET block headers for an array of hashes`, async () => {
it("should GET block headers for an array of hashes", async () => {
const hash = [
"000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf",
"00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2"
@@ -92,21 +96,7 @@ describe(`#blockchain`, () => {
])
})
it(`should throw an error for improper input`, async () => {
try {
const hash = 12345
await bchjs.Blockchain.getBlockHeader(hash)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input hash must be a string or array of strings`
)
}
})
it(`should throw error on array size rate limit`, async () => {
it("should throw error on array size rate limit", async () => {
try {
const data = []
for (let i = 0; i < 25; i++) {
@@ -115,7 +105,7 @@ describe(`#blockchain`, () => {
)
}
const result = await bchjs.Blockchain.getBlockHeader(data)
await bchjs.Blockchain.getBlockHeader(data)
// console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, "Unexpected result!")
@@ -178,7 +168,7 @@ describe(`#blockchain`, () => {
})
*/
it(`should throw an error if txid is not in mempool`, async () => {
it("should throw an error if txid is not in mempool", async () => {
try {
const txid =
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
@@ -187,39 +177,25 @@ describe(`#blockchain`, () => {
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: ${util.inspect(err)}`)
// console.log(`err: ${util.inspect(err)}`)
assert.hasAnyKeys(err, ["error"])
assert.include(err.error, `Transaction not in mempool`)
}
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.getMempoolEntry(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
assert.include(err.error, "Transaction not in mempool")
}
})
})
describe(`#getTxOutProof`, () => {
it(`should get single tx out proof`, async () => {
describe("#getTxOutProof", () => {
it("should get single tx out proof", async () => {
const txid =
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04"
const result = await bchjs.Blockchain.getTxOutProof(txid)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result)
})
it(`should get an array of tx out proofs`, async () => {
it("should get an array of tx out proofs", async () => {
const txid = [
"1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04",
"fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752"
@@ -231,27 +207,13 @@ describe(`#blockchain`, () => {
assert.isArray(result)
assert.isString(result[0])
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.getTxOutProof(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
}
})
})
describe(`#verifyTxOutProof`, () => {
describe("#verifyTxOutProof", () => {
const mockTxOutProof =
"00000020ac86ce8f2bda235c0dc135d18f6a777c44b121e8f41db8a51ca65000000000000cd4de3c49337712f3a1092c47c3bf73ec2f9b1cfd289ee991ede0b6eab4df229d5b8c5dffff001d82ce005b0700000003ec55d9142eac2c1d2229e5af24898b2590c111b62e2787fa1998d3d713fd432fd557124ed758fa156a6cdc6d317d5575aec2b86dfb159c62ecb4743f28bef1cb52e7a32fbec31e367ec152fdb952f75ee75bd8575f97b394093dbb0e6c694ffc0135"
it(`should verify a single proof`, async () => {
it("should verify a single proof", async () => {
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -263,10 +225,10 @@ describe(`#blockchain`, () => {
)
})
it(`should verify an array of proofs`, async () => {
it("should verify an array of proofs", async () => {
const proofs = [mockTxOutProof, mockTxOutProof]
const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
@@ -276,21 +238,7 @@ describe(`#blockchain`, () => {
)
})
it(`should throw an error for improper single input`, async () => {
try {
const txid = 12345
await bchjs.Blockchain.verifyTxOutProof(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
assert.include(
err.message,
`Input must be a string or array of strings`
)
}
})
it(`should throw error on array size rate limit`, async () => {
it("should throw error on array size rate limit", async () => {
try {
const data = []
for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
@@ -306,3 +254,7 @@ describe(`#blockchain`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+9 -1
View File
@@ -7,12 +7,16 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://tapi.fullstack.cash/v3/`
: `https://testnet3.fullstack.cash/v3/`
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)
})
describe(`#getNetworkInfo`, () => {
it("should get info on the full node", async () => {
const result = await bchjs.Control.getNetworkInfo()
@@ -22,3 +26,7 @@ describe(`#control`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+187 -2
View File
@@ -4,14 +4,20 @@ const sinon = require("sinon")
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://tapi.fullstack.cash/v3/`
: `https://testnet3.fullstack.cash/v3/`
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
describe(`#ElectrumX`, () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
beforeEach(async () => {
sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
afterEach(() => sandbox.restore())
describe(`#utxo`, () => {
@@ -182,4 +188,183 @@ describe(`#ElectrumX`, () => {
}
})
})
describe(`#unconfirmed`, () => {
// These tests won't work because unconfirmed transactions are transient in nature.
/*
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
const addr = "bchtest:qp25k20dgcljrz4hkdz43partam3j5httyprjp23qd"
const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "height")
assert.property(result.utxos[0], "tx_hash")
assert.property(result.utxos[0], "fee")
})
it(`should POST unconfirmed UTXO details for an array of addresses`, async () => {
const addr = [
"bchtest:qp25k20dgcljrz4hkdz43partam3j5httyprjp23qd",
"bchtest:qpkl3xylrjx4jup6m66e7zg7whlaucsxeudxeqawdj"
]
const result = await bchjs.Electrumx.unconfirmed(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos")
assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address")
assert.property(result.utxos[0].utxos[0], "height")
assert.property(result.utxos[0].utxos[0], "tx_hash")
assert.property(result.utxos[0].utxos[0], "fee")
})
*/
it(`should throw error on array size rate limit`, async () => {
try {
const addr = []
for (let i = 0; i < 25; i++)
addr.push("bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2")
await bchjs.Electrumx.unconfirmed(addr)
//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")
}
})
})
describe(`#blockHeader`, () => {
it(`should GET block headers for given height and count`, async () => {
const height = 42
const count = 2
const result = await bchjs.Electrumx.blockHeader(height, count)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "headers")
assert.isArray(result.headers)
assert.equal(result.headers.length, 2)
})
it(`should GET block headers for given height with default count = 1`, async () => {
const height = 42
const result = await bchjs.Electrumx.blockHeader(height)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "headers")
assert.isArray(result.headers)
assert.equal(result.headers.length, 1)
})
})
describe(`#txData`, () => {
it(`should GET details for a single transaction`, async () => {
const txid =
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee"
const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "details")
assert.isObject(result.details)
assert.property(result.details, "blockhash")
assert.property(result.details, "hash")
assert.property(result.details, "hex")
assert.property(result.details, "vin")
assert.property(result.details, "vout")
assert.equal(result.details.hash, txid)
})
it(`should POST details for an array of transactions`, async () => {
const txids = [
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee",
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee"
]
const result = await bchjs.Electrumx.txData(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "transactions")
assert.isArray(result.transactions)
assert.property(result.transactions[0], "txid")
assert.property(result.transactions[0], "details")
assert.property(result.transactions[0].details, "blockhash")
assert.property(result.transactions[0].details, "hash")
assert.property(result.transactions[0].details, "hex")
assert.property(result.transactions[0].details, "vin")
assert.property(result.transactions[0].details, "vout")
})
it(`should throw error on array size rate limit`, async () => {
try {
const txids = []
for (let i = 0; i < 25; i++) {
txids.push(
"76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee"
)
}
await bchjs.Electrumx.txData(txids)
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")
}
})
})
describe(`#broadcast`, () => {
it(`should broadcast a single transaction`, async () => {
const txHex =
"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"
try {
await bchjs.Electrumx.broadcast(txHex)
} catch (err) {
assert.property(err, "success")
assert.equal(err.success, false)
assert.include(
err.error,
"the transaction was rejected by network rules"
)
}
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+146
View File
@@ -0,0 +1,146 @@
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))
}
-124
View File
@@ -1,124 +0,0 @@
/*
tests for OpenBazaar library.
*/
// Force testnet call to OB1 servers.
process.env.NETWORK = "testnet"
const chai = require("chai")
const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://tapi.fullstack.cash/v3/`
const BCHJS = require("../../../src/bch-js")
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
describe(`#OpenBazaar`, () => {
describe(`#Balance`, () => {
// it(`should GET balance for a single address`, async () => {
// const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
//
// const result = await bchjs.OpenBazaar.balance(addr)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.hasAllKeys(result, [
// "page",
// "totalPages",
// "itemsOnPage",
// "addrStr",
// "balance",
// "totalReceived",
// "totalSent",
// "unconfirmedBalance",
// "unconfirmedTxApperances",
// "txApperances",
// "transactions"
// ])
// assert.isArray(result.transactions)
// })
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.balance(addr)
// assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(err.message, `Input address must be a string`)
}
})
})
describe(`#utxo`, () => {
// it(`should GET utxos for a single address`, async () => {
// const addr = "bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2"
//
// const result = await bchjs.OpenBazaar.utxo(addr)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isArray(result)
// assert.hasAllKeys(result[0], [
// "txid",
// "vout",
// "amount",
// "height",
// "confirmations",
// "satoshis"
// ])
// })
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.utxo(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(err.message, `Input address must be a string`)
}
})
})
describe(`#tx`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.OpenBazaar.tx(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(err.message, `Input txid must be a string`)
}
})
// it(`should GET transactions for a single address`, async () => {
// const addr =
// "ed4692f50a4553527dd26cd8674ca06a0ab2d366f3135ca3668310467ead3cbf"
//
// const result = await bchjs.OpenBazaar.tx(addr)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.hasAnyKeys(result, [
// "txid",
// "version",
// "vin",
// "vout",
// "blockhash",
// "blockheight",
// "confirmations",
// "blocktime",
// "valueOut",
// "valueIn",
// "fees",
// "hex"
// ])
// assert.isArray(result.vin)
// assert.isArray(result.vout)
// })
})
})
+11 -49
View File
@@ -10,7 +10,7 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://tapi.fullstack.cash/v3/`
: `https://testnet3.fullstack.cash/v3/`
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
@@ -26,6 +26,10 @@ util.inspect.defaultOptions = {
}
describe("#rawtransaction", () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe("#decodeRawTransaction", () => {
it("should decode tx for a single hex", async () => {
const hex =
@@ -70,21 +74,6 @@ describe("#rawtransaction", () => {
assert.isArray(result[0].vout)
})
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.decodeRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const data = []
@@ -239,22 +228,6 @@ describe("#rawtransaction", () => {
const result = await bchjs.RawTransactions.decodeScript(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
})
*/
/*
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.decodeRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
*/
})
@@ -278,7 +251,7 @@ describe("#rawtransaction", () => {
//console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
}
})
@@ -295,22 +268,7 @@ describe("#rawtransaction", () => {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ["error"])
assert.include(err.error, "Missing inputs")
}
})
it(`should throw an error for improper single input`, async () => {
try {
const addr = 12345
await bchjs.RawTransactions.sendRawTransaction(addr)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input hex must be a string or array of strings`
)
assert.include(err.error, "bad-txns-inputs-missingorspent (code 16)")
}
})
@@ -332,3 +290,7 @@ describe("#rawtransaction", () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+57 -19
View File
@@ -7,7 +7,7 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://tapi.fullstack.cash/v3/`
: `https://testnet3.fullstack.cash/v3/`
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
@@ -25,7 +25,7 @@ util.inspect.defaultOptions = {
describe(`#SLP`, () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe("#util", () => {
@@ -73,23 +73,7 @@ describe(`#SLP`, () => {
})
describe("#balancesForAddress", () => {
it(`should throw an error if input is not a string or array of strings`, async () => {
try {
const address = 1234
await bchjs.SLP.Utils.balancesForAddress(address)
assert.equal(true, false, "Uh oh. Code path should not end here.")
} catch (err) {
//console.log(`Error: `, err)
assert.include(
err.message,
`Input address must be a string or array of strings`
)
}
})
it(`should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
it(`should fetch all balances for address: slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9`, async () => {
// Mock the call to rest.bitcoin.com
if (process.env.TEST === "unit") {
sandbox
@@ -139,6 +123,60 @@ describe(`#SLP`, () => {
])
})
})
describe("#tokenUtxoDetails", () => {
it("should hydrate UTXOs", async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
)
const utxos = await bchjs.Electrumx.utxo([bchAddr])
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(
utxos.utxos[0].utxos
)
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
assert.isArray(utxoInfo)
// first UTXO should be a PSF test token.
assert.equal(utxoInfo[0].isValid, true)
})
})
describe("#hydrateUtxos", () => {
// This test will error out if the LOCAL_RESTURL settings is not set properly
// in bch-api.
it("should hydrate UTXOs", async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress(
"slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9"
)
const utxos = await bchjs.Electrumx.utxo([bchAddr])
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
assert.isArray(utxoInfo.slpUtxos[0].utxos)
// first UTXO should be a PSF test token.
assert.equal(utxoInfo.slpUtxos[0].utxos[0].isValid, true)
})
})
describe("#validateTxid2", () => {
it("should validate a token txid", async () => {
const txid =
"ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1"
const validated = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(validated)
assert.equal(validated[0].valid, true)
})
})
})
// Promise-based sleep function
+3 -2
View File
@@ -2,7 +2,8 @@
export RESTURL=https://free-test.fullstack.cash/v3/
export NETWORK=testnet
#export RESTURL=http://localhost:3000/v3/
export IS_USING_FREE_TIER=true
cd test/integration/testnet/
mocha --timeout 30000 blockchain.js control.js electrumx.js openbazaar.js rawtransaction.js slp.js util.js
mocha --timeout 30000 blockchain.js control.js electrumx.js rawtransaction.js slp.js util.js
#mocha --timeout 30000 blockchain.js
+9 -16
View File
@@ -8,7 +8,7 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: `https://tapi.fullstack.cash/v3/`
: `https://testnet3.fullstack.cash/v3/`
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require("../../../src/bch-js")
@@ -24,6 +24,10 @@ util.inspect.defaultOptions = {
}
describe(`#util`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#validateAddress`, () => {
it(`should return false for testnet addr on mainnet`, async () => {
const address = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peu`
@@ -82,21 +86,6 @@ describe(`#util`, () => {
])
})
it(`should throw an error for improper single input`, async () => {
try {
const address = 15432
await bchjs.Util.validateAddress(address)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const dataMock = `bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh`
@@ -114,3 +103,7 @@ describe(`#util`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+8 -15
View File
@@ -17,6 +17,10 @@ util.inspect.defaultOptions = {
}
describe(`#util`, () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
describe(`#validateAddress`, () => {
it(`should return false for testnet addr on mainnet`, async () => {
const address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`
@@ -75,21 +79,6 @@ describe(`#util`, () => {
])
})
it(`should throw an error for improper single input`, async () => {
try {
const address = 15432
await bchjs.Util.validateAddress(address)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(
err.message,
`Input must be a string or array of strings.`
)
}
})
it(`should throw error on array size rate limit`, async () => {
try {
const dataMock = `bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z`
@@ -107,3 +96,7 @@ describe(`#util`, () => {
})
})
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
-25
View File
@@ -1,25 +0,0 @@
const chai = require("chai")
const assert = chai.assert
const BCHJS = require("../../src/bch-js")
// Used for debugging and iterrogating JS objects.
const util = require("util")
util.inspect.defaultOptions = { depth: 1 }
describe(`#BitboxShim`, () => {
it("should create the shim library", () => {
const BitboxShim = BCHJS.BitboxShim()
const bitbox = new BitboxShim()
//console.log(`bitbox.Address: ${util.inspect(bitbox.Address)}`)
assert.hasAnyKeys(bitbox.Address, [
// "details",
// "utxo",
// "unconfirmed",
// "transactions",
// "toLegacyAddress"
"restURL"
])
})
})
+222
View File
@@ -196,4 +196,226 @@ describe(`#ElectrumX`, () => {
assert.property(result.transactions[0].transactions[0], "tx_hash")
})
})
describe(`#unconfirmed`, () => {
it(`should throw an error for improper input`, async () => {
try {
const addr = 12345
await bchjs.Electrumx.unconfirmed(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 unconfirmed utxos for a single address`, async () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.unconfirmed })
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "height")
assert.property(result.utxos[0], "tx_hash")
assert.property(result.utxos[0], "fee")
})
it(`should POST unconfirmed utxo details for an array of addresses`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.unconfirmedArray })
const addr = [
"bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf",
"bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
]
const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos")
assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address")
assert.property(result.utxos[0].utxos[0], "height")
assert.property(result.utxos[0].utxos[0], "tx_hash")
assert.property(result.utxos[0].utxos[0], "fee")
})
})
describe(`#blockHeader`, () => {
it(`should throw an error for improper height input`, async () => {
try {
// Mock network calls.
sandbox.stub(axios, "get").rejects({
response: {
data: {
success: false,
error: "height must be a positive number"
}
}
})
const height = -10
await bchjs.Electrumx.blockHeader(height)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, `height must be a positive number`)
}
})
it(`should throw an error for improper count input`, async () => {
try {
// Mock network calls.
sandbox.stub(axios, "get").rejects({
response: {
data: {
success: false,
error: "count must be a positive number"
}
}
})
const height = 42
const count = -10
await bchjs.Electrumx.blockHeader(height, count)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, `count must be a positive number`)
}
})
it(`should GET block headers for a given height`, async () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.blockHeaders })
const height = 42
const result = await bchjs.Electrumx.blockHeader(height)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 2)
})
})
describe(`#txData`, () => {
it(`should throw an error for improper input`, async () => {
try {
const txid = 12345
await bchjs.Electrumx.txData(txid)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
// console.log(`err: `, err)
assert.include(
err.message,
`Input txId must be a string or array of strings`
)
}
})
it(`should GET details data for a single transaction`, async () => {
// Stub the network call.
sandbox.stub(axios, "get").resolves({ data: mockData.details })
const txid =
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "details")
assert.isObject(result.details)
assert.property(result.details, "blockhash")
assert.property(result.details, "hash")
assert.property(result.details, "hex")
assert.property(result.details, "vin")
assert.property(result.details, "vout")
assert.equal(result.details.hash, txid)
})
it(`should POST details for an array of transactions`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.detailsArray })
const txids = [
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251",
"4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251"
]
const result = await bchjs.Electrumx.txData(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "transactions")
assert.isArray(result.transactions)
assert.property(result.transactions[0], "txid")
assert.property(result.transactions[0], "details")
assert.property(result.transactions[0].details, "blockhash")
assert.property(result.transactions[0].details, "hash")
assert.property(result.transactions[0].details, "hex")
assert.property(result.transactions[0].details, "vin")
assert.property(result.transactions[0].details, "vout")
assert.equal(result.transactions.length, 2, "2 outputs for 2 inputs")
})
})
describe(`#broadcast`, () => {
it(`should throw an error for improper input`, async () => {
try {
const txHex = 12345
await bchjs.Electrumx.broadcast(txHex)
assert.equal(true, false, "Unexpected result!")
} catch (err) {
// console.log(`err: `, err)
assert.include(err.message, `Input txHex must be a string.`)
}
})
it(`should broadcast a single transaction`, async () => {
// Stub the network call.
sandbox.stub(axios, "post").resolves({ data: mockData.broadcast })
const tx = mockData.details
const txid = tx.details.txid
const result = await bchjs.Electrumx.broadcast(tx.details.hex)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "txid")
assert.equal(result.txid, txid)
})
})
})
+143 -1
View File
@@ -119,11 +119,153 @@ const transactions = {
]
}
const unconfirmed = {
success: true,
utxos: [
{
height: 602405,
tx_hash:
"2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7",
fee: 34100
}
]
}
const unconfirmedArray = {
success: true,
utxos: [
{
utxos: [
{
height: 604392,
tx_hash:
"7774e449c5a3065144cefbc4c0c21e6b69c987f095856778ef9f45ddd8ae1a41",
fee: 34210
},
{
height: 630834,
tx_hash:
"4fe60a51e0d8f5134bfd8e5f872d6e502d7f01b28a6afebb27f4438a4f638d53",
value: 3000
}
],
address: "bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"
},
{
utxos: [],
address: "bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v"
}
]
}
const blockHeaders = [
'010000008b52bbd72c2f49569059f559c1b1794de5192e4f7d6d2b03c7482bad0000000083e4f8a9d502ed0c419075c1abb5d56f878a2e9079e5612bfb76a2dc37d9c42741dd6849ffff001d2b909dd6',
'01000000f528fac1bcb685d0cd6c792320af0300a5ce15d687c7149548904e31000000004e8985a786d864f21e9cbb7cbdf4bc9265fe681b7a0893ac55a8e919ce035c2f85de6849ffff001d385ccb7c'
]
const txDetails = {
blockhash: '0000000000000000002aaf94953da3b487317508ebd1003a1d75d6d6ec2e75cc',
blocktime: 1578327094,
confirmations: 31861,
hash: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
hex: '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000',
locktime: 0,
size: 392,
time: 1578327094,
txid: '4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251',
version: 2,
vin: [
{
scriptSig: {
asm: 'dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e[ALL|FORKID] 020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309',
hex: '41dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309'
},
sequence: 4294967295,
txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165',
vout: 1
},
{
scriptSig: {
asm: '347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f77[ALL|FORKID] 028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954',
hex: '41347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954'
},
sequence: 4294967295,
txid: '6796672c8f0342770e3d4ac2a3b0e4494daeb7af7997f3518a0c8402f43ed165',
vout: 0
}
],
vout: [
{
n: 0,
scriptPubKey: {
addresses: ['bitcoincash: pqvfecpwxvj53ayqfwkxtjaxsgpvnklcyg8xewk9hl'],
asm: 'OP_HASH160 189ce02e332548f4804bac65cba68202c9dbf822 OP_EQUAL',
hex: 'a914189ce02e332548f4804bac65cba68202c9dbf82287',
reqSigs: 1,
type: 'scripthash'
},
value: 0.0562057
},
{
n: 1,
scriptPubKey: {
addresses: ['bitcoincash: qq59hv6s3qdjrtyfwfxxldkuj9xsjmx48vrz882knz'],
asm: 'OP_DUP OP_HASH160 285bb350881b21ac89724c6fb6dc914d096cd53b OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914285bb350881b21ac89724c6fb6dc914d096cd53b88ac',
reqSigs: 1,
type: 'pubkeyhash'
},
value: 0.00589197
},
{
n: 2,
scriptPubKey: {
addresses: ['bitcoincash: qpzlruwy4xu5rxjs3z37nsj29y7h59gwvsu4ddp0u4'],
asm: 'OP_DUP OP_HASH160 45f1f1c4a9b9419a5088a3e9c24a293d7a150e64 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac',
reqSigs: 1,
type: 'pubkeyhash'
},
value: 0.03272697
}
]
}
const details = {
success: true,
details: txDetails
}
const detailsArray = {
success: true,
transactions: [
{
txid: txDetails.txid,
details: txDetails
},
{
txid: txDetails.txid,
details: txDetails
}
]
}
const broadcast = {
success: true,
txid: txDetails.txid
}
module.exports = {
utxo,
utxos,
balance,
balances,
transaction,
transactions
transactions,
unconfirmed,
unconfirmedArray,
blockHeaders,
details,
detailsArray,
broadcast
}
+146 -2
View File
@@ -21,6 +21,150 @@ const utxo = {
"OP_DUP OP_HASH160 2fe2c4c5ef359bb2fe1a849f891cecffbcfb4f77 OP_EQUALVERIFY OP_CHECKSIG"
}
module.exports = {
utxo
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
}
+214
View File
@@ -0,0 +1,214 @@
/*
Mocks for price.
*/
const mockRates = {
AED: "915.049218",
AFN: "19144.48874646",
ALGO: "826.6633482661356600405",
ALL: "26221.96098759",
AMD: "119977.82663822",
ANG: "446.841810455",
AOA: "162269.774275",
ARS: "19322.704621",
ATOM: "45.5920571010248851205",
AUD: "353.78913716",
AWG: "448.407",
AZN: "424.1182875",
BAL: "18.9361216125975755781",
BAM: "413.75759465",
BAND: "40.84187228461349099275",
BAT: "1169.75444148879972951",
BBD: "498.23",
BCH: "1.0",
BDT: "21107.92000745",
BGN: "413.93197515",
BHD: "93.93578597",
BIF: "481540.76228735",
BMD: "249.115",
BND: "337.84677362",
BOB: "1718.851399565",
BRL: "1396.737982",
BSD: "249.115",
BSV: "1.555324933590611026515",
BTC: "0.021265",
BTN: "18249.513962505",
BWP: "2848.17713393",
BYN: "637.963336685",
BYR: "6379633.36685000049823",
BZD: "501.752236985",
CAD: "328.80838319",
CDF: "488848.679106575",
CGLD: "121.0412516398620018305",
CHF: "226.8989243",
CLF: "7.110489445",
CLP: "196202.978234955",
CNH: "1664.25510705",
CNY: "1666.429881",
COMP: "2.4706436576415750709",
COP: "958730.858397465",
CRC: "150173.845981",
CUC: "248.997666835",
CVE: "23541.3675",
CZK: "5770.848621",
DAI: "247.0463573269230002455",
DASH: "3.327367317363110236645",
DJF: "44315.31795969",
DKK: "1575.7370741",
DOP: "14529.76988648",
DZD: "32107.28592277",
EEK: "3640.489633465",
EGP: "3911.404438",
EOS: "96.6873665825732601419",
ERN: "3736.727740265",
ETB: "9302.454572035",
ETC: "44.91795888928957612395",
ETH: "0.6574429621419051422355",
EUR: "211.595",
FJD: "533.11855575",
FKP: "192.55642863",
GBP: "192.87",
GEL: "804.64145",
GGP: "192.55642863",
GHS: "1448.776859925",
GIP: "192.55642863",
GMD: "12891.70125",
GNF: "2438630.43574976",
GTQ: "1936.689014855",
GYD: "52047.88891278",
HKD: "1930.67861725",
HNL: "6126.43736492",
HRK: "1605.40119007",
HTG: "15682.48428085",
HUF: "77261.419177275",
IDR: "3666848.2425",
ILS: "843.29662455",
IMP: "192.55642863",
INR: "18275.43761675",
IQD: "297176.65680577",
ISK: "34639.44075",
JEP: "192.55642863",
JMD: "36261.061070375",
JOD: "176.622535",
JPY: "26304.9247525",
KES: "27093.7474",
KGS: "20240.23303148",
KHR: "1021526.66327067",
KMF: "104678.164103975",
KNC: "275.5392102643512742545",
KRW: "284115.6575",
KWD: "76.196555935",
KYD: "207.44852333",
KZT: "106578.169689505",
LAK: "2300372.70137523",
LBP: "376426.60931701",
LINK: "22.8704181013371177476",
LKR: "45903.840861165",
LRC: "1428.4116972477063306",
LRD: "48689.521020355",
LSL: "4093.68786226",
LTC: "5.182877353583689269445",
LTL: "803.357262175",
LVL: "163.484459015",
LYD: "340.470203685",
MAD: "2290.86403115",
MDL: "4231.74490411",
MGA: "978918.42727237",
MKD: "13034.71219274",
MKR: "0.43727565410331105846",
MMK: "321367.16219401",
MNT: "706980.17312004",
MOP: "1987.157222705",
MRO: "88934.055",
MTL: "170.32939187",
MUR: "9942.18014823",
MVR: "3836.371",
MWK: "187533.350248305",
MXN: "5285.574344805",
MYR: "1033.2044625",
MZN: "18168.45667469",
NAD: "4120.3621",
NGN: "95465.8503",
NIO: "8675.07065117",
NMR: "8.6886689628111785348",
NOK: "2328.95371465",
NPR: "29198.52701022",
NZD: "378.934057915",
OMG: "74.27621574882972595255",
OMR: "95.913509955",
OXT: "1023.479868529169993565",
PAB: "249.115",
PEN: "892.41413087",
PGK: "870.976539545",
PHP: "12100.979598855",
PKR: "40413.26181118",
PLN: "968.71705891",
PYG: "1750259.579863715",
QAR: "907.08999375",
REN: "758.802924154736515935",
REP: "18.2168190127970744169",
REPV2: "18.225711080673967324",
RON: "1032.33256",
RSD: "24895.307525",
RUB: "19351.4774035",
RWF: "243426.179717085",
SAR: "934.309544225",
SBD: "2013.0584566",
SCR: "4561.388071665",
SEK: "2201.10291435",
SGD: "338.356712025",
SHP: "192.55642863",
SLL: "2482430.971263275",
SOS: "144000.839307095",
SRD: "3525.97371",
SSP: "32449.7199",
STD: "5232524.31108792",
SVC: "2178.147216215",
SZL: "4091.741526765",
THB: "7785.4665375",
TJS: "2568.96954016",
TMT: "871.9025",
TND: "686.00043125",
TOP: "578.0165522",
TRY: "1963.587954325",
TTD: "1689.460313635",
TWD: "7158.9423125",
TZS: "577510.41827928",
UAH: "7061.44019619",
UGX: "931508.2111739",
UMA: "30.09543944427665349095",
UNI: "79.18593747516647884725",
USD: "249.115",
USDC: "249.115",
UYU: "10688.478117885",
UZS: "2582693.606121005",
VEF: "61901998.996866715",
VES: "112695476.713406465",
VND: "5773814.12282902",
VUV: "28486.73470656",
WST: "653.35042289",
XAF: "138884.3079243",
XAG: "10.22661168355",
XAU: "0.1312935696",
XCD: "673.24574325",
XDR: "176.50246157",
XLM: "2936.20532162536435083",
XOF: "138884.3079243",
XPD: "0.1061030608",
XPF: "25265.84267069",
XPT: "0.2910211253",
XRP: "1014.931757995518373565",
XTZ: "113.8135051169590628124",
YER: "62365.930534515",
YFI: "0.0178703370267443543872",
ZAR: "4122.31765275",
ZEC: "3.87305659203980154283",
ZMK: "1308619.842149325",
ZMW: "5027.95231667",
ZRX: "644.844402797695193656",
ZWL: "80215.03"
}
module.exports = {
mockRates
}
+257 -9
View File
@@ -4,7 +4,6 @@
const assert = require("chai").assert
const sinon = require("sinon")
const BCHJS = require("../../src/bch-js")
let bchjs
@@ -27,12 +26,12 @@ describe(`#IPFS`, () => {
})
})
describe("#createFileModel", () => {
describe("#createFileModelServer", () => {
it("should throw an error if file does not exist", async () => {
try {
const path = "/non-existant-file"
await bchjs.IPFS.createFileModel(path)
await bchjs.IPFS.createFileModelServer(path)
assert.equal(true, false, "Unexpected result")
} catch (err) {
@@ -48,7 +47,7 @@ describe(`#IPFS`, () => {
.stub(bchjs.IPFS.axios, "post")
.resolves({ data: mockData.mockNewFileModel })
const result = await bchjs.IPFS.createFileModel(path)
const result = await bchjs.IPFS.createFileModelServer(path)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
@@ -72,12 +71,12 @@ describe(`#IPFS`, () => {
})
})
describe("#uploadFile", () => {
describe("#uploadFileServer", () => {
it("should throw an error if file does not exist", async () => {
try {
const path = "/non-existant-file"
await bchjs.IPFS.uploadFile(path)
await bchjs.IPFS.uploadFileServer(path)
assert.equal(true, false, "Unexpected result")
} catch (err) {
@@ -90,7 +89,7 @@ describe(`#IPFS`, () => {
try {
const path = `${__dirname}/ipfs.js`
await bchjs.IPFS.uploadFile(path)
await bchjs.IPFS.uploadFileServer(path)
assert.equal(true, false, "Unexpected result")
} catch (err) {
@@ -112,7 +111,7 @@ describe(`#IPFS`, () => {
sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mock)
const path = `${__dirname}/ipfs.js`
await bchjs.IPFS.uploadFile(path, "5ec562319bfacc745e8d8a52")
await bchjs.IPFS.uploadFileServer(path, "5ec562319bfacc745e8d8a52")
assert.equal(true, false, "Unexpected result")
} catch (err) {
@@ -126,7 +125,7 @@ describe(`#IPFS`, () => {
sandbox.stub(bchjs.IPFS.uppy, "upload").resolves(mockData.uploadData)
const path = `${__dirname}/ipfs.js`
const result = await bchjs.IPFS.uploadFile(
const result = await bchjs.IPFS.uploadFileServer(
path,
"5ec562319bfacc745e8d8a52"
)
@@ -192,4 +191,253 @@ describe(`#IPFS`, () => {
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")
}
})
})
})
+202 -2
View File
@@ -22,13 +22,16 @@ describe(`#Ninsight`, () => {
assert.equal(true, false, "Unexpected result!")
} catch (err) {
//console.log(`err: `, err)
assert.include(err.message, `address needs to be a string`)
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, "get").resolves({ data: mockData.utxo })
sandbox.stub(axios, "post").resolves({ data: mockData.utxo })
const addr = "bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9"
@@ -51,5 +54,202 @@ describe(`#Ninsight`, () => {
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")
})
})
})
+61 -19
View File
@@ -1,28 +1,70 @@
const chai = require("chai")
const assert = require("assert")
const assert = require("chai").assert
const BCHJS = require("../../src/bch-js")
const bchjs = new BCHJS()
const axios = require("axios")
const sinon = require("sinon")
describe("#Price", () => {
const mockDataLib = require("./fixtures/price-mocks")
let mockData
describe("#price", () => {
let sandbox
let bchjs
beforeEach(() => {
sandbox = sinon.createSandbox()
bchjs = new BCHJS()
mockData = Object.assign({}, mockDataLib)
})
afterEach(() => sandbox.restore())
describe("#current", () => {
describe("#single currency", () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
it("should get current price for single currency", async () => {
sandbox
.stub(bchjs.Price.axios, "get")
.resolves({ data: { price: 24905 } })
it("should get current price for single currency", done => {
const data = 46347
const resolved = new Promise(r => r({ data: data }))
sandbox.stub(axios, "get").returns(resolved)
const result = await bchjs.Price.current("usd")
// console.log(result)
bchjs.Price.current("usd")
.then(result => {
assert.deepEqual(data.price, result)
})
.then(done, done)
})
assert.isNumber(result)
})
})
describe("#getUsd", () => {
it("should get the USD price of BCH", async () => {
sandbox.stub(bchjs.Price.axios, "get").resolves({ data: { usd: 249.87 } })
const result = await bchjs.Price.getUsd()
// console.log(result)
assert.isNumber(result)
})
})
describe("#rates", () => {
it("should get the price of BCH in several currencies", async () => {
sandbox
.stub(bchjs.Price.axios, "get")
.resolves({ data: mockData.mockRates })
const result = await bchjs.Price.rates()
// console.log(result)
assert.property(result, "USD")
assert.property(result, "CAD")
})
})
describe("#getBchaUsd", () => {
it("should get the USD price of BCHA", async () => {
sandbox.stub(bchjs.Price.axios, "get").resolves({ data: { usd: 18.87 } })
const result = await bchjs.Price.getBchaUsd()
// console.log(result)
assert.isNumber(result)
})
})
})
+184 -16
View File
@@ -231,22 +231,6 @@ describe("#SLP Utils", () => {
})
})
describe("#balance", () => {
it(`should fetch balance of single token for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9`, async () => {
// Mock the call to the REST API
if (process.env.TEST === "unit")
sandbox.stub(axios, "get").resolves({ data: mockData.mockBalance })
const balance = await slp.Utils.balance(
"simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9",
"df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb"
)
//console.log(`balance: ${JSON.stringify(balance, null, 2)}`)
assert2.hasAllKeys(balance, ["tokenId", "balance", "balanceString"])
})
})
describe("#validateTxid", () => {
it(`should validate slp txid`, async () => {
// Mock the call to the REST API
@@ -1711,6 +1695,76 @@ describe("#SLP Utils", () => {
assert2.equal(data[2].isValid, false)
})
it("should return null value when 429 recieved", async () => {
const utxos = [
{
height: 654522,
tx_hash:
"072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
tx_pos: 0,
value: 6999,
satoshis: 6999,
txid:
"072a1e2c2d5f1309bf4eef7f88684e4ecd544a903b386b07f3e04b91b13d8af1",
vout: 0
},
{
height: 654522,
tx_hash:
"a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
tx_pos: 1,
value: 546,
satoshis: 546,
txid:
"a72db6a0883ecb8e379f317231b2571e41e041b7b1107e3e54c2e0b3386ac6ca",
vout: 1
}
]
sandbox.stub(slp.Utils, "decodeOpReturn").rejects({
error:
"Too many requests. Your limits are currently 3 requests per minute. Increase rate limits at https://fullstack.cash"
})
const data = await slp.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// Values should be 'null' to signal that a determination could not be made
// due to a throttling issue.
assert.equal(data[0].isValid, null)
assert.equal(data[1].isValid, null)
})
// it("should handle a dust attack", async () => {
it("#dustattack", async () => {
// Mock external dependencies.
// Stub the calls to decodeOpReturn.
sandbox
.stub(slp.Utils, "decodeOpReturn")
.rejects(new Error("lokad id wrong size"))
const utxos = [
{
height: 655965,
tx_hash:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
"a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e",
vout: 151,
address: "bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww",
hdIndex: 11
}
]
const data = await slp.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
})
describe("#txDetails", () => {
@@ -1776,4 +1830,118 @@ describe("#SLP Utils", () => {
])
})
})
describe("#hydrateUtxos", () => {
it("should throw an error if input is not an array", async () => {
try {
const utxos = 1234
await slp.Utils.hydrateUtxos(utxos)
assert2.equal(true, false, "Uh oh. Code path should not end here.")
} catch (err) {
// console.log(`Error: `, err)
assert2.include(err.message, `Input must be an array.`)
}
})
})
describe("#validateTxid2", () => {
it("should throw an error if the input is an array", async () => {
try {
const txid = [
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
]
await slp.Utils.validateTxid2(txid)
assert2.equal(true, false, "Unexpected result")
} catch (err) {
// console.log("err: ", err)
assert2.include(err.message, "txid must be 64 character string")
}
})
it("should throw an error for a malformed txid", async () => {
try {
const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783"
await slp.Utils.validateTxid2(txid)
assert2.equal(true, false, "Unexpected result")
} catch (err) {
// console.log("err: ", err)
assert2.include(err.message, "txid must be 64 character string")
}
})
it("should invalidate a known invalid TXID", async () => {
const txid =
"f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a"
// Mock live network calls.
sandbox.stub(axios, "get").resolves({
data: {
txid: txid,
isValid: false,
msg: ""
}
})
const result = await slp.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.property(result, "txid")
assert2.equal(result.txid, txid)
assert2.property(result, "isValid")
assert2.equal(result.isValid, false)
})
it("should validate a known valid TXID", async () => {
const txid =
"3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488"
// Mock live network calls.
sandbox.stub(axios, "get").resolves({
data: {
txid: txid,
isValid: true,
msg: ""
}
})
const result = await slp.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.property(result, "txid")
assert2.equal(result.txid, txid)
assert2.property(result, "isValid")
assert2.equal(result.isValid, true)
})
// slp-validate can take a long time. bch-api cuts it off if it fails to
// return is less than 10 seconds. This test case handle this situation.
it("should handle timeout errors", async () => {
try {
const txid =
"eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579"
// Mock live network calls.
sandbox.stub(axios, "get").rejects({
error:
"Network error: Could not communicate with full node or other external service."
})
await slp.Utils.validateTxid2(txid)
assert2.equal(true, false, "Unexpected result")
} catch (err) {
// console.log("err: ", err)
assert2.include(err.message, "slp-validate timed out")
}
})
})
})