Compare commits

..
227 Commits
Author SHA1 Message Date
Chris Troutner a8a4eb68a6 fix(bchjs.Crypto.sha256): Updating docs 2021-11-30 06:35:19 -08:00
Chris Troutner c05e2f14e2 Merge pull request #197 from salemkode/master
edit docs of bchjs.Crypto.sha256
2021-11-30 06:34:09 -08:00
Salem Mohammed Shamakh 3540d98910 Update docs of bchjs.Crypto.sha256 2021-11-30 08:15:12 +03:00
Chris Troutner d0129c01f3 Merge pull request #196 from Permissionless-Software-Foundation/temp-documentation
Temp documentation
2021-11-13 06:46:59 -08:00
Chris Troutner ad40607e5b Merge pull request #186 from mr-zwets/documentation-fix
small fixes documentation
2021-11-13 06:43:55 -08:00
Chris Troutner a4f0ee6e42 Merge branch 'master' into documentation-fix 2021-11-13 06:42:57 -08:00
Chris Troutner 0b70ea108b Merge pull request #195 from Permissionless-Software-Foundation/temp-documentation
Temp documentation
2021-11-13 06:42:05 -08:00
Chris Troutner cef9a3cf52 Merge pull request #194 from mr-zwets/documentation
clarified some of the documentation
2021-11-13 06:38:04 -08:00
mathi c216f6c075 clarified some of the documentation 2021-11-13 11:37:56 +01:00
Chris Troutner b53f6bcfc6 Merge pull request #193 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Moving util.js integration tests to bchn specific folder
2021-11-10 18:16:45 -08:00
Chris Troutner cbc834cc1a fix(tests): Moving util.js integration tests to bchn specific folder 2021-11-10 17:32:17 -08:00
Chris Troutner 1cb5fe069b Merge pull request #192 from Permissionless-Software-Foundation/ct-unstable
Created Transaction.get3() to help with SLP indexer
2021-11-03 10:21:21 -07:00
Chris Troutner 425e2cbbc1 fix(Transaction.get3()): Created Transaction.get3() 2021-11-03 10:16:38 -07:00
Chris Troutner 9e92f77523 Making progress on Transaction.get3() 2021-11-03 07:53:56 -07:00
Chris Troutner dc85bb4cfe Making progress on Transaction.get3() 2021-11-03 07:53:42 -07:00
Chris Troutner 92960b6957 Merge branch 'master' into ct-unstable 2021-11-02 20:37:07 -07:00
Chris Troutner 6f34ea286a Merge pull request #191 from Permissionless-Software-Foundation/fix-bip32
fix(bip32-util): Moving to @chris.troutner/bip32-util
2021-11-01 06:21:49 -07:00
Chris Troutner 26f39023c4 fix(bip32-util): Moving to @chris.troutner/bip32-util 2021-11-01 06:18:55 -07:00
Chris Troutner 4a3baca72d Messing around with different variations of transaction.get2() 2021-11-01 06:12:14 -07:00
Chris Troutner fe37c3ca43 Merge pull request #190 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get2()): Improvement to vin token inputs
2021-10-31 18:56:09 -07:00
Chris Troutner ff4ffab670 fix(Transaction.get2()): Improvement to vin token inputs 2021-10-31 18:50:47 -07:00
Chris Troutner 400e7b31a8 Merge pull request #189 from Permissionless-Software-Foundation/ct-unstable
Adding Transaction.get2()
2021-10-31 17:02:14 -07:00
Chris Troutner 360fca0475 fix(Transaction.get2()): Added block height to tx data 2021-10-31 16:58:40 -07:00
Chris Troutner ef7056ee7a fix(Transaction.get2()): Added non-validating tx get 2021-10-31 16:29:50 -07:00
Chris Troutner d221077786 Merge pull request #188 from Permissionless-Software-Foundation/ct-unstable
fix(Jenkins): Attempting to fix CI/CD
2021-10-31 14:52:22 -07:00
Chris Troutner 046ab78c6a fix(Jenkins): Attempting to fix CI/CD 2021-10-31 14:44:07 -07:00
Chris Troutner 29e90915e7 Merge pull request #187 from Permissionless-Software-Foundation/ct-unstable
fix(bip-32): Updated bip-32 library to improve vulns
2021-10-31 14:31:52 -07:00
Chris Troutner 3d85dbfb22 Updating depds 2021-10-31 14:26:43 -07:00
Chris Troutner 2c0fa9dfbd Switched to exact dependency versions 2021-10-31 14:22:36 -07:00
Chris Troutner 6ca1d729dc fix(bip-32): Updated bip-32 library to improve vulns 2021-10-31 14:14:13 -07:00
Chris Troutner c095af8a29 Merge branch 'master' into documentation-fix 2021-10-30 09:07:46 -07:00
mathi 89e4ea1635 small fixes documentation 2021-10-30 17:06:46 +02:00
Chris Troutner 85a34a8305 Merge pull request #185 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Adding token ID to tx token inputs
2021-10-28 16:58:56 -07:00
Chris Troutner ef6cfe4409 fix(Transaction.get()): Adding token ID to tx token inputs 2021-10-28 16:56:35 -07:00
Chris Troutner a29d6ed26a Merge pull request #184 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Validating SLP inputs
2021-10-21 21:07:15 -07:00
Chris Troutner fa58a0764c fix(Transaction.get()): Validating SLP inputs 2021-10-21 21:04:11 -07:00
Chris Troutner 2c2fbeaa46 Merge pull request #183 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Fixing another bug in MINT input handling
2021-10-21 20:47:03 -07:00
Chris Troutner 1e4cd5f99e fix(Transaction.get()): Fixing another bug in MINT input handling 2021-10-21 20:44:47 -07:00
Chris Troutner 52c1ebdafe Merge pull request #182 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Fixing bug in previous PR
2021-10-21 19:45:36 -07:00
Chris Troutner 2e56d66f48 fix(Transaction.get()): Fixing bug in previous PR 2021-10-21 19:42:12 -07:00
Chris Troutner 9cf6c7e7a7 Merge pull request #181 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Adding support for MINT corner-case
2021-10-21 19:15:08 -07:00
Chris Troutner 43043672c6 linting 2021-10-21 19:12:10 -07:00
Chris Troutner 4f4b2c864d fix(Transaction.get()): Adding support for MINT corner-case 2021-10-21 19:11:55 -07:00
Chris Troutner 951fea8f9b Merge pull request #180 from Permissionless-Software-Foundation/ct-unstable
fix(npm): Ran npm audit fix, to fix vulns
2021-10-16 09:49:50 -07:00
Chris Troutner 6d75dceffb fix(npm): Ran npm audit fix, to fix vulns 2021-10-16 09:46:55 -07:00
Chris Troutner 3a491c7aba Merge pull request #178 from Permissionless-Software-Foundation/ct-unstable
fix(generateGenesisOpReturn): Fixing bug when 0 decimal
2021-10-14 13:08:31 -07:00
Chris Troutner f03c020e4f fix(generateGenesisOpReturn): Fixing bug when 0 decimal 2021-10-14 13:06:15 -07:00
Chris Troutner 4aeb1148e5 Merge pull request #177 from Permissionless-Software-Foundation/review01
added toEcashAddress() & ecashtoCashAddress() for ecash format support
2021-10-04 07:55:41 -07:00
Chris Troutner f03ee13a1e Merge pull request #175 from mr-zwets/master
conversion between bitcoincash: and ecash:
2021-10-04 06:55:48 -07:00
mr-zwets cb98773d6a Merge branch 'Permissionless-Software-Foundation:master' into master 2021-09-23 07:43:07 +02:00
Chris Troutner c593d68ee5 Merge pull request #176 from Permissionless-Software-Foundation/ct-unstable
Improvements to Transaction.get()
2021-09-22 17:36:30 -07:00
Chris Troutner dc7d3a9339 fix(Transaction.get): Using BigNumber for better tokenqty calc 2021-09-22 17:33:27 -07:00
Chris Troutner 6f4cc0f439 fix(Transaction.get): Fixed bug when Genesis TX was input to queried TX 2021-09-22 16:58:14 -07:00
Chris Troutner 7aa0fa865e fix(decodeOpReturn): Fixed api docs 2021-09-22 15:40:23 -07:00
mathi e8b6d7fb36 added toEcashAddress() & ecashtoCashAddress() for ecash format support 2021-09-22 14:38:54 +02:00
Chris Troutner b13ef94ea3 Merge pull request #174 from Permissionless-Software-Foundation/ct-unstable
fix(npm): Ran npm audit fix, 0 vulns
2021-09-20 16:07:18 -07:00
Chris Troutner 912f899f4e fix(npm): Ran npm audit fix, 0 vulns 2021-09-20 16:04:14 -07:00
Chris Troutner 9063d8da0b Merge pull request #173 from Permissionless-Software-Foundation/feat/ecash-price
Feat/ecash price
2021-09-20 16:00:32 -07:00
Chris Troutner f4e2bd96f0 fix(XEC price): Fixing BCHA XEC price feeds 2021-09-20 15:56:42 -07:00
Chris Troutner 1c898a4ad3 Removing ninsight from integration tests 2021-09-20 15:54:54 -07:00
Chris Troutner c4db813f76 Merge pull request #172 from mr-zwets/master
changed getBchaUsd which returns the price for the BCHA denomination, added getXecUsd for the XEC denomination
2021-09-20 15:33:30 -07:00
mathi 38b96b441f changed getBchaUsd so it returns the BCHA denomination, added getXecUsd 2021-09-20 22:13:24 +02:00
Chris Troutner c94f78f510 Merge pull request #170 from Permissionless-Software-Foundation/ct-unstable
fix(minimal-slp-wallet): Fixed code that was breaking minimal-slp-wal…
2021-08-12 09:16:52 -07:00
Chris Troutner 6de2b712ec fix(minimal-slp-wallet): Fixed code that was breaking minimal-slp-wallet build 2021-08-12 09:14:29 -07:00
Chris Troutner 6f681fd7b0 Merge pull request #169 from Permissionless-Software-Foundation/ct-unstable
fix(npm): Ran npm audit fix. 0 vulns
2021-08-09 17:59:43 -07:00
Chris Troutner 9a82effb2b fix(npm): Ran npm audit fix. 0 vulns 2021-08-09 17:41:38 -07:00
Chris Troutner 955a5e428e Merge pull request #168 from Permissionless-Software-Foundation/ct-unstable
fix(getRawTransaction): Fixing integration test
2021-08-09 14:56:52 -07:00
Chris Troutner 44c3bf44f9 fix(getRawTransaction): Fixing integration test 2021-08-09 14:54:49 -07:00
Chris Troutner 31ce84abb0 Merge pull request #167 from Permissionless-Software-Foundation/ct-unstable
fix(waterfallValidateTxid): Skipping validateTxid3() if it throws an error
2021-08-09 10:39:18 -07:00
Chris Troutner 40fa4b6bc1 linting 2021-08-09 10:36:07 -07:00
Chris Troutner 8c5e111d60 fix(waterfallValidateTxid): Skipping validateTxid3() if it throws an error 2021-08-09 10:35:51 -07:00
Chris Troutner 8965dfc57f Merge pull request #166 from Permissionless-Software-Foundation/ct-unstable
fix(apiToken): Changed _this.axiosOptions to this.axiosOptions
2021-08-08 20:06:05 -07:00
Chris Troutner 93904a632f fix(apiToken): Changed _this.axiosOptions to this.axiosOptions 2021-08-08 20:01:38 -07:00
Chris Troutner 851d4c404c fix(rate limits): Fixing more bugs with rate limit handling axios.options 2021-08-08 19:51:01 -07:00
Chris Troutner 44bac45c32 Updating README 2021-08-08 18:42:04 -07:00
Chris Troutner 95ee8054be Merge pull request #165 from Permissionless-Software-Foundation/ct-unstable
fix(Transactions.get()): Adding extra error handling for rate limits
2021-08-08 18:40:43 -07:00
Chris Troutner 547b28cac8 fix(Transactions.get()): Adding extra error handling for rate limits 2021-08-08 18:38:46 -07:00
Chris Troutner 40ba334a41 Merge pull request #163 from Permissionless-Software-Foundation/ct-unstable
fix(validateTxid3): Throwing an error when rate limit encountered.
2021-08-08 18:06:52 -07:00
Chris Troutner ffd8ccaf2b fix(validateTxid3): Throwing an error when rate limit encountered. 2021-08-08 18:04:39 -07:00
Chris Troutner 9a16c0c6d6 Merge pull request #162 from Permissionless-Software-Foundation/ct-unstable
fix(raw-transaction): Fixing JWT token bug
2021-08-07 12:48:00 -07:00
Chris Troutner 52a891fd78 fix(raw-transaction): Fixing JWT token bug 2021-08-07 12:41:24 -07:00
Chris Troutner c09df08429 fix(npm): Ran npm audit fix 2021-07-16 15:56:36 -07:00
Chris Troutner 4dc88c0af3 Merge pull request #160 from Permissionless-Software-Foundation/ct-unstable
fix(transactions): Adding better error messages
2021-07-16 15:20:12 -07:00
Chris Troutner 85caa9556b fix(transactions): Adding better error messages 2021-07-16 15:18:35 -07:00
Chris Troutner bbdc417eb2 Merge pull request #159 from Permissionless-Software-Foundation/ct-unstable
Updating README
2021-07-12 19:57:47 -07:00
Chris Troutner cc3bc002e7 Updating README 2021-07-12 19:52:29 -07:00
Chris Troutner a75e1ab633 Merge pull request #158 from Permissionless-Software-Foundation/ct-unstable
fix(dsproof): Adding documentation
2021-06-22 15:49:17 -07:00
Chris Troutner 8c5f1e2274 fix(dsproof): Adding documentation 2021-06-22 15:47:22 -07:00
Chris Troutner c30e76c942 Merge pull request #157 from Permissionless-Software-Foundation/dh-dsproof
feat(DSProof): Added DSProof to bch-js
2021-06-22 12:28:40 -07:00
Chris Troutner a575621c50 Fixing ABC integration tests 2021-06-22 12:24:29 -07:00
Daniel Gonzalez d3a0c5948a feat(DSProof): Added DSProof to bch-js 2021-06-19 22:40:16 -04:00
Chris Troutner 61bb509195 Merge pull request #156 from Permissionless-Software-Foundation/ct-unstable
fix(raw-transactions): Adding better error handling for rate-limits
2021-06-19 10:43:24 -07:00
Chris Troutner a18e549694 fix(raw-transactions): Adding better error handling for rate-limits 2021-06-19 10:41:49 -07:00
Chris Troutner 7d53f47d8a Merge pull request #155 from Permissionless-Software-Foundation/ct-unstable
fix(error): Adding additional rate limit error handling
2021-06-19 10:09:51 -07:00
Chris Troutner e8e615795d fix(error): Adding additional rate limit error handling 2021-06-19 10:08:28 -07:00
Chris Troutner 5759b096d5 Merge pull request #153 from Permissionless-Software-Foundation/ct-unstable
fix(errors): Throwing rate limit errors as Error objects
2021-06-19 09:44:11 -07:00
Chris Troutner 5b300f68b2 fix(errors): Throwing rate limit errors as Error objects 2021-06-19 09:42:36 -07:00
Chris Troutner 3cce6f9754 Merge pull request #152 from Permissionless-Software-Foundation/ct-unstable
fix(testnet): Fixing testnet integration test
2021-06-18 09:43:06 -07:00
Chris Troutner 3da04d9327 fix(testnet): Fixing testnet integration test 2021-06-18 09:32:07 -07:00
Chris Troutner 79091815a2 Merge pull request #151 from Permissionless-Software-Foundation/ct-unstable
fix(Electrumx): Fixing integration test
2021-06-17 07:19:55 -07:00
Chris Troutner 258144d26d fix(Electrumx): Fixing integration test 2021-06-17 07:16:48 -07:00
Chris Troutner 4c4a45841b Merge pull request #150 from Permissionless-Software-Foundation/ct-unstable
fix(package.json): Using /v5 in npm scripts
2021-06-16 19:17:14 -07:00
Chris Troutner 010afd10a3 Merge branch 'master' into ct-unstable 2021-06-16 19:13:03 -07:00
Chris Troutner 82b4d2ab9e fix(package.json): Using /v5 in npm scripts 2021-06-16 19:12:48 -07:00
Chris Troutner 8eb6a21dc2 Merge pull request #149 from Permissionless-Software-Foundation/ct-unstable
feat(/v5 REST API): Using the new v5 route in bch-api
2021-06-16 19:03:45 -07:00
Chris Troutner 5f2a762d4f feat(/v5 REST API): Using the new v5 route in bch-api 2021-06-16 17:00:08 -07:00
Chris Troutner 24e2c78d03 Merge pull request #148 from Permissionless-Software-Foundation/ct-unstable
fix(testnet): Removing testnet ninsight tests as they rarely pass
2021-05-22 07:58:39 -07:00
Chris Troutner eab02fdbc9 fix(testnet): Removing testnet ninsight tests as they rarely pass 2021-05-22 07:55:02 -07:00
Chris Troutner f7bd9c75db Merge pull request #147 from Permissionless-Software-Foundation/ct-unstable
fix(nft docs): Fixing a typo in docs
2021-05-21 15:54:07 -07:00
Chris Troutner 2a96f3a4f5 fix(nft docs): Fixing a typo in docs 2021-05-21 15:51:43 -07:00
Chris Troutner 3dc4a8ba6f Merge pull request #146 from Permissionless-Software-Foundation/ct-unstable
fix(dependencies): Updating dependencies. 0 npm vulns
2021-05-21 15:39:46 -07:00
Chris Troutner 65916f2c5c fix(dependencies): Updating dependencies. 0 npm vulns 2021-05-21 15:38:02 -07:00
Chris Troutner 18188bfdce Merge pull request #145 from Permissionless-Software-Foundation/nft-group-get-call
Return parent NFT group token information
2021-05-18 12:40:16 -07:00
Stoyan Zhekov 45acc2b0b7 Return parent NFT group token information 2021-05-16 14:22:33 +09:00
Chris Troutner 31913c3fb7 Merge pull request #144 from Permissionless-Software-Foundation/nft-children-get-call
NFT children get call
2021-05-15 08:54:58 -07:00
Stoyan Zhekov 2311643398 Example fixed 2021-05-14 21:58:52 +09:00
Stoyan Zhekov 7501aa0238 Lint fixes 2021-05-14 21:18:08 +09:00
Stoyan Zhekov 92a89a543d List NFT children per group 2021-05-14 21:17:07 +09:00
Chris Troutner cdf8171994 Merge pull request #143 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): Adding integration tests for bchn with no whi…
2021-05-03 19:18:34 -07:00
Chris Troutner 385baf0afd fix(integration tests): Adding integration tests for bchn with no whitelist SLPDB 2021-05-03 19:15:41 -07:00
Chris Troutner 9a9aababd2 Merge pull request #142 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): Adding tests for bchn with no whitelist SLPDB
2021-05-03 19:06:14 -07:00
Chris Troutner c228acd28a fix(integration tests): Adding tests for bchn with no whitelist SLPDB 2021-05-03 18:53:41 -07:00
Chris Troutner c1c140bdce Merge pull request #139 from Permissionless-Software-Foundation/dh-getblock
feat(getBlock): Added getBlock endpoint to bch-js
2021-04-07 13:07:05 -07:00
Daniel Gonzalez 54d9b80f26 feat(getBlock): Added getBlock endpoint to bch-js 2021-04-06 21:33:33 -04:00
Chris Troutner 961a190309 Merge pull request #138 from Permissionless-Software-Foundation/ct-unstable
fix(debugging): Removing all the debugging statements
2021-03-26 18:05:04 -08:00
Chris Troutner 2d2a91dfde fix(debugging): Removing all the debugging statements 2021-03-26 18:57:08 -07:00
Chris Troutner fbac18d456 Merge pull request #137 from Permissionless-Software-Foundation/ct-unstable
More connection reset debugging
2021-03-26 17:32:23 -08:00
Chris Troutner 62ae67ceab Logging axios options in decodeOpReturn 2021-03-26 18:29:48 -07:00
Chris Troutner 8e64d854cf fix(hydrateUtxos): Adding debugging info for axiosOptions 2021-03-26 18:26:18 -07:00
Chris Troutner 5fe8e515ff Merge pull request #136 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Adding debugging information
2021-03-26 16:30:50 -08:00
Chris Troutner f11297339b fix(decodeOpReturn): Adding debugging information 2021-03-26 17:27:57 -07:00
Chris Troutner 61d52c8ebe Merge pull request #135 from Permissionless-Software-Foundation/ct-unstable
fix(debugging): Adding additional debugging info to SLP utils
2021-03-26 12:53:35 -08:00
Chris Troutner d89bbe0bf6 fix(debugging): Adding additional debugging info to SLP utils 2021-03-26 13:51:29 -07:00
Chris Troutner 2a1c2500e6 Merge pull request #134 from Permissionless-Software-Foundation/ct-unstable
fix(slp): Adding more verbose error messages for SLP utility functions
2021-03-26 12:16:55 -08:00
Chris Troutner 8879a5170a fix(slp): Adding more verbose error messages for SLP utility functions 2021-03-26 13:11:40 -07:00
Chris Troutner b29cb7c12b Merge pull request #133 from Permissionless-Software-Foundation/ct-unstable
fix(integration tests): slowing down integration tests for the sake o…
2021-03-26 08:28:46 -07:00
Chris Troutner 69fa10d3a6 fix(integration tests): slowing down integration tests for the sake of rate limits 2021-03-26 08:25:39 -07:00
Chris Troutner d16431d165 Merge pull request #132 from Permissionless-Software-Foundation/ct-unstable
Adding Transaction.get()
2021-03-24 07:30:48 -07:00
Chris Troutner faa2972460 fix(Transaction.get()): Added documentation 2021-03-24 07:28:38 -07:00
Chris Troutner 5d3ea2b559 fix(Transaction.get()): Added unit tests 2021-03-24 07:24:08 -07:00
Chris Troutner 40fa6440f4 feat(Transactions.get()): Added tx hydration method 2021-03-23 18:49:27 -07:00
Chris Troutner ce951afddb fix(RawTransactions.getTxData): Adding BCH value to tx inputs 2021-03-23 17:37:00 -07:00
Chris Troutner d2e847d3fb feat(transactions): Created transaction library 2021-03-23 17:11:37 -07:00
Chris Troutner fe697455c0 Merge branch 'master' into ct-unstable 2021-03-09 08:09:01 -08:00
Chris Troutner 25832d52fd fix(raw-transactions): Adding proper axios header for Basic Auth 2021-03-09 08:04:14 -08:00
Chris Troutner 9d0840c2fe Merge pull request #131 from Permissionless-Software-Foundation/ct-unstable
fix(tokenUtxoDetailsWL): Adding usrObj input variable
2021-03-08 17:37:31 -08:00
Chris Troutner 74dc3e7e1d Merge branch 'master' into ct-unstable 2021-03-08 17:33:25 -08:00
Chris Troutner 171786a470 fix(tokenUtxoDetailsWL): Adding usrObj input variable 2021-03-08 17:32:33 -08:00
Chris Troutner 5cbd685060 Merge pull request #130 from Permissionless-Software-Foundation/dh-bch-price
feat(price): Created getBchUsd() for bch-js
2021-03-05 07:51:32 -08:00
Daniel Gonzalez ef05aff7a3 feat(price): Created getBchUsd() for bch-js 2021-03-04 15:14:46 -04:00
Chris Troutner 3f01fdba7c Merge pull request #129 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Stringifying bch-api object when passing error
2021-03-02 07:49:14 -08:00
Chris Troutner 232747280e Merge branch 'master' into ct-unstable 2021-03-02 07:47:03 -08:00
Chris Troutner e96da1615b fix(hydrateUtxos): Stringifying bch-api object when passing error 2021-03-02 07:46:51 -08:00
Chris Troutner 9538501f01 Merge pull request #128 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Adding better error handler
2021-03-02 07:31:43 -08:00
Chris Troutner 0a4fee5716 fix(hydrateUtxos): Adding better error handler 2021-03-02 07:29:41 -08:00
Chris Troutner cec79be4fa Merge pull request #127 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Adding usrObj input for delaying UTXO processing
2021-03-01 08:21:23 -08:00
Chris Troutner c839f0b891 fix(integration tests): Fixing broken ABC integration test 2021-03-01 08:19:16 -08:00
Chris Troutner 24d629ebe1 fix(hydrateUtxos): Adding usrObj input for delaying UTXO processing 2021-03-01 07:48:08 -08:00
Chris Troutner f729918385 fix(_hydrateUtxo): Adding user-configurable artifical delay 2021-03-01 06:55:35 -08:00
Chris Troutner b7ab02c0ed Merge pull request #126 from Permissionless-Software-Foundation/ct-unstable
fix(docs): Updating docs and commenting out debug logs
2021-02-27 15:40:58 -08:00
Chris Troutner 1c291880e0 fix(docs): Updating docs and commenting out debug logs 2021-02-27 15:38:24 -08:00
Chris Troutner 09b79c4d3e Merge pull request #125 from Permissionless-Software-Foundation/ct-unstable
Adding unit tests for 429 and 503 errors
2021-02-27 14:56:14 -08:00
Chris Troutner 828f6a1b1c fix(_hydrateUtxo): Added unit tests 2021-02-27 14:53:54 -08:00
Chris Troutner 1b8727da55 fix(decodeOpReturn): Adding test cases for 429 and 503 errors 2021-02-27 13:28:53 -08:00
Chris Troutner f84862d875 Merge pull request #124 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Added NFT child test case
2021-02-27 13:12:30 -08:00
Chris Troutner 179d924899 Adding respond.data in error 2021-02-27 13:10:33 -08:00
Chris Troutner 676fb85abf fix(decodeOpReturn): Added NFT child test case 2021-02-27 12:58:40 -08:00
Chris Troutner d59c1fe445 Merge pull request #123 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Trying different error configuration
2021-02-27 10:03:58 -08:00
Chris Troutner 0a34611557 fix(decodeOpReturn): Trying different error configuration 2021-02-27 10:02:12 -08:00
Chris Troutner c13b029e99 Merge pull request #122 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Replacing error message with better one
2021-02-27 09:47:20 -08:00
Chris Troutner b038e9a7fd fix(decodeOpReturn): Replacing error message with better one 2021-02-27 09:45:24 -08:00
Chris Troutner 7b728fb45d Merge pull request #121 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Adding additional debugging for errors
2021-02-27 09:33:00 -08:00
Chris Troutner d4dbce8df0 fix(decodeOpReturn): Adding additional debugging for errors 2021-02-27 09:31:16 -08:00
Chris Troutner 2419aeceaa Merge pull request #120 from Permissionless-Software-Foundation/ct-unstable
fix(_hydrateUtxo): Adding more debugging
2021-02-27 09:09:58 -08:00
Chris Troutner 158b009069 fix(_hydrateUtxo): Adding more debugging 2021-02-27 09:07:50 -08:00
Chris Troutner f3c3250a69 Merge pull request #119 from Permissionless-Software-Foundation/ct-unstable
fix(_hydrateUtxo): Debugging error handling
2021-02-27 09:00:20 -08:00
Chris Troutner 5ae4db88c3 Merge branch 'master' into ct-unstable 2021-02-27 08:58:23 -08:00
Chris Troutner 33e161df26 fix(_hydrateUtxo): Debugging error handling 2021-02-27 08:58:12 -08:00
Chris Troutner 81c910361c Merge pull request #118 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Debugging error handling by decodeOpReturn()
2021-02-27 08:41:57 -08:00
Chris Troutner 9751a39538 Merge branch 'master' into ct-unstable 2021-02-27 08:38:38 -08:00
Chris Troutner 7058f77ee9 fix(decodeOpReturn): Debugging error handling by decodeOpReturn() 2021-02-27 08:37:29 -08:00
Chris Troutner ad580d67a4 Adding decatur test script 2021-02-27 08:23:07 -08:00
Chris Troutner acc0300a44 Merge pull request #117 from Permissionless-Software-Foundation/ct-unstable
fix(bip32-utils): Bumping to v1.0.0 with updated keccak
2021-02-24 18:56:39 -08:00
Chris Troutner 4e1b806230 fix(bip32-utils): Bumping to v1.0.0 with updated keccak 2021-02-24 18:53:20 -08:00
Chris Troutner 357e350176 Merge pull request #116 from Permissionless-Software-Foundation/ct-unstable
fix(electrumx): Adding usrObj to Electrumx.Transactions()
2021-02-24 17:59:35 -08:00
Chris Troutner f6140acc76 fix(electrumx): Adding usrObj to Electrumx.Transactions() 2021-02-24 17:53:53 -08:00
Chris Troutner df65b12aab Merge pull request #115 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): replacing ip with usrObj
2021-02-24 16:44:58 -08:00
Chris Troutner 754b4cd807 fix(decodeOpReturn): replacing ip with usrObj 2021-02-24 16:42:54 -08:00
Chris Troutner 3c6079e54c Merge pull request #114 from Permissionless-Software-Foundation/ct-unstable
fix(usrObj): Fixing typo of usbObj to usrObj
2021-02-24 16:37:09 -08:00
Chris Troutner 18621cd76c fix(usrObj): Fixing typo of usbObj to usrObj 2021-02-24 16:35:09 -08:00
Chris Troutner 456159a4cc Merge pull request #113 from Permissionless-Software-Foundation/ct-unstable
fix(usbObj): Passing a usrObj when bch-api calls bch-js
2021-02-24 16:29:54 -08:00
Chris Troutner 33e723befe fix(usbObj): Passing a usrObj when bch-api calls bch-js 2021-02-24 16:27:49 -08:00
Chris Troutner 30fdee1920 Merge pull request #112 from Permissionless-Software-Foundation/ct-unstable
fix(decodeOpReturn): Switching to POST from GET to getRawTx call
2021-02-24 15:14:51 -08:00
Chris Troutner feb336c331 fix(decodeOpReturn): Switching to POST from GET to getRawTx call 2021-02-24 15:11:58 -08:00
Chris Troutner 8bacc0a991 Merge pull request #111 from Permissionless-Software-Foundation/ct-unstable
fix(ip): Renaming origin to ip
2021-02-24 14:10:07 -08:00
Chris Troutner d22f1ec8a6 fix(ip): Renaming origin to ip 2021-02-24 14:05:25 -08:00
Chris Troutner 7eb3a796b8 Merge pull request #110 from Permissionless-Software-Foundation/ct-unstable
fix(origin): Testing idea around passing origin when bch-api calls bc…
2021-02-24 13:29:05 -08:00
Chris Troutner 9919f811b0 fix(origin): Testing idea around passing origin when bch-api calls bch-js 2021-02-24 13:26:08 -08:00
Chris Troutner e7854481a0 Merge pull request #109 from Permissionless-Software-Foundation/ct-unstable
fix(dependencies): Removing several unused dependencies
2021-02-23 10:29:22 -08:00
Chris Troutner 0b406c467e fix(dependencies): Removing more unnecessary dependencies 2021-02-23 10:26:12 -08:00
Chris Troutner e41217b710 fix(dependencies): Removing several unused dependencies 2021-02-23 09:04:49 -08:00
Chris Troutner c22ce8d57d fix(dependencies): Ran npm audit with 0 vulns 2021-02-23 08:46:27 -08:00
Chris Troutner bf3612583e Merge pull request #108 from Permissionless-Software-Foundation/ct-unstable
Sleep: Adding a Promise-based wait function to Util lib
2021-02-15 15:28:26 -08:00
Chris Troutner ef56452ec9 feat(sleep): Adding sleep function to Util lib 2021-02-15 15:16:30 -08:00
Chris Troutner b3be470c0e fix(utxo.get()): Fixing ABC integration test 2021-02-15 15:12:39 -08:00
Chris Troutner 7274475898 Merge pull request #107 from Permissionless-Software-Foundation/ct-unstable
fix(package-lock.json): Updating package-lock.json
2021-02-13 17:46:19 -08:00
Chris Troutner 7bb347a833 fix(package-lock.json): Updating package-lock.json 2021-02-13 17:44:14 -08:00
Chris Troutner fadceedd00 Merge pull request #106 from Permissionless-Software-Foundation/ct-unstable
fix(debug): Removing debugging lines
2021-02-13 17:33:43 -08:00
Chris Troutner 7f4ef60f8e fix(debug): Removing debugging lines 2021-02-13 17:30:55 -08:00
Chris Troutner 81e54e0590 Merge pull request #105 from Permissionless-Software-Foundation/ct-unstable
Porting bch-util into bch-js
2021-02-13 15:07:44 -08:00
Chris Troutner 1d5e3da976 feat(chunk20): bchjs.Util.chunk20() - chunk array into 20-element arrays 2021-02-13 15:04:39 -08:00
Chris Troutner 2cff305f46 feat(floor2): bchjs.Util.floor2(num) - round down to 2 decimal places 2021-02-13 14:59:33 -08:00
Chris Troutner 9495381cf4 feat(floor8): bchjs.Util.floor8(num) - Round down to 8 decimal places 2021-02-13 14:55:12 -08:00
Chris Troutner 32f89346c3 linting 2021-02-13 14:38:37 -08:00
Chris Troutner e8582fd0ab feat(findBiggestUtxo): Ported from bch-util. Finds biggest UTXO in an array 2021-02-13 14:38:01 -08:00
Chris Troutner 7049b96df1 Merge pull request #104 from Permissionless-Software-Foundation/ct-unstable
utxo.get(): Refined and finalized.
2021-02-12 19:48:13 -08:00
Chris Troutner 9cc6824591 feat(utxo.get whitelist): Added flag to validate against the whitelist filtered SLPDB 2021-02-12 19:44:24 -08:00
Chris Troutner bb82d520ed feat(utxo.get()): Output changed for different types of utxos 2021-02-12 18:30:25 -08:00
Chris Troutner fef05124f1 feat(utxo.get()): Changes to accept arrays of up to 20 addresses 2021-02-12 15:58:16 -08:00
Chris Troutner 978db07526 Merge pull request #103 from Permissionless-Software-Foundation/ct-unstable
feat(utxo): Added Utxo.get() for easy getting of utxos
2021-02-11 19:25:42 -08:00
Chris Troutner 68bab5dd26 feat(utxo): Added Utxo.get() for easy getting of utxos 2021-02-11 19:22:25 -08:00
Chris Troutner 20e685206d Removing the integration/implementations folder 2021-02-11 14:14:15 -08:00
Chris Troutner e90afac701 Updating local testnet integration test script 2021-02-10 13:31:21 -08:00
Chris Troutner 96f96f837e Merge pull request #102 from Permissionless-Software-Foundation/ct-unstable
fix(abc): Fixing testing scripts for ABC chain
2021-02-09 15:12:47 -08:00
Chris Troutner 03be1d5838 fix(abc): Fixing testing scripts for ABC chain 2021-02-09 15:08:14 -08:00
Chris Troutner 6b6bfa2ae2 Merge pull request #101 from Permissionless-Software-Foundation/ct-unstable
feat(hydrateUtxosWL): hydrateUtxo but uses SLPDB with whitelist filter
2021-02-09 12:37:15 -08:00
Chris Troutner da3af3656a feat(hydrateUtxosWL): hydrateUtxo but uses SLPDB with whitelist filter 2021-02-09 12:34:25 -08:00
74 changed files with 15386 additions and 19736 deletions
+2 -1
View File
@@ -1,7 +1,8 @@
Copyright 2021 Chris Troutner
Copyright 2021 Permissionless Software Foundation contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+5 -7
View File
@@ -33,17 +33,13 @@ Here are two YouTube walk-through videos to help you get started:
```
const BCHJS = require("@psf/bch-js")
let bchjs = new BCHJS() // Defaults to BCHN network.
// testnet
bchjs = new BCHJS({ restURL: 'https://testnet3.fullstack.cash/v4/' })
```
This library is intended to be paired with
the [bch-api](https://github.com/Permissionless-Software-Foundation/bch-api) REST API, and the infrastructure provided by [FullStack.cash](https://fullstack.cash). The `restURL` property can be changed to work with different Bitcoin Cash networks:
- BCHN Mainnet REST API server: https://bchn.fullstack.cash/v4/
- ABC Mainnet REST API server: https://abc.fullstack.cash/v4/
- Testnet3 REST API server: https://testnet3.fullstack.cash/v4/
- BCHN Mainnet REST API server: https://bchn.fullstack.cash/v5/
- ABC Mainnet REST API server: https://abc.fullstack.cash/v5/
- Check server status: https://metrics.fullstack.cash
### API Key
@@ -57,7 +53,7 @@ rate limits when interacting with the back end server. See [this article](https:
```
const BCHJS = require("@psf/bch-js")
let bchjs = new BCHJS({
restURL: 'https://bchn.fullstack.cash/v3/',
restURL: 'https://bchn.fullstack.cash/v5/',
apiToken: 'eyJhbGciO...' // Your JWT token here.
})
```
@@ -122,3 +118,5 @@ Copies of this repository are also published on [IPFS](https://ipfs.io).
## License
[MIT](LICENSE.md)
test
+1 -1
View File
@@ -1,3 +1,3 @@
Examples have been moved to this repository:
Examples have been moved to this repository
https://github.com/Permissionless-Software-Foundation/bch-js-examples
+9873 -18846
View File
File diff suppressed because it is too large Load Diff
+57 -67
View File
@@ -4,20 +4,27 @@
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
"author": "Chris Troutner <chris.troutner@gmail.com>",
"contributors": [
"Gabriel Cardona <gabriel@bitcoin.com>"
"Gabriel Cardona <gabriel@bitcoin.com>",
"Stoyan Zhekov <zh@zhware.net>"
],
"main": "src/bch-js",
"scripts": {
"test": "nyc mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
"test:integration": "npm run test:integration:bchn",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v4/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:nft": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 -g '#nft1' test/integration/chains/bchn/slp.js",
"test:integration:abc": "export RESTURL=https://abc.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/chains/testnet/",
"test:integration:local": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration",
"test:integration:local:bchn": "RESTURL=http://localhost:3000/v4/ mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v4/ mocha --timeout 30000 test/integration/testnet",
"test:temp": "export RESTURL=https://bchn.fullstack.cash/v4/ && mocha --timeout 30000 -g '#tokenUtxoDetailsWL' test/integration/",
"test:temp2": "mocha --timeout=30000 -g '#tokenUtxoDetailsWL' test/unit/",
"test:integration:local:abc": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:local:bchn": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v5/ mocha --timeout 30000 test/integration/chains/testnet",
"test:integration:decatur:bchn": "export RESTURL=http://192.168.0.36:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:decatur:abc": "export RESTURL=http://192.168.0.38:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v5/ && mocha --timeout 30000 test/integration/",
"test:temp": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
"test:temp2": "mocha --timeout=30000 -g '#TransactionLib' test/unit/",
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v5/ && mocha --timeout 30000 -g '#DSProof' test/integration/chains/",
"test:temp4": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#decodeOpReturn' test/integration/",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs",
@@ -30,69 +37,52 @@
"url": "git+https://github.com/Permissionless-Software-Foundation/bch-js.git"
},
"dependencies": {
"@psf/bip21": "^2.0.1",
"@psf/bip32-utils": "^0.13.1",
"@psf/bitcoincash-ops": "^2.0.0",
"@psf/bitcoincashjs-lib": "^4.0.2",
"@psf/coininfo": "^4.0.0",
"@uppy/core": "^1.10.4",
"@uppy/tus": "^1.5.12",
"assert": "^2.0.0",
"axios": "^0.21.1",
"bc-bip68": "^1.0.5",
"bchaddrjs-slp": "^0.2.5",
"bigi": "^1.4.2",
"bignumber.js": "^9.0.0",
"bip-schnorr": "^0.3.0",
"bip38": "^2.0.2",
"bip39": "^3.0.2",
"bip66": "^1.1.5",
"bitcoinjs-message": "^2.0.0",
"bs58": "^4.0.1",
"buffer": "^5.1.0",
"cashaddrjs": "^0.3.3",
"chalk": "^2.3.0",
"clear": "0.1.0",
"commander": "^3.0.0",
"cp-file": "^7.0.0",
"ecurve": "^1.0.6",
"figlet": "^1.2.0",
"git-clone": "^0.1.0",
"ini": "^1.3.8",
"mkdirp": "^0.5.1",
"node-cmd": "^3.0.0",
"node-emoji": "^1.8.1",
"qrcode": "^1.4.1",
"randombytes": "^2.0.6",
"repl.history": "^0.1.4",
"safe-buffer": "^5.1.2",
"satoshi-bitcoin": "^1.0.4",
"@psf/bip21": "2.0.1",
"@chris.troutner/bip32-utils": "1.0.5",
"@psf/bitcoincash-ops": "2.0.0",
"@psf/bitcoincashjs-lib": "4.0.2",
"@psf/coininfo": "4.0.0",
"@uppy/core": "1.10.4",
"@uppy/tus": "1.5.12",
"axios": "^0.21.4",
"bc-bip68": "1.0.5",
"bchaddrjs-slp": "0.2.5",
"bigi": "1.4.2",
"bignumber.js": "9.0.0",
"bip-schnorr": "0.3.0",
"bip38": "2.0.2",
"bip39": "3.0.2",
"bip66": "1.1.5",
"bitcoinjs-message": "2.0.0",
"bs58": "4.0.1",
"ecashaddrjs": "1.0.7",
"ini": "1.3.8",
"randombytes": "2.0.6",
"safe-buffer": "5.1.2",
"satoshi-bitcoin": "1.0.4",
"slp-mdm": "0.0.6",
"slp-parser": "0.0.4",
"socket.io": "^2.1.1",
"socket.io-client": "^2.1.1",
"touch": "^3.1.0",
"wif": "^2.0.6"
"wif": "2.0.6"
},
"devDependencies": {
"apidoc": "^0.17.7",
"chai": "^4.1.2",
"coveralls": "^3.0.2",
"eslint": "5.16.0",
"eslint-config-prettier": "^6.0.0",
"eslint-config-standard": "^14.1.0",
"eslint-plugin-node": "^9.1.0",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-standard": "^4.0.0",
"husky": "^4.3.6",
"lodash.clonedeep": "^4.5.0",
"mocha": "^6.1.4",
"node-mocks-http": "^1.7.0",
"nyc": "^14.1.0",
"prettier": "^1.18.2",
"semantic-release": "^17.3.1",
"sinon": "^7.3.2",
"standard": "^16.0.3"
"apidoc": "0.17.7",
"assert": "2.0.0",
"chai": "4.1.2",
"coveralls": "3.0.2",
"eslint": "7.17.0",
"eslint-config-prettier": "7.1.0",
"eslint-config-standard": "16.0.3",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "3.3.1",
"eslint-plugin-standard": "4.0.0",
"husky": "^4.3.8",
"lodash.clonedeep": "4.5.0",
"mocha": "9.1.3",
"node-mocks-http": "1.7.0",
"nyc": "15.1.0",
"semantic-release": "18.0.0",
"sinon": "9.2.2",
"standard": "^16.0.4"
},
"apidoc": {
"title": "bch-js",
+78 -5
View File
@@ -1,12 +1,12 @@
// const axios = require("axios")
const Bitcoin = require('@psf/bitcoincashjs-lib')
const cashaddr = require('cashaddrjs')
const cashaddr = require('ecashaddrjs')
const coininfo = require('@psf/coininfo')
class Address {
constructor (config) {
const tmp = {}
if (!config || !config.restURL) tmp.restURL = 'https://api.bchjs.cash/v4/'
if (!config || !config.restURL) tmp.restURL = 'https://api.bchjs.cash/v5/'
else tmp.restURL = config.restURL
this.restURL = tmp.restURL
@@ -129,6 +129,63 @@ class Address {
return cashAddress.split(':')[1]
}
/**
* @api Address.toEcashAddress() toEcashAddress()
* @apiName toEcashAddress
* @apiGroup Address
* @apiDescription Convert legacy to cashAddress format
*
* @apiExample Example usage:
* // mainnet
* bchjs.Address.toEcashAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2')
* // ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
*
* // mainnet no prefix
* bchjs.Address.toEcashAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2', false)
* // qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
*
*/
toEcashAddress (address, prefix = true) {
const decoded = this._decode(address)
const ecashAddress = cashaddr.encode(
'ecash',
decoded.type,
decoded.hash
)
if (prefix) return ecashAddress
return ecashAddress.split(':')[1]
}
/**
* @api Address.ecashtoCashAddress() ecashtoCashAddress()
* @apiName ecashtoCashAddress
* @apiGroup Address
* @apiDescription Convert legacy to cashAddress format
*
* @apiExample Example usage:
* // mainnet
* bchjs.Address.ecashtoCashAddress('ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da')
* // bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2
*
* // mainnet no prefix
* bchjs.Address.ecashtoCashAddress('ecash:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da', false)
* // qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2
*/
ecashtoCashAddress (address, prefix = true) {
const decoded = this._decodeEcashAddress(address)
const cashAddress = cashaddr.encode(
'bitcoincash',
decoded.type,
decoded.hash
)
if (prefix) return cashAddress
return cashAddress.split(':')[1]
}
// Converts any address format to hash160
toHash160 (address) {
const legacyAddress = this.toLegacyAddress(address)
@@ -166,7 +223,7 @@ class Address {
* @api Address.hash160ToCash() hash160ToCash()
* @apiName hash160ToCash
* @apiGroup Address
* @apiDescription Convert hash160 to cash address.
* @apiDescription Convert hash160 to cash address. Accepts either hexadecimal or buffer.
*
* @apiExample Example usage:
* bchjs.Address.hash160ToCash("573d93b475be4f1925f3b74ed951201b0147eac1")
@@ -271,6 +328,22 @@ class Address {
throw new Error(`Invalid format : ${address}`)
}
_decodeEcashAddress (address) {
if (address.indexOf(':') !== -1) {
const decoded = cashaddr.decode(address)
decoded.format = 'cashaddr'
return decoded
}
try {
const decoded = cashaddr.decode(`ecash:${address}`)
decoded.format = 'cashaddr'
return decoded
} catch (error) {}
throw new Error(`Invalid format : ${address}`)
}
/**
* @api Address.isLegacyAddress() isLegacyAddress()
* @apiName isLegacyAddress
@@ -721,13 +794,13 @@ class Address {
* @apiDescription Detect an addess from an OutputScript..
*
* @apiExample Example usage:
* const script = bchjs.Script.encode([
* const scriptBuffer = bchjs.Script.encode([
* Buffer.from("BOX", "ascii"),
* bchjs.Script.opcodes.OP_CAT,
* Buffer.from("BITBOX", "ascii"),
* bchjs.Script.opcodes.OP_EQUAL
* ]);
* const p2sh_hash160 = bchjs.Crypto.hash160(script);
* const p2sh_hash160 = bchjs.Crypto.hash160(scriptBuffer);
* const scriptPubKey = bchjs.Script.scriptHash.output.encode(p2sh_hash160);
*
* // mainnet address from output script
+9 -4
View File
@@ -7,8 +7,8 @@
*/
// bch-api mainnet.
const DEFAULT_REST_API = 'https://api.fullstack.cash/v4/'
// const DEFAULT_REST_API = "http://localhost:3000/v4/"
const DEFAULT_REST_API = 'https://api.fullstack.cash/v5/'
// const DEFAULT_REST_API = "http://localhost:3000/v5/"
// local deps
const BitcoinCash = require('./bitcoincash')
@@ -26,11 +26,13 @@ const TransactionBuilder = require('./transaction-builder')
const ECPair = require('./ecpair')
const Script = require('./script')
const Price = require('./price')
const Socket = require('./socket')
const Schnorr = require('./schnorr')
const SLP = require('./slp/slp')
const IPFS = require('./ipfs')
const Encryption = require('./encryption')
const Utxo = require('./utxo')
const Transaction = require('./transaction')
const DSProof = require('./dsproof')
// Indexers
const Ninsight = require('./ninsight')
@@ -106,13 +108,16 @@ class BCHJS {
this.TransactionBuilder = TransactionBuilder
this.TransactionBuilder.setAddress(this.Address)
this.Util = new Util(libConfig)
this.Socket = Socket
this.Schnorr = new Schnorr(libConfig)
this.SLP = new SLP(libConfig)
this.SLP.HDNode = this.HDNode
this.IPFS = new IPFS()
this.Utxo = new Utxo(libConfig)
this.Transaction = new Transaction(libConfig)
this.DSProof = new DSProof(libConfig)
}
}
+58 -48
View File
@@ -5,7 +5,7 @@
const axios = require('axios')
let _this
// let _this
class Blockchain {
constructor (config) {
@@ -29,7 +29,7 @@ class Blockchain {
}
}
_this = this
// _this = this
}
/**
@@ -54,7 +54,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBestBlockHash`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -68,8 +68,8 @@ class Blockchain {
* @apiName getBlock
* @apiGroup Blockchain
* @apiDescription
* If verbose is false, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is true, returns an Object with information about block hash.
*
* If verbose is 0, returns a string that is serialized, hex-encoded data for block 'hash'. If verbose is 1, returns an Object with information about block hash.
* If verbose is 2, returns an Object with information about block hash and information about tx.
* @apiExample Example usage:
* (async () => {
* try {
@@ -80,29 +80,39 @@ class Blockchain {
* }
* })()
*
* // { hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
* // confirmations: 528236,
* // size: 216,
* // height: 1000,
* // version: 1,
* // versionHex: '00000001',
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
* // tx:
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
* // time: 1232346882,
* // mediantime: 1232344831,
* // nonce: 2595206198,
* // bits: '1d00ffff',
* // difficulty: 1,
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6' }
* // {
* // hash: '00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09',
* // confirmations: 528236,
* // size: 216,
* // height: 1000,
* // version: 1,
* // versionHex: '00000001',
* // merkleroot: 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
* // tx:
* // [ 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33' ],
* // time: 1232346882,
* // mediantime: 1232344831,
* // nonce: 2595206198,
* // bits: '1d00ffff',
* // difficulty: 1,
* // chainwork: '000000000000000000000000000000000000000000000000000003e903e903e9',
* // previousblockhash: '0000000008e647742775a230787d66fdf92c46a48c896bfbc85cdc8acc67e87d',
* // nextblockhash: '00000000a2887344f8db859e372e7e4bc26b23b9de340f725afbf2edb265b4c6'
* // }
*/
async getBlock (blockhash, verbose = true) {
async getBlock (blockhash, verbosity = 1) {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBlock/${blockhash}?verbose=${verbose}`,
_this.axiosOptions
// Input validation
if (!blockhash || typeof blockhash !== 'string') {
throw new Error('blockhash must be a string')
}
const response = await axios.post(
`${this.restURL}blockchain/getblock`,
{
blockhash,
verbosity
},
this.axiosOptions
)
return response.data
} catch (error) {
@@ -152,7 +162,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBlockchainInfo`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -183,7 +193,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBlockCount`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -216,7 +226,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBlockHash/${height}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -263,7 +273,7 @@ class Blockchain {
if (typeof hash === 'string') {
const response = await axios.get(
`${this.restURL}blockchain/getBlockHeader/${hash}?verbose=${verbose}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -277,7 +287,7 @@ class Blockchain {
hashes: hash,
verbose: verbose
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -324,7 +334,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getChainTips`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -356,7 +366,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getDifficulty`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -372,7 +382,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getMempoolAncestors/${txid}?verbose=${verbose}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -387,7 +397,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getMempoolDescendants/${txid}?verbose=${verbose}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -484,7 +494,7 @@ class Blockchain {
if (typeof txid === 'string') {
const response = await axios.get(
`${this.restURL}blockchain/getMempoolEntry/${txid}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -495,7 +505,7 @@ class Blockchain {
{
txids: txid
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -535,7 +545,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getMempoolInfo`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -582,7 +592,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/getRawMempool?vebose=${verbose}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -626,7 +636,7 @@ class Blockchain {
// Send the request to the REST API.
// const response = await axios.get(
// `${this.restURL}blockchain/getTxOut/${txid}/${n}?includeMempool=${includeMempool}`,
// _this.axiosOptions
// this.axiosOptions
// )
const response = await axios.post(
`${this.restURL}blockchain/getTxOut`,
@@ -635,7 +645,7 @@ class Blockchain {
vout: n,
mempool: includeMempool
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -688,7 +698,7 @@ class Blockchain {
const path = `${this.restURL}blockchain/getTxOutProof/${txids}`
// if (blockhash) path = `${path}?blockhash=${blockhash}`
const response = await axios.get(path, _this.axiosOptions)
const response = await axios.get(path, this.axiosOptions)
return response.data
// Array of txids.
@@ -699,7 +709,7 @@ class Blockchain {
{
txids: txids
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -716,7 +726,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/preciousBlock/${blockhash}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -729,7 +739,7 @@ class Blockchain {
try {
const response = await axios.post(
`${this.restURL}blockchain/pruneBlockchain/${height}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -742,7 +752,7 @@ class Blockchain {
try {
const response = await axios.get(
`${this.restURL}blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -794,7 +804,7 @@ class Blockchain {
if (typeof proof === 'string') {
const response = await axios.get(
`${this.restURL}blockchain/verifyTxOutProof/${proof}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -806,7 +816,7 @@ class Blockchain {
{
proofs: proof
},
_this.axiosOptions
this.axiosOptions
)
return response.data
+4 -4
View File
@@ -4,7 +4,7 @@
const axios = require('axios')
let _this // Global reference to the instance of this class.
// let _this // Global reference to the instance of this class.
class Control {
constructor (config) {
@@ -28,7 +28,7 @@ class Control {
}
}
_this = this
// _this = this
}
/**
@@ -81,7 +81,7 @@ class Control {
try {
const response = await axios.get(
`${this.restURL}control/getNetworkInfo`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -94,7 +94,7 @@ class Control {
try {
const response = await axios.get(
`${this.restURL}control/getMemoryInfo`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
+3 -3
View File
@@ -22,7 +22,7 @@ class Crypto {
* // buffer from hex
* let buffer = Buffer.from('03123464075c7a5fa6b8680afa2c962a02e7bf071c6b2395b0ac711d462cac9354', 'hex')
* bchjs.Crypto.sha256(buffer)
* // <Buffer 26 b0 78 0a 68 3a 1e 09 8e 9c b8 cf a1 b0 92 42 28 25 00 97>
* // <Buffer 97 8c 09 dd 46 09 1d 19 22 fa 01 e9 f4 a9 75 b9 1a 37 1f 26 ba 83 99 de 27 d5 38 01 15 21 21 de>
*
* */
// Translate address from any address format into a specific format.
@@ -60,7 +60,7 @@ class Crypto {
* @api Crypto.hash256() hash256()
* @apiName hash256
* @apiGroup Crypto
* @apiDescription Utility for creating double sha256 hash digests of data.
* @apiDescription Utility for creating double sha256 hash digests of buffer encoded data.
*
* @apiExample Example usage:
* // buffer from hex
@@ -86,7 +86,7 @@ class Crypto {
* @api Crypto.hash160() hash160()
* @apiName hash160
* @apiGroup Crypto
* @apiDescription Utility for creating ripemd160(sha256()) hash digests of data.
* @apiDescription Utility for creating ripemd160(sha256()) hash digests of buffer encoded data.
*
* @apiExample Example usage:
* // buffer from hex
+81
View File
@@ -0,0 +1,81 @@
const axios = require('axios')
let _this
class DSProof {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
this.axios = axios
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
}
}
_this = this
}
/**
* @api DSProof.getDSProof() getDSProof()
* @apiName getDSProof
* @apiGroup DSProof
* @apiDescription Checks if a transaction generated a double-spend proof.
*
* If a double-spend is attempted, one of the transactions will generate a
* 'double spend proof'. This call can be used to check if a transaction
* generated such a proof.
*
* Merchants should wait 3-5 seconds after receiving notification of a
* transaction before calling this endpoint, to see if the TXID generated a
* proof. If this method returns no data, then the TX can be considered 'safe'
* and not a double spend. If proof data is returned by this method, then
* the transaction generated a proof and can be considered a 'double spend'.
*
* @apiExample Example usage:
* (async () => {
* try {
* const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
* const result = await bchjs.DSProof.getDSProof(txid)
* console.log(result);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* null
*/
async getDSProof (txid) {
try {
if (!txid) {
throw new Error('txid is required')
}
if (txid.length !== 64) {
throw new Error(`txid must be of length 64 (not ${txid.length})`)
}
const response = await _this.axios.get(
`${this.restURL}dsproof/getdsproof/${txid}`,
this.axiosOptions
)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = DSProof
+19 -18
View File
@@ -8,7 +8,7 @@ const axios = require('axios')
// Local libraries.
const Blockchain = require('./blockchain')
let _this
// let _this
class ElectrumX {
constructor (config) {
@@ -34,7 +34,7 @@ class ElectrumX {
this.blockchain = new Blockchain(config)
_this = this
// _this = this
}
/**
@@ -108,7 +108,7 @@ class ElectrumX {
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/utxos/${address}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -119,7 +119,7 @@ class ElectrumX {
{
addresses: address
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -142,7 +142,7 @@ class ElectrumX {
* (async () => {
* try {
* let balance = await bchjs.Electrumx.balance('bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf');
* console.log(utxo);
* console.log(balance);
* } catch(error) {
* console.error(error)
* }
@@ -159,7 +159,7 @@ class ElectrumX {
* (async () => {
* try {
* let balance = await bchjs.Electrumx.balance(['bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf', 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v']);
* console.log(utxo);
* console.log(balance);
* } catch(error) {
* console.error(error)
* }
@@ -192,7 +192,7 @@ class ElectrumX {
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/balance/${address}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -203,7 +203,7 @@ class ElectrumX {
{
addresses: address
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -284,13 +284,13 @@ class ElectrumX {
* }
*
*/
async transactions (address) {
async transactions (address, usrObj = null) {
try {
// Handle single address.
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/transactions/${address}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -299,9 +299,10 @@ class ElectrumX {
const response = await axios.post(
`${this.restURL}electrumx/transactions`,
{
addresses: address
addresses: address,
usrObj // pass user data when making an internal call.
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -382,7 +383,7 @@ class ElectrumX {
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/unconfirmed/${address}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -393,7 +394,7 @@ class ElectrumX {
{
addresses: address
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -451,7 +452,7 @@ class ElectrumX {
try {
const response = await axios.get(
`${this.restURL}electrumx/block/headers/${height}?count=${count}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -542,7 +543,7 @@ class ElectrumX {
if (typeof txid === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/tx/data/${txid}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} else if (Array.isArray(txid)) {
@@ -551,7 +552,7 @@ class ElectrumX {
{
txids: txid
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -591,7 +592,7 @@ class ElectrumX {
const response = await axios.post(
`${this.restURL}electrumx/tx/broadcast`,
{ txHex },
_this.axiosOptions
this.axiosOptions
)
return response.data
+4 -2
View File
@@ -35,11 +35,13 @@ class Encryption {
// Search the blockchain for a public key associated with a BCH address.
async getPubKey (addr) {
try {
if (!addr || typeof addr !== 'string') { throw new Error('Input must be a valid Bitcoin Cash address.') }
if (!addr || typeof addr !== 'string') {
throw new Error('Input must be a valid Bitcoin Cash address.')
}
const response = await _this.axios.get(
`${this.restURL}encryption/publickey/${addr}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
+3 -3
View File
@@ -1,6 +1,6 @@
const axios = require('axios')
let _this
// let _this
class Generating {
constructor (config) {
@@ -24,14 +24,14 @@ class Generating {
}
}
_this = this
// _this = this
}
async generateToAddress (blocks, address, maxtries = 1000000) {
try {
const response = await axios.post(
`${this.restURL}generating/generateToAddress/${blocks}/${address}?maxtries=${maxtries}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
+4 -2
View File
@@ -1,6 +1,6 @@
const Bitcoin = require('@psf/bitcoincashjs-lib')
const coininfo = require('@psf/coininfo')
const bip32utils = require('@psf/bip32-utils')
const bip32utils = require('@chris.troutner/bip32-utils')
const bchaddrjs = require('bchaddrjs-slp')
class HDNode {
@@ -34,7 +34,9 @@ class HDNode {
*/
fromSeed (rootSeedBuffer, network = 'mainnet') {
let bitcoincash
if (network === 'bitcoincash' || network === 'mainnet') { bitcoincash = coininfo.bitcoincash.main } else bitcoincash = coininfo.bitcoincash.test
if (network === 'bitcoincash' || network === 'mainnet') {
bitcoincash = coininfo.bitcoincash.main
} else bitcoincash = coininfo.bitcoincash.test
const bitcoincashBitcoinJSLib = bitcoincash.toBitcoinJS()
return Bitcoin.HDNode.fromSeedBuffer(
+6 -6
View File
@@ -1,6 +1,6 @@
const axios = require('axios')
let _this
// let _this
class Mining {
constructor (config) {
@@ -24,14 +24,14 @@ class Mining {
}
}
_this = this
// _this = this
}
async getBlockTemplate (templateRequest) {
try {
const response = await axios.get(
`${this.restURL}mining/getBlockTemplate/${templateRequest}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -44,7 +44,7 @@ class Mining {
try {
const response = await axios.get(
`${this.restURL}mining/getMiningInfo`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -57,7 +57,7 @@ class Mining {
try {
const response = await axios.get(
`${this.restURL}mining/getNetworkHashps?nblocks=${nblocks}&height=${height}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
} catch (error) {
@@ -71,7 +71,7 @@ class Mining {
if (parameters) path = `${path}?parameters=${parameters}`
try {
const response = await axios.post(path, _this.axiosOptions)
const response = await axios.post(path, this.axiosOptions)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
+10 -10
View File
@@ -5,7 +5,7 @@
const axios = require('axios')
let _this
// let _this
class Ninsight {
constructor (config) {
@@ -27,7 +27,7 @@ class Ninsight {
}
}
_this = this
// _this = this
}
/**
@@ -75,7 +75,7 @@ class Ninsight {
{
addresses: [address]
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -86,7 +86,7 @@ class Ninsight {
{
addresses: address
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -143,7 +143,7 @@ class Ninsight {
{
addresses: [address]
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -153,7 +153,7 @@ class Ninsight {
{
addresses: address
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -219,7 +219,7 @@ class Ninsight {
{
addresses: [address]
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -229,7 +229,7 @@ class Ninsight {
{
addresses: address
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -292,7 +292,7 @@ class Ninsight {
{
txids: [txid]
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -302,7 +302,7 @@ class Ninsight {
{
txids: txid
},
_this.axiosOptions
this.axiosOptions
)
return response.data
+85 -11
View File
@@ -1,10 +1,10 @@
const axios = require('axios')
let _this
// let _this
class Price {
constructor (config) {
_this = this
// _this = this
this.restURL = config.restURL
this.apiToken = config.apiToken
@@ -58,7 +58,7 @@ class Price {
* let current = await bchjs.Price.getUsd();
* console.log(current);
* } catch(err) {
* console.err(err)
* console.error(err)
* }
*})()
*
@@ -68,7 +68,7 @@ class Price {
try {
const response = await this.axios.get(
`${this.restURL}price/usd`,
_this.axiosOptions
this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
@@ -94,7 +94,7 @@ class Price {
* let current = await bchjs.Price.rates();
* console.log(current);
* } catch(err) {
* console.err(err)
* console.error(err)
* }
*})()
*
@@ -111,7 +111,7 @@ class Price {
try {
const response = await this.axios.get(
`${this.restURL}price/rates`,
_this.axiosOptions
this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
@@ -127,8 +127,9 @@ class Price {
* @apiName Price getBchaUsd()
* @apiGroup Price
* @apiDescription Return current price of BCHA in USD.
* This endpoint gets the USD price of BCHA from the Coinex API. The price
* comes from bch-api, so it has a better chance of working in Tor.
* This endpoint gets the USD price of XEC from the Coinex API. The price
* denominated in BCHA comes from bch-api, so it has a better chance of
* working in Tor.
*
* @apiExample Example usage:
*(async () => {
@@ -136,17 +137,90 @@ class Price {
* let current = await bchjs.Price.getBchaUsd();
* console.log(current);
* } catch(err) {
* console.err(err)
* console.error(err)
* }
*})()
*
* // 18.81
* // 212.34
*/
async getBchaUsd () {
try {
const response = await this.axios.get(
`${this.restURL}price/bchausd`,
_this.axiosOptions
this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
const bchaPrice = response.data.usd * 1000000
// Convert XEC denomination to BCHA denomination
return bchaPrice
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.getXecUsd() getXecUsd()
* @apiName Price getXecUsd()
* @apiGroup Price
* @apiDescription Return current price of XEC in USD.
* This endpoint gets the USD price of XEC from the Coinex API. The price
* comes from bch-api, so it has a better chance of working in Tor.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getXecUsd();
* console.log(current);
* } catch(err) {
* console.error(err)
* }
*})()
*
* // 0.00021234
*/
async getXecUsd () {
try {
const response = await this.axios.get(
`${this.restURL}price/bchausd`,
this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.usd
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
/**
* @api price.getBchUsd() getBchUsd()
* @apiName Price getBchUsd()
* @apiGroup Price
* @apiDescription Return current price of BCH in USD.
* This endpoint gets the USD price of BCH from the Coinex API. The price
* comes from bch-api, so it has a better chance of working in Tor.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getBchUsd();
* console.log(current);
* } catch(err) {
* console.error(err)
* }
*})()
*
* // 512.81
*/
async getBchUsd () {
try {
const response = await this.axios.get(
`${this.restURL}price/bchusd`,
this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
+56 -110
View File
@@ -1,8 +1,6 @@
const axios = require('axios')
const SlpUtils = require('./slp/utils')
let _this
// let _this
class RawTransactions {
constructor (config) {
@@ -29,10 +27,7 @@ class RawTransactions {
// Encapsulate dependencies
this.axios = axios
// Dependencies
this.slpUtils = new SlpUtils(config)
_this = this
// this = this
}
/**
@@ -103,7 +98,7 @@ class RawTransactions {
if (typeof hex === 'string') {
const response = await axios.get(
`${this.restURL}rawtransactions/decodeRawTransaction/${hex}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -116,9 +111,7 @@ class RawTransactions {
data: {
hexes: hex
},
headers: {
authorization: `Token ${_this.apiToken}`
}
headers: this.axiosOptions.headers
}
const response = await axios(options)
@@ -127,7 +120,8 @@ class RawTransactions {
throw new Error('Input must be a string or array of strings.')
} catch (error) {
if (error.response && error.response.data) throw error.response.data
if (error.error) throw new Error(error.error)
else if (error.response && error.response.data) throw error.response.data
else throw error
}
}
@@ -172,7 +166,7 @@ class RawTransactions {
if (typeof script === 'string') {
const response = await axios.get(
`${this.restURL}rawtransactions/decodeScript/${script}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -183,9 +177,7 @@ class RawTransactions {
data: {
hexes: script
},
headers: {
authorization: `Token ${this.apiToken}`
}
headers: this.axiosOptions.headers
}
const response = await axios(options)
@@ -194,7 +186,8 @@ class RawTransactions {
throw new Error('Input must be a string or array of strings.')
} catch (error) {
if (error.response && error.response.data) throw error.response.data
if (error.error) throw new Error(error.error)
else if (error.response && error.response.data) throw error.response.data
else throw error
}
}
@@ -263,12 +256,16 @@ class RawTransactions {
* // time: 1547752564,
* // blocktime: 1547752564 } ]
*/
async getRawTransaction (txid, verbose = false) {
async getRawTransaction (txid, verbose = false, usrObj = null) {
try {
if (typeof txid === 'string') {
// console.log(
// 'getRawTransaction() this.axiosOptions: ',
// this.axiosOptions
// )
const response = await axios.get(
`${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=${verbose}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -278,9 +275,10 @@ class RawTransactions {
url: `${this.restURL}rawtransactions/getRawTransaction`,
data: {
txids: txid,
verbose: verbose
verbose: verbose,
usrObj // pass user data when making an internal call.
},
headers: _this.axiosOptions.headers
headers: this.axiosOptions.headers
}
const response = await axios(options)
@@ -289,8 +287,14 @@ class RawTransactions {
throw new Error('Input must be a string or array of strings.')
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
if (error.error) throw new Error(error.error)
// This case handles rate limit errors.
if (error.response && error.response.data && error.response.data.error) {
throw new Error(error.response.data.error)
} else if (error.response && error.response.data) {
throw error.response.data
} else throw error
}
}
@@ -315,6 +319,9 @@ class RawTransactions {
const inputTxid = vin.txid
const inputVout = vin.vout
// TODO: Coinbase TXs have no input transaction. Figure out how to
// handle this corner case.
// Get the TX details for the input, in order to retrieve the address of
// the sender.
const txDetailsParent = await this.getRawTransaction(inputTxid, true)
@@ -327,14 +334,21 @@ class RawTransactions {
retArray.push({
vin: i,
address: voutSender.scriptPubKey.addresses[0]
address: voutSender.scriptPubKey.addresses[0],
value: voutSender.value
})
}
return retArray
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
if (error.error) throw new Error(error.error)
// This case handles rate limit errors.
if (error.response && error.response.data && error.response.data.error) {
throw new Error(error.response.data.error)
} else if (error.response && error.response.data) {
throw error.response.data
} else throw error
}
}
@@ -368,7 +382,9 @@ class RawTransactions {
async getTxData (txid) {
try {
if (typeof txid !== 'string') {
throw new Error('Input must be a string or array of strings.')
throw new Error(
'Input to raw-transaction.js/getTxData() must be a string containg a TXID.'
)
}
// Get the TX details for the transaction under consideration.
@@ -381,90 +397,19 @@ class RawTransactions {
// Add the input address to the transaction data.
for (let i = 0; i < inAddrs.length; i++) {
txDetails.vin[i].address = inAddrs[i].address
txDetails.vin[i].value = inAddrs[i].value
}
return txDetails
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
if (error.error) throw new Error(error.error)
// Wraps getTxData(), but also appends SLP token information to each input
// and output of the transaction. This is a very API-heavy call.
// Returns false if the txid is not an SLP transaction.
//
// Warning! This is a prototype function and the output can change at any time
// without reflecting a change in the semantic version. DO NOT USE IN PRODUCTION.
async getTxDataSlp (txid) {
try {
if (typeof txid !== 'string') {
throw new Error('Input must be a string or array of strings.')
}
const txDetails = await this.getTxData(txid)
// First get the token information for the output. If that fails, then
// this is not an SLP transaction, and this method can return false.
let outTokenData
try {
outTokenData = await this.slpUtils.decodeOpReturn(txid)
// console.log(`outTokenData: ${JSON.stringify(outTokenData, null, 2)}`)
// Add token information to the tx details object.
txDetails.tokenTxType = outTokenData.txType
txDetails.tokenId = outTokenData.tokenId
// Add the token quantity to each output.
for (let i = 0; i < outTokenData.amounts.length; i++) {
txDetails.vout[i + 1].tokenQty = outTokenData.amounts[i]
}
// Loop through each input and retrieve the token data.
for (let i = 0; i < txDetails.vin.length; i++) {
const thisVin = txDetails.vin[i]
try {
// If decodeOpReturn() throws an error, then this input is not
// from an SLP transaction and can be ignored.
const inTokenData = await this.slpUtils.decodeOpReturn(thisVin.txid)
// console.log(
// `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
// )
// Get the appropriate vout token amount. This may throw an error,
// which means this Vin is not actually a token UTXO, it was just
// associated with a previous token TX.
const tokenQty = inTokenData.amounts[thisVin.vout - 1]
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
if (tokenQty) {
thisVin.tokenQty = tokenQty
// txDetails.vin[i].tokenQty = tokenQty
} else {
thisVin.tokenQty = null
}
} catch (err) {
// console.log('catch 2: ', err)
// If decodeOpReturn() throws an error, then this input is not
// from an SLP transaction and can be ignored.
// thisVin.tokenQty = null
thisVin.tokenQty = null
continue
}
// console.log(
// `2: txDetails.vin[i]: ${JSON.stringify(txDetails.vin[i], null, 2)}`
// )
}
} catch (err) {
// console.log('catch 1: ', err)
return false
}
return txDetails
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
// This case handles rate limit errors.
if (error.response && error.response.data && error.response.data.error) {
throw new Error(error.response.data.error)
} else if (error.response && error.response.data) {
throw error.response.data
} else throw error
}
}
@@ -506,7 +451,7 @@ class RawTransactions {
if (typeof hex === 'string') {
const response = await this.axios.get(
`${this.restURL}rawtransactions/sendRawTransaction/${hex}`,
_this.axiosOptions
this.axiosOptions
)
if (response.data === '66: insufficient priority') {
@@ -527,16 +472,17 @@ class RawTransactions {
data: {
hexes: hex
},
headers: _this.axiosOptions.headers
headers: this.axiosOptions.headers
}
const response = await _this.axios(options)
const response = await this.axios(options)
return response.data
}
throw new Error('Input hex must be a string or array of strings.')
} catch (error) {
if (error.response && error.response.data) throw error.response.data
if (error.error) throw new Error(error.error)
else if (error.response && error.response.data) throw error.response.data
else throw error
}
}
+100 -2
View File
@@ -8,16 +8,21 @@
(Parent) token.
*/
// Public npm libraries
const axios = require('axios')
// Local libraries.
const Address = require('./address')
// const BigNumber = require('bignumber.js')
const slpMdm = require('slp-mdm')
// let _this
// const addy = new Address()
let addy
const TransactionBuilder = require('../transaction-builder')
class TokenType1 {
class Nft1 {
constructor (config) {
this.restURL = config.restURL
@@ -25,6 +30,8 @@ class TokenType1 {
// Instantiate the transaction builder.
TransactionBuilder.setAddress(addy)
// _this = this
}
/**
@@ -412,6 +419,97 @@ class TokenType1 {
throw err
}
}
/**
* @api SLP.NFT1.listNFTGroupChildren() listNFTGroupChildren()
* @apiName listNFTGroupChildren
* @apiGroup SLP NFT1
* @apiDescription Return list of NFT children tokens in a NFT Group.
* It's assumed provided groupId parameter is for an NFT Group token (type=129)
*
* Returns an Array with GENESIS transaction IDs of the children tokens.
*
* @apiExample Example usage:
*
* const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
* const children = await bchjs.SLP.Nft1.listNFTGroupChildren(groupId)
*
* children = {
* "nftChildren": [
* "45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9",
* "928ce61fe1006b1325a0ba0dce700bf83986a6f0691ba26e121c9ac035d12a55"
* ]
* }
*/
async listNFTGroupChildren (groupId) {
try {
if (typeof groupId === 'string') {
const response = await axios.get(
`${this.restURL}slp/nftChildren/${groupId}`,
this.axiosOptions
)
return response.data
}
throw new Error('groupId must be a string.')
} catch (error) {
// console.log('Error in listNFTGroupChildren()')
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api SLP.NFT1.parentNFTGroup() parentNFTGroup()
* @apiName parentNFTGroup
* @apiGroup SLP NFT1
* @apiDescription Return parent NFT Group information for a given NFT child token.
* It's assumed provided groupId parameter is for an NFT Child token (type=65)
*
* Returns a JSON with NFT group information.
*
* @apiExample Example usage:
*
* const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
* const group = await bchjs.SLP.Nft1.parentNFTGroup(tokenId)
*
* group = {
* "nftGroup": {
* "decimals": 0,
* "timestamp": "2021-05-03 10:36:01",
* "timestamp_unix": 1620038161,
* "versionType": 129,
* "documentUri": "psfoundation.cash",
* "symbol": "PSF.TEST.GROUP",
* "name": "PSF Test NFT Group",
* "containsBaton": true,
* "id": "68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a",
* "documentHash": null,
* "initialTokenQty": 1000000,
* "blockCreated": 686117,
* "totalMinted": null,
* "totalBurned": null,
* "circulatingSupply": null
* }
* }
*/
async parentNFTGroup (tokenId) {
try {
if (typeof tokenId === 'string') {
const response = await axios.get(
`${this.restURL}slp/nftGroup/${tokenId}`,
this.axiosOptions
)
return response.data
}
throw new Error('tokenId must be a string.')
} catch (error) {
// console.log('Error in parentNFTGroup()')
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = TokenType1
module.exports = Nft1
+1 -1
View File
@@ -41,7 +41,7 @@ class SLP {
this.Address = new Address(config)
this.ECPair = ECPair
this.TokenType1 = new TokenType1(config)
this.NFT1 = new NFT1(this.restURL)
this.NFT1 = new NFT1(config)
this.Utils = new Utils(config)
}
}
+20 -14
View File
@@ -118,20 +118,20 @@ class TokenType1 {
// Convert the send quantity to the format expected by slp-mdm.
const baseQty = sendQtyBig.toString()
console.log('baseQty: ', baseQty)
// console.log('baseQty: ', baseQty)
// Convert the change quantity to the format expected by slp-mdm.
const baseChange = change.toString()
console.log('baseChange: ', baseChange)
// console.log('baseChange: ', baseChange)
// Check for potential burns
const outputQty = new BigNumber(baseChange).plus(
new BigNumber(baseQty)
)
const outputQty = new BigNumber(baseChange).plus(new BigNumber(baseQty))
const inputQty = new BigNumber(totalTokens)
const tokenOutputDelta = outputQty.minus(inputQty).toString() !== '0'
if (tokenOutputDelta) {
throw new Error('Token transaction inputs do not match outputs, cannot send transaction')
throw new Error(
'Token transaction inputs do not match outputs, cannot send transaction'
)
}
// Generate the OP_RETURN as a Buffer.
@@ -152,7 +152,9 @@ class TokenType1 {
const tokenSingleOutputError =
noChangeOutputQty.minus(noChangeInputQty).toString() !== '0'
if (tokenSingleOutputError) {
throw new Error('Token transaction inputs do not match outputs, cannot send transaction')
throw new Error(
'Token transaction inputs do not match outputs, cannot send transaction'
)
}
// Generate the OP_RETURN as a Buffer.
@@ -285,9 +287,15 @@ class TokenType1 {
try {
// TODO: Add input validation.
let baseQty = new BigNumber(configObj.initialQty).times(
10 ** configObj.decimals
)
let baseQty
if (configObj.decimals !== 0) {
baseQty = new BigNumber(configObj.initialQty).times(
10 ** configObj.decimals
)
} else {
baseQty = new BigNumber(configObj.initialQty)
}
baseQty = baseQty.absoluteValue()
baseQty = Math.floor(baseQty)
baseQty = baseQty.toString()
@@ -378,9 +386,7 @@ class TokenType1 {
// Throw an error if the minting baton could not be found.
if (!mintBatonUtxo) {
throw new Error(
'Minting baton could not be found in tokenUtxos array.'
)
throw new Error('Minting baton could not be found in tokenUtxos array.')
}
const tokenId = mintBatonUtxo.tokenId
@@ -454,7 +460,7 @@ class TokenType1 {
const result = await _this.axios.post(
`${this.restURL}slp/generatesendopreturn`,
data,
_this.axiosOptions
this.axiosOptions
)
const slpSendObj = result.data
+253 -78
View File
@@ -1,12 +1,12 @@
/* eslint-disable no-useless-catch */
// Public npm libraries
const axios = require('axios')
const slpParser = require('slp-parser')
const BigNumber = require('bignumber.js')
// const Script = require("../script")
// const scriptLib = new Script()
// const BigNumber = require("bignumber.js")
// Local libraries
const Util = require('../util')
let _this
@@ -37,6 +37,8 @@ class Utils {
_this = this
this.whitelist = []
this.util = new Util(config)
}
/**
@@ -215,14 +217,14 @@ class Utils {
try {
let response
if (method === 'get') {
response = await _this.axios.get(path, _this.axiosOptions)
response = await _this.axios.get(path, this.axiosOptions)
} else {
response = await _this.axios.post(
path,
{
tokenIds: id
},
_this.axiosOptions
this.axiosOptions
)
}
return response.data
@@ -313,7 +315,7 @@ class Utils {
if (typeof address === 'string') {
const path = `${this.restURL}slp/balancesForAddress/${address}`
const response = await _this.axios.get(path, _this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
return response.data
// Array of addresses.
@@ -326,7 +328,7 @@ class Utils {
{
addresses: address
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -378,7 +380,7 @@ class Utils {
try {
const path = `${this.restURL}slp/balancesForToken/${tokenId}`
const response = await _this.axios.get(path, _this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
@@ -436,10 +438,11 @@ class Utils {
// will be like the examples above. If SLPDB has fallen behind real-time
// processing, it will return this output:
// [ null ]
async validateTxid (txid) {
async validateTxid (txid, usrObj = null) {
const path = `${this.restURL}slp/validateTxid`
// console.log(`txid: ${JSON.stringify(txid, null, 2)}`)
// console.log(`validateTxid usrObj: ${JSON.stringify(usrObj, null, 2)}`)
// Handle a single TXID or an array of TXIDs.
let txids
@@ -447,12 +450,14 @@ class Utils {
else txids = txid
try {
// console.log('validateTxid() this.axiosOptions: ', this.axiosOptions)
const response = await _this.axios.post(
path,
{
txids: txids
txids: txids,
usrObj // pass user data when making an internal call.
},
_this.axiosOptions
this.axiosOptions
)
// console.log(
// `validateTxid response.data: ${JSON.stringify(response.data, null, 2)}`
@@ -516,7 +521,8 @@ class Utils {
const path = `${this.restURL}slp/validateTxid2/${txid}`
const response = await _this.axios.get(path, _this.axiosOptions)
// console.log('validateTxid2() this.axiosOptions: ', this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
@@ -585,7 +591,7 @@ class Utils {
// Retrieve the whitelist from the REST API if we haven't gotten it yet.
if (this.whitelist.length === 0) {
const response = await _this.axios.get(path, _this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
this.whitelist = response.data
@@ -602,7 +608,8 @@ class Utils {
* @api SLP.Utils.validateTxid3() validateTxid3()
* @apiName validateTxid3
* @apiGroup SLP Utils
* @apiDescription Validate that txid is an SLP transaction using the SLPDB whitelist server.
* @apiDescription
* Validate that txid is an SLP transaction using the SLPDB whitelist server.
* Same exact functionality as the validateTxid() function, but this function
* calls the whitelist SLPDB. It will only validate SLP tokens that are in the
* whitelist. You can retrieve the whitelist with the SLP.Utils.whitelist()
@@ -648,10 +655,12 @@ class Utils {
* '00ea27261196a411776f81029c0ebe34362936b4a9847deb1f7a40a02b3a1476',
* valid: true } ]
*/
async validateTxid3 (txid) {
async validateTxid3 (txid, usrObj = null) {
const path = `${this.restURL}slp/validateTxid3`
// console.log(`txid: ${JSON.stringify(txid, null, 2)}`)
// console.log(`path: ${JSON.stringify(path, null, 2)}`)
// console.log('validateTxid3 usrObj: ', usrObj)
// Handle a single TXID or an array of TXIDs.
let txids
@@ -659,12 +668,14 @@ class Utils {
else txids = txid
try {
// console.log('validateTxid3() this.axiosOptions: ', this.axiosOptions)
const response = await _this.axios.post(
path,
{
txids: txids
txids: txids,
usrObj // pass user data when making an internal call.
},
_this.axiosOptions
this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
@@ -672,6 +683,14 @@ class Utils {
return validatedTxids
} catch (error) {
// console.log('validateTxid3 error: ', error)
// This case handles rate limit errors.
if (error.response && error.response.data && error.response.data.error) {
throw new Error(error.response.data.error)
}
// Not sure if this can be safely deprecated?
if (error.response && error.response.data) throw error.response.data
throw error
}
@@ -716,7 +735,7 @@ class Utils {
try {
const path = `${this.restURL}slp/tokenStats/${tokenId}`
const response = await _this.axios.get(path, _this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
return response.data
} catch (error) {
@@ -787,7 +806,7 @@ class Utils {
try {
const path = `${this.restURL}slp/transactions/${tokenId}/${address}`
const response = await _this.axios.get(path, _this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
return response.data
} catch (error) {
@@ -827,7 +846,7 @@ class Utils {
try {
const path = `${this.restURL}slp/burnTotal/${transactionId}`
const response = await _this.axios.get(path, _this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
return response.data
} catch (error) {
@@ -871,7 +890,7 @@ class Utils {
// console.log(`this.restURL: ${this.restURL}`)
const path = `${this.restURL}slp/txDetails/${txid}`
const response = await _this.axios.get(path, _this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
@@ -891,6 +910,9 @@ class Utils {
* If optional associative array parameter cache is used, will cache and
* reuse responses for the same input.
*
* A third optional input, `usrObj`, is used by bch-api for managing rate limits.
* It can be safely ignored when writing apps using this call.
*
*
* @apiExample Example usage:
*
@@ -899,7 +921,7 @@ class Utils {
* const txid =
* "266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1"
*
* const data = await slp.Utils.decodeOpReturn2(txid)
* const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
*
* console.log(`Decoded OP_RETURN data: ${JSON.stringify(data,null,2)}`)
* } catch (error) {
@@ -920,7 +942,7 @@ class Utils {
*
*/
// Reimplementation of decodeOpReturn() using slp-parser.
async decodeOpReturn (txid, cache = null) {
async decodeOpReturn (txid, cache = null, usrObj = 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
@@ -937,16 +959,28 @@ class Utils {
if (cachedVal) return cachedVal
}
// console.log(`decodeOpReturn usrObj: ${JSON.stringify(usrObj, null, 2)}`)
try {
// Validate the txid input.
if (!txid || txid === '' || typeof txid !== 'string') {
throw new Error('txid string must be included.')
}
// CT: 2/24/21 Deprected GET in favor of POST, to pass IP address.
// Retrieve the transaction object from the full node.
const path = `${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=true`
const response = await _this.axios.get(path, _this.axiosOptions)
const txDetails = response.data
const path = `${this.restURL}rawtransactions/getRawTransaction`
// console.log('decodeOpReturn() this.axiosOptions: ', this.axiosOptions)
const response = await this.axios.post(
path,
{
verbose: true,
txids: [txid],
usrObj // pass user data when making an internal call.
},
this.axiosOptions
)
const txDetails = response.data[0]
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
// SLP spec expects OP_RETURN to be the first output of the transaction.
@@ -993,8 +1027,16 @@ class Utils {
return tokenData
} catch (error) {
// Used for debugging
// console.log('decodeOpReturn error: ', error)
if (error.response && error.response.data) throw error.response.data
// console.log(`decodeOpReturn error.message: ${error.message}`)
// if (error.response && error.response.data) {
// console.log(
// `decodeOpReturn error.response.data: ${JSON.stringify(
// error.response.data
// )}`
// )
// }
throw error
}
}
@@ -1007,15 +1049,30 @@ class Utils {
*
* 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.
*
* - 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 the `isValid` property is `true`, the UTXO is a valid SLP UTXO.
* - If the `isValid` property 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.
*
* An optional second input object, `usrObj`, allows the user to inject an
* artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
* a number, the call will delay by that number of milliseconds between
* processing UTXOs.
*
* This is an API-heavy call. If you get a lot of `null` values, then slow down
* the calls by using the usrObj.utxoDelay property, or request info on fewer
* UTXOs at a
* time. `null` indicates that the UTXO can *not* be safely spent, because
* a judgement as to weather it is a token UTXO has not been made. Spending it
* could burn tokens. It's safest to ignore UTXOs with a value of `null`.
*
*
* @apiExample Example usage:
*
@@ -1023,7 +1080,8 @@ class Utils {
* try {
* const utxos = await bchjs.Electrumx.utxo(`bitcoincash:qpcqs0n5xap26un2828n55gan2ylj7wavvzeuwdx05`)
*
* const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
* // Delay 100mS between processing UTXOs, to prevent rate-limit errors.
* const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(utxos, { utxoDelay: 100 })
*
* console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
* } catch (error) {
@@ -1051,16 +1109,13 @@ class Utils {
* "tokenType": 1
* }
*/
// CT 1/11/20: Refactored to comply with this GitHub Issue:
// https://github.com/Bitcoin-com/slp-sdk/issues/84
// CT 5/31/20: Refactored to use slp-parse library.
async tokenUtxoDetails (utxos) {
async tokenUtxoDetails (utxos, usrObj = null) {
try {
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
// console.log(`tokenUtxoDetails usrObj: ${JSON.stringify(usrObj, null, 2)}`)
// Loop through each element in the array and validate the input before
// further processing.
for (let i = 0; i < utxos.length; i++) {
@@ -1092,7 +1147,7 @@ class Utils {
}
// Hydrate each UTXO with data from SLP OP_REUTRNs.
const outAry = await this._hydrateUtxo(utxos)
const outAry = await this._hydrateUtxo(utxos, usrObj)
// console.log(`outAry: ${JSON.stringify(outAry, null, 2)}`)
// *After* each UTXO has been hydrated with SLP data,
@@ -1103,18 +1158,19 @@ class Utils {
// *After* the UTXO has been hydrated with SLP data,
// validate the TXID with SLPDB.
if (utxo.tokenType) {
// Only execute this block if the current UTXO has a 'tokenType'
// Only execute this code-path if the current UTXO has a 'tokenType'
// property. i.e. it has been successfully hydrated with SLP
// information.
// Validate using a 'waterfall' of validators.
utxo.isValid = await this.waterfallValidateTxid(utxo.txid)
// console.log(`isValid: ${JSON.stringify(isValid, null, 2)}`)
utxo.isValid = await this.waterfallValidateTxid(utxo.txid, usrObj)
// console.log(`isValid: ${JSON.stringify(utxo.isValid, null, 2)}`)
}
}
return outAry
} catch (error) {
// console.log('Error in tokenUtxoDetails()')
if (error.response && error.response.data) throw error.response.data
throw error
}
@@ -1131,8 +1187,20 @@ class Utils {
* but much more performant handling of SLP tokens. Some wallet apps prefer
* the scaling performance over the breadth of supported tokens.
*
* An optional second input object, `usrObj`, allows the user to inject an
* artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
* a number, the call will delay by that number of milliseconds between
* processing UTXOs.
*
* This is an API-heavy call. If you get a lot of `null` values, then slow down
* the calls by using the usrObj.utxoDelay property, or request info on fewer
* UTXOs at a
* time. `null` indicates that the UTXO can *not* be safely spent, because
* a judgement as to weather it is a token UTXO has not been made. Spending it
* could burn tokens. It's safest to ignore UTXOs with a value of `null`.
*
*/
async tokenUtxoDetailsWL (utxos) {
async tokenUtxoDetailsWL (utxos, usrObj = null) {
try {
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
@@ -1168,7 +1236,7 @@ class Utils {
}
// Hydrate each UTXO with data from SLP OP_REUTRNs.
const outAry = await this._hydrateUtxo(utxos)
const outAry = await this._hydrateUtxo(utxos, usrObj)
// console.log(`outAry: ${JSON.stringify(outAry, null, 2)}`)
// *After* each UTXO has been hydrated with SLP data,
@@ -1184,7 +1252,7 @@ class Utils {
// information.
// Validate against the whitelist SLPDB.
const whitelistResult = await this.validateTxid3(utxo.txid)
const whitelistResult = await this.validateTxid3(utxo.txid, usrObj)
// console.log(
// `whitelist-SLPDB for ${txid}: ${JSON.stringify(
// whitelistResult,
@@ -1212,10 +1280,26 @@ class Utils {
// This is a private function that is called by tokenUtxoDetails().
// It loops through an array of UTXOs and tries to hydrate them with SLP
// token information from the OP_RETURN data.
async _hydrateUtxo (utxos) {
//
// This function makes several calls to decodeOpReturn() to retrieve SLP
// token data. If that call throws an error due to hitting rate limits, this
// function will not throw an error. Instead, it will mark the `isValid`
// property as `null`
//
// Exception to the above: It *will* throw an error if decodeOpReturn() throws
// an error while trying to get the Genesis transaction for a Send or Mint
// transaction. However, that is a rare occurence since the cache of
// decodeOpReturn() will minimize API calls for this case. This behavior
// could be changed, but right now it's a corner case of a corner case.
//
// If the usrObj has a utxoDelay property, then it will delay the loop for
// each UTXO by that many milliseconds.
async _hydrateUtxo (utxos, usrObj = null) {
try {
const decodeOpReturnCache = {}
// console.log(`_hydrateUtxo usrObj: ${JSON.stringify(usrObj, null, 2)}`)
// Output Array
const outAry = []
@@ -1223,15 +1307,28 @@ class Utils {
for (let i = 0; i < utxos.length; i++) {
const utxo = utxos[i]
// If the user passes in a delay, then wait.
if (usrObj && usrObj.utxoDelay && !isNaN(Number(usrObj.utxoDelay))) {
const delayMs = Number(usrObj.utxoDelay)
await this.util.sleep(delayMs)
}
// Get raw transaction data from the full node and attempt to decode
// the OP_RETURN data.
// If there is no OP_RETURN, mark the UTXO as false.
let slpData = false
try {
slpData = await this.decodeOpReturn(utxo.txid, decodeOpReturnCache)
slpData = await this.decodeOpReturn(
utxo.txid,
decodeOpReturnCache,
usrObj // pass user data when making an internal call.
)
// console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
} catch (err) {
// console.log(`error from decodeOpReturn(${utxo.txid}): `, err)
// console.log(
// `error in _hydrateUtxo() from decodeOpReturn(${utxo.txid}): `,
// err
// )
// An error will be thrown if the txid is not SLP.
// If error is for some other reason, like a 429 error, mark utxo as 'null'
@@ -1243,7 +1340,7 @@ class Utils {
err.message.indexOf('trailing data') === -1)
) {
// console.log(
// 'unknown error from decodeOpReturn(). Marking as \'null\'',
// "unknown error from decodeOpReturn(). Marking as 'null'",
// err
// )
@@ -1262,6 +1359,7 @@ class Utils {
// Halt the execution of the loop and increase to the next index.
continue
}
// console.log(`slpData: ${JSON.stringify(slpData, null, 2)}`)
const txType = slpData.txType.toLowerCase()
@@ -1299,6 +1397,10 @@ class Utils {
utxo.decimals = slpData.decimals
utxo.tokenType = slpData.tokenType
// Initial value is null until UTXO can be validated and confirmed
// to be valid (true) or not (false).
utxo.isValid = null
outAry[i] = utxo
}
}
@@ -1318,7 +1420,8 @@ class Utils {
const genesisData = await this.decodeOpReturn(
slpData.tokenId,
decodeOpReturnCache
decodeOpReturnCache,
usrObj // pass user data when making an internal call.
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
@@ -1347,6 +1450,10 @@ class Utils {
utxo.mintBatonVout = slpData.mintBatonVout
// Initial value is null until UTXO can be validated and confirmed
// to be valid (true) or not (false).
utxo.isValid = null
outAry[i] = utxo
}
}
@@ -1370,7 +1477,8 @@ class Utils {
const genesisData = await this.decodeOpReturn(
slpData.tokenId,
decodeOpReturnCache
decodeOpReturnCache,
usrObj // pass user data when making an internal call.
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
@@ -1387,6 +1495,10 @@ class Utils {
utxo.decimals = genesisData.decimals
utxo.tokenType = slpData.tokenType
// Initial value is null until UTXO can be validated and confirmed
// to be valid (true) or not (false).
utxo.isValid = null
// Calculate the real token quantity.
const tokenQtyBig = new BigNumber(tokenQty).div(
@@ -1404,7 +1516,7 @@ class Utils {
return outAry
} catch (error) {
if (error.response && error.response.data) throw error.response.data
// console.log('_hydrateUtxo error: ', error)
throw error
}
}
@@ -1445,9 +1557,10 @@ class Utils {
* // returns
* true
*/
async waterfallValidateTxid (txid) {
async waterfallValidateTxid (txid, usrObj = null) {
try {
// console.log('txid: ', txid)
const cachedTxValidation = {}
// If the value has been cached, use the cached version first.
@@ -1476,7 +1589,7 @@ class Utils {
// validateTxid2() uses slp-validate, which has a different output format.
// Validate against the whitelist SLPDB first.
const whitelistResult = await this.validateTxid3(txid)
const whitelistResult = await this.validateTxid3(txid, usrObj)
// console.log(
// `whitelist-SLPDB for ${txid}: ${JSON.stringify(
// whitelistResult,
@@ -1497,7 +1610,7 @@ class Utils {
}
// Try the general SLPDB, if the whitelist returned null.
const generalResult = await this.validateTxid(txid)
const generalResult = await this.validateTxid(txid, usrObj)
// console.log(
// `validateTxid() isValid: ${JSON.stringify(generalResult, null, 2)}`
// )
@@ -1538,6 +1651,12 @@ class Utils {
// could not be validated.
return isValid
} catch (error) {
// This case handles rate limit errors.
if (error.response && error.response.data && error.response.data.error) {
throw new Error(error.response.data.error)
}
// console.log('Error in waterfallValidateTxid()')
if (error.response && error.response.data) throw error.response.data
throw error
}
@@ -1550,19 +1669,33 @@ class 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.
* which greatly increases the speed, since fewer API calls need to be made.
* However, internal API calls are still counted against your rate limits.
*
* 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.
* This function expects an array of UTXO objects as input. It returns an
* array of equal size. The 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 `isValid` is `true`, the UTXO is a valid SLP UTXO.
* - If `isValid` 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.
*
* An optional second input object, `usrObj`, allows the user to inject an
* artifical delay while processing UTXOs. If `usrObj.utxoDelay` is set to
* a number, the call will delay by that number of milliseconds between
* processing UTXOs.
*
* This is an API-heavy call. If you get a lot of `null` values, then slow down
* the calls by using the usrObj.utxoDelay property, or request info on fewer
* UTXOs at a
* time. `null` indicates that the UTXO can *not* be safely spent, because
* a judgement as to weather it is a token UTXO has not been made. Spending it
* could burn tokens. It's safest to ignore UTXOs with a value of `null`.
*
* @apiExample Example usage:
*
@@ -1574,7 +1707,8 @@ class Utils {
* "bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk"
* ])
*
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
* // Wait 100mS between processing UTXOs, to prevent rate limit errors.
* const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos, { utxoDelay: 100 })
*
* console.log(`${JSON.stringify(utxoInfo, null, 2)}`)
* } catch (error) {
@@ -1708,17 +1842,58 @@ class Utils {
*/
// Same as tokenUtxoDetails(), but reduces API calls by having bch-api server
// do the heavy lifting.
async hydrateUtxos (utxos) {
async hydrateUtxos (utxos, usrObj) {
try {
// Throw error if input is not an array.
if (!Array.isArray(utxos)) throw new Error('Input must be an array.')
const response = await _this.axios.post(
`${this.restURL}slp/hydrateUtxos`,
{
utxos: utxos,
usrObj
},
this.axiosOptions
)
return response.data
} catch (error) {
console.log('Error in hydrateUtxos(): ', error)
if (error.response && error.response.data) {
throw new Error(JSON.stringify(error.response.data, null, 2))
}
throw error
}
}
/**
* @api SLP.Utils.hydrateUtxosWL() hydrateUtxosWL()
* @apiName hydrateUtxosWL
* @apiGroup SLP Utils
* @apiDescription
* This call is exactly the same as `hydrateUtxos()`. This version hydrate a
* UTXO with SLP token metadata, but only uses the whitelist SLPDB for
* validation.
*
* Whitelist SLPDBs will return `isValid: null` for any token not in the
* 'whitelist' filter. Filtered SLPDBs are much smaller and more reliable
* to operate.
*
*/
// Same as tokenUtxoDetailsWL(), but reduces API calls by having bch-api server
// do the heavy lifting.
async hydrateUtxosWL (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 _this.axios.post(
`${this.restURL}slp/hydrateUtxosWL`,
{
utxos: utxos
},
_this.axiosOptions
this.axiosOptions
)
return response.data
@@ -1758,7 +1933,7 @@ class Utils {
const path = `${this.restURL}slp/status`
try {
const response = await _this.axios.get(path, _this.axiosOptions)
const response = await _this.axios.get(path, this.axiosOptions)
// console.log(
// `getStatus response.data: ${JSON.stringify(response.data, null, 2)}`
// )
-28
View File
@@ -1,28 +0,0 @@
const io = require('socket.io-client')
class Socket {
constructor (config = {}) {
if (typeof config === 'string') {
// TODO remove this check in v2.0
this.socket = io(`${config}`)
} else {
if (config.restURL) {
this.socket = io(`${config.restURL}`)
} else {
const restURL = 'https://rest.bitcoin.com'
this.socket = io(`${restURL}`)
}
if (config.callback) config.callback()
}
}
listen (endpoint, cb) {
this.socket.emit(endpoint)
if (endpoint === 'blocks') this.socket.on('blocks', msg => cb(msg))
else if (endpoint === 'transactions') { this.socket.on('transactions', msg => cb(msg)) }
}
}
module.exports = Socket
+528
View File
@@ -0,0 +1,528 @@
/*
High-level functions for working with Transactions
*/
const BigNumber = require('bignumber.js')
const RawTransaction = require('./raw-transactions')
const SlpUtils = require('./slp/utils')
const Blockchain = require('./blockchain')
class Transaction {
constructor (config) {
// Encapsulate dependencies
this.slpUtils = new SlpUtils(config)
this.rawTransaction = new RawTransaction(config)
this.blockchain = new Blockchain(config)
}
/**
* @api Transaction.get() get()
* @apiName get
* @apiGroup Transaction
* @apiDescription
* Returns an object of transaction data, including addresses for input UTXOs.
* If it is a SLP token transaction, the token information for inputs and
* outputs will also be included.
*
* This is an API heavy call. This function will only work with a single txid.
* It does not yet support an array of TXIDs.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txData = await bchjs.Transaction.get("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
* console.log(txData);
* } catch(error) {
* console.error(error)
* }
* })()
*/
// CT 10/31/21: TODO: this function should be refactored to use get2(), but
// add waterfall validation of the TX and its inputs.
async get (txid) {
try {
if (typeof txid !== 'string') {
throw new Error(
'Input to Transaction.get() must be a string containing a TXID.'
)
}
const txDetails = await this.rawTransaction.getTxData(txid)
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
// Setup default SLP properties.
txDetails.isValidSLPTx = false
// First get the token information for the output. If that fails, then
// this is not an SLP transaction, and this method can return false.
let outTokenData
try {
outTokenData = await this.slpUtils.decodeOpReturn(txid)
// console.log(`outTokenData: ${JSON.stringify(outTokenData, null, 2)}`)
// Get Genesis data for this token.
const genesisData = await this.slpUtils.decodeOpReturn(
outTokenData.tokenId
// decodeOpReturnCache
// usrObj // pass user data when making an internal call.
)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// Add token information to the tx details object.
txDetails.tokenTxType = outTokenData.txType
txDetails.tokenId = outTokenData.tokenId
txDetails.tokenTicker = genesisData.ticker
txDetails.tokenName = genesisData.name
txDetails.tokenDecimals = genesisData.decimals
txDetails.tokenUri = genesisData.documentUri
txDetails.tokenDocHash = genesisData.documentHash
// Add the token quantity to each output.
for (let i = 0; i < outTokenData.amounts.length; i++) {
const rawQty = outTokenData.amounts[i]
// const realQty = Number(rawQty) / Math.pow(10, txDetails.tokenDecimals)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(rawQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
txDetails.vout[i + 1].tokenQtyStr = realQty
txDetails.vout[i + 1].tokenQty = parseFloat(realQty)
}
// Add tokenQty = null to any outputs that don't have a value.
for (let i = 0; i < txDetails.vout.length; i++) {
const thisVout = txDetails.vout[i]
if (!thisVout.tokenQty && thisVout.tokenQty !== 0) {
thisVout.tokenQty = null
}
}
// Loop through each input and retrieve the token data.
for (let i = 0; i < txDetails.vin.length; i++) {
const thisVin = txDetails.vin[i]
// console.log(`thisVin: ${JSON.stringify(thisVin, null, 2)}`)
try {
// If decodeOpReturn() throws an error, then this input is not
// from an SLP transaction and can be ignored.
const inTokenData = await this.slpUtils.decodeOpReturn(thisVin.txid)
// console.log(
// `vin[${i}] tokenData: ${JSON.stringify(inTokenData, null, 2)}`
// )
let tokenQty = 0
// Validate the input. Mark qty as null if not valid.
const vinIsValid = await this.slpUtils.waterfallValidateTxid(
thisVin.txid
)
if (!vinIsValid) {
// If the input is not a valid, then set qty as null.
tokenQty = null
} else if (inTokenData.txType === 'SEND') {
// Get the appropriate vout token amount. This may throw an error,
// which means this Vin is not actually a token UTXO, it was just
// associated with a previous token TX.
tokenQty = inTokenData.amounts[thisVin.vout - 1]
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
//
} else if (inTokenData.txType === 'GENESIS') {
// Only vout[1] of a Genesis transaction represents the tokens.
// Any other outputs in that transaction are normal BCH UTXOs.
if (thisVin.vout === 1) {
tokenQty = inTokenData.qty
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
}
} else if (inTokenData.txType === 'MINT') {
// vout=1 (second output) recieves the newly minted tokens.
if (thisVin.vout === 1) {
tokenQty = inTokenData.qty
} else {
tokenQty = null
}
//
} else {
console.log(
'Unexpected code path in Transaction.get(). What is the txType?'
)
console.log(inTokenData)
throw new Error('Unexpected code path')
}
if (tokenQty) {
// const realQty =
// Number(tokenQty) / Math.pow(10, txDetails.tokenDecimals)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVin.tokenQtyStr = realQty
thisVin.tokenQty = parseFloat(realQty)
// txDetails.vin[i].tokenQty = tokenQty
// Add token ID to input
thisVin.tokenId = inTokenData.tokenId
} else {
thisVin.tokenQty = null
}
} catch (err) {
// If decodeOpReturn() throws an error, then this input is not
// from an SLP transaction and can be ignored.
// thisVin.tokenQty = null
thisVin.tokenQty = null
continue
}
}
// Finally, validate the SLP TX.
// this.slpUtils.waterfallValidateTxid(txid, usrObj)
txDetails.isValidSLPTx = await this.slpUtils.waterfallValidateTxid(txid)
// TODO: Convert the block hash to a block height. Add block height
// value to the transaction.
} catch (err) {
// console.log('Error: ', err)
// This case handles rate limit errors.
if (err.response && err.response.data && err.response.data.error) {
throw new Error(err.response.data.error)
}
// If decoding the op_return fails, then it's not an SLP transaction,
// and the non-hyrated TX details can be returned.
return txDetails
}
return txDetails
} catch (err) {
// console.error('Error in transactions.js/get(): ', err)
// This case handles rate limit errors.
if (err.response && err.response.data && err.response.data.error) {
throw new Error(err.response.data.error)
}
if (err.error) throw new Error(err.error)
throw err
}
}
/**
* @api Transaction.get3() get3()
* @apiName get3
* @apiGroup Transaction
* @apiDescription
* Returns an object of transaction data, including addresses for input UTXOs.
* If it is a SLP token transaction, the token information for inputs and
* outputs will also be included.
*
* This is an API heavy call. This function will only work with a single txid.
* It does not yet support an array of TXIDs.
*
* This is the same as get(), except it omits DAG validation of the TXID.
*
* @apiExample Example usage:
* (async () => {
* try {
* let txData = await bchjs.Transaction.get2("0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098");
* console.log(txData);
* } catch(error) {
* console.error(error)
* }
* })()
*/
async get3 (txid) {
try {
if (typeof txid !== 'string') {
throw new Error(
'Input to Transaction.get() must be a string containing a TXID.'
)
}
// Get TX data
const txDetails = await this.rawTransaction.getTxData(txid)
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
// Get the block height the transaction was mined in.
const blockHeader = await this.blockchain.getBlockHeader(
txDetails.blockhash
)
txDetails.blockheight = blockHeader.height
// console.log(`blockHeader: ${JSON.stringify(blockHeader, null, 2)}`)
// Set default as not an SLP tx
txDetails.isSlpTx = false
// Get Token Data
const txTokenData = await this.getTokenInfo(txid)
// console.log(`txTokenData: ${JSON.stringify(txTokenData, null, 2)}`)
// If not a token, return the tx data. Processing is complete.
if (!txTokenData) return txDetails
// Mark TX as an SLP tx. This does not mean it's valid, it just means
// the OP_RETURN passes a basic check.
txDetails.isSlpTx = true
// Get Genesis data
const genesisData = await this.getTokenInfo(txTokenData.tokenId)
// console.log(`genesisData: ${JSON.stringify(genesisData, null, 2)}`)
// Add token information to the tx details object.
txDetails.tokenTxType = txTokenData.txType
txDetails.tokenId = txTokenData.tokenId
txDetails.tokenTicker = genesisData.ticker
txDetails.tokenName = genesisData.name
txDetails.tokenDecimals = genesisData.decimals
txDetails.tokenUri = genesisData.documentUri
txDetails.tokenDocHash = genesisData.documentHash
// console.log(`txDetails before processing input and outputs: ${JSON.stringify(txDetails, null, 2)}`)
// Process TX Outputs
// Add the token quantity to each output.
// 'i' starts at 1, because vout[0] is the OP_RETURN
for (let i = 0; i < txDetails.vout.length; i++) {
const thisVout = txDetails.vout[i]
if (txTokenData.txType === 'SEND') {
// console.log(
// `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
// )
// First output is OP_RETURN, so tokenQty is null.
if (i === 0) {
thisVout.tokenQty = null
thisVout.tokenQtyStr = null
continue
}
// Non SLP outputs.
if (i > txTokenData.amounts.length) {
thisVout.tokenQty = null
thisVout.tokenQtyStr = null
continue
}
const rawQty = txTokenData.amounts[i - 1]
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(rawQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
txDetails.vout[i].tokenQtyStr = realQty
txDetails.vout[i].tokenQty = parseFloat(realQty)
// console.log(
// `thisVout ${i}: ${JSON.stringify(txDetails.vout[i], null, 2)}`
// )
} else if (
txTokenData.txType === 'GENESIS' ||
txTokenData.txType === 'MINT'
) {
// console.log(
// `output txTokenData: ${JSON.stringify(txTokenData, null, 2)}`
// )
let tokenQty = 0 // Default value
// Only vout[1] of a Genesis or Mint transaction represents the tokens.
// Any other outputs in that transaction are normal BCH UTXOs.
if (i === 1) {
tokenQty = txTokenData.qty
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVout.tokenQtyStr = realQty
thisVout.tokenQty = parseFloat(realQty)
// console.log(`thisVout[${i}]: ${JSON.stringify(thisVout, null, 2)}`)
} else if (i === txTokenData.mintBatonVout) {
// Optional Mint baton
thisVout.tokenQtyStr = '0'
thisVout.tokenQty = 0
thisVout.isMintBaton = true
} else {
thisVout.tokenQtyStr = '0'
thisVout.tokenQty = 0
}
} else {
throw new Error('Unknown SLP TX type for TX')
}
}
// Process TX inputs
for (let i = 0; i < txDetails.vin.length; i++) {
const thisVin = txDetails.vin[i]
// console.log(`thisVin[${i}]: ${JSON.stringify(thisVin, null, 2)}`)
const vinTokenData = await this.getTokenInfo(thisVin.txid)
// console.log(
// `vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
// )
// Corner case: Ensure the token ID is the same.
const vinTokenIdIsTheSame = vinTokenData.tokenId === txDetails.tokenId
// If the input is not a token input, or if the tokenID is not the same,
// then mark the token output as null.
if (!vinTokenData || !vinTokenIdIsTheSame) {
thisVin.tokenQty = 0
thisVin.tokenQtyStr = '0'
thisVin.tokenId = null
continue
}
if (vinTokenData.txType === 'SEND') {
// console.log(
// `SEND vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
// )
const tokenQty = vinTokenData.amounts[thisVin.vout - 1]
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVin.tokenQtyStr = realQty
thisVin.tokenQty = parseFloat(realQty)
thisVin.tokenId = vinTokenData.tokenId
} else if (vinTokenData.txType === 'MINT') {
// console.log(
// `MINT vinTokenData ${i}: ${JSON.stringify(vinTokenData, null, 2)}`
// )
let tokenQty = 0 // Default value
// Only vout[1] of a Genesis transaction represents the tokens.
// Any other outputs in that transaction are normal BCH UTXOs.
if (thisVin.vout === 1) {
tokenQty = vinTokenData.qty
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVin.tokenQtyStr = realQty
thisVin.tokenQty = parseFloat(realQty)
thisVin.tokenId = vinTokenData.tokenId
} else if (thisVin.vout === vinTokenData.mintBatonVout) {
// Optional Mint baton
thisVin.tokenQtyStr = '0'
thisVin.tokenQty = 0
thisVin.tokenId = vinTokenData.tokenId
thisVin.isMintBaton = true
} else {
thisVin.tokenQtyStr = '0'
thisVin.tokenQty = 0
thisVin.tokenId = null
}
} else if (vinTokenData.txType === 'GENESIS') {
// console.log(
// `GENESIS vinTokenData ${i}: ${JSON.stringify(
// vinTokenData,
// null,
// 2
// )}`
// )
let tokenQty = 0 // Default value
// Only vout[1] of a Genesis transaction represents the tokens.
// Any other outputs in that transaction are normal BCH UTXOs.
if (thisVin.vout === 1) {
tokenQty = vinTokenData.qty
// console.log(`tokenQty: ${JSON.stringify(tokenQty, null, 2)}`)
// Calculate the real quantity using a BigNumber, then convert it to a
// floating point number.
let realQty = new BigNumber(tokenQty).dividedBy(
10 ** parseInt(txDetails.tokenDecimals)
)
realQty = realQty.toString()
// realQty = parseFloat(realQty)
thisVin.tokenQtyStr = realQty
thisVin.tokenQty = parseFloat(realQty)
thisVin.tokenId = vinTokenData.tokenId
} else if (thisVin.vout === vinTokenData.mintBatonVout) {
// Optional Mint baton
thisVin.tokenQtyStr = '0'
thisVin.tokenQty = 0
thisVin.tokenId = vinTokenData.tokenId
thisVin.isMintBaton = true
} else {
thisVin.tokenQtyStr = '0'
thisVin.tokenQty = 0
thisVin.tokenId = null
}
} else {
console.log(
`Unknown vinTokenData: ${JSON.stringify(vinTokenData, null, 2)}`
)
throw new Error('Unknown token type in input')
}
}
return txDetails
} catch (err) {
console.error('Error in get3()')
// This case handles rate limit errors.
if (err.response && err.response.data && err.response.data.error) {
throw new Error(err.response.data.error)
}
if (err.error) throw new Error(err.error)
throw err
}
}
// A wrapper for decodeOpReturn(). Returns false if txid is not an SLP tx.
// Returns the token data if the txid is an SLP tx.
async getTokenInfo (txid) {
try {
const tokenData = await this.slpUtils.decodeOpReturn(txid)
return tokenData
} catch (err) {
return false
}
}
}
module.exports = Transaction
+151 -4
View File
@@ -1,6 +1,6 @@
const axios = require('axios')
let _this
// let _this
class Util {
constructor (config) {
@@ -24,11 +24,158 @@ class Util {
}
}
_this = this
// _this = this
}
/**
* @api util.validateAddress() validateAddress()
* @api Util.floor8() floor8()
* @apiName floor8
* @apiGroup Util
* @apiDescription Round a number down to 8 decimal places.
*
*
* @apiExample Example usage:
* (async () => {
* try {
* const num = 1.234567891111
* const result = bchjs.Util.floor8(num)
* console.log(result)
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* 1.23456789
*/
// floor8 - round to 8 decimal places
// Takes a number and returns it, rounded to the nearest 8 decimal place.
floor8 (num) {
const thisNum = Number(num)
if (isNaN(thisNum)) throw new Error('input must be a number')
let tempNum = thisNum * 100000000
tempNum = Math.floor(tempNum)
tempNum = tempNum / 100000000
return tempNum
}
/**
* @api Util.floor2() floor2()
* @apiName floor2
* @apiGroup Util
* @apiDescription Round a number down to 2 decimal places.
*
*
* @apiExample Example usage:
* (async () => {
* try {
* const num = 1.234567891111
* const result = bchjs.Util.floor2(num)
* console.log(result)
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* 1.23
*/
// floor2 - round down to 2 decimal places
// Takes a number and returns it, rounded to the nearest 2 decimal place.
floor2 (num) {
const thisNum = Number(num)
if (isNaN(thisNum)) throw new Error('input must be a number')
let tempNum = thisNum * 100
tempNum = Math.floor(tempNum)
tempNum = tempNum / 100
return tempNum
}
/**
* @api Util.chunk20() chunk20()
* @apiName chunk20
* @apiGroup Util
* @apiDescription chunk up an array into multiple arrays of 20 elements each.
* Input: arrayToSlice - a one-dimensional array of elements.
* Returns a two-dimensional array. An array of 20-element arrays.
*
* @apiExample Example usage:
* (async () => {
* try {
* const bigArray = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]
*
* const chunked = bchjs.Util.chunk20(bigArray)
* console.log(chunked)
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* [
* [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],
* [20,21,22,23,24,25,26]
* ]
*/
// chunk20 - chunk up an array into multiple arrays of 20 elements each.
// Input: arrayToSlice - a one-dimensional array of elements.
// Returns a two-dimensional array. An array of 20-element arrays.
chunk20 (arrayToSlice) {
try {
// Validate inputs
if (!Array.isArray(arrayToSlice)) {
throw new Error('input must be an array')
}
let offset = 0
const result = []
// Loop over the array and slice off chunks of 20 elements.
while (offset < arrayToSlice.length) {
const chunk = arrayToSlice.slice(offset, offset + 20)
result.push(chunk)
offset = offset + 20
}
return result
} catch (err) {
console.error('Error in chunk20()')
throw err
}
}
/**
* @api Util.sleep() sleep()
* @apiName sleep
* @apiGroup Util
* @apiDescription Promise-based delay.
* Expects an integer as input, which represents milliseconds. This function
* will return a Promise that resolves that many milliseconds later.
*
*
* @apiExample Example usage:
* (async () => {
* try {
* const tenSeconds = 10000
* await bchjs.Util.sleep(tenSeconds)
* } catch(error) {
* console.error(error)
* }
* })()
*
*/
sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
/**
* @api Util.validateAddress() validateAddress()
* @apiName Validate Address.
* @apiGroup Util
* @apiDescription Return information about the given bitcoin address.
@@ -78,7 +225,7 @@ class Util {
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}util/validateAddress/${address}`,
_this.axiosOptions
this.axiosOptions
)
return response.data
+350
View File
@@ -0,0 +1,350 @@
/*
High-level functions for working with UTXOs
TODO:
- Make a getWL() clone of get(), but uses hydrateUtxosWL()
*/
// Local libraries
const Electrumx = require('./electrumx')
const Slp = require('./slp/slp')
class UTXO {
constructor (config) {
// Encapsulate dependencies for easier mocking.
this.electrumx = new Electrumx(config)
this.slp = new Slp(config)
}
/**
* @api Utxo.get() get()
* @apiName get
* @apiGroup UTXO
* @apiDescription Get UTXOs for an address
*
* Given an address, this function will return an object with thre following
* properties:
* - address: "" - the address these UTXOs are associated with
* - bchUtxos: [] - UTXOs confirmed to be spendable as normal BCH
* - nullUtxo: [] - UTXOs that did not pass SLP validation. Should be ignored and
* not spent, to be safe.
* - slpUtxos: {} - UTXOs confirmed to be colored as valid SLP tokens
* - type1: {}
* - tokens: [] - SLP token Type 1 tokens.
* - mintBatons: [] - SLP token Type 1 mint batons.
* - nft: {}
* - tokens: [] - NFT tokens
* - groupTokens: [] - NFT Group tokens, used to create NFT tokens.
* - groupMintBatons: [] - Minting baton to create more NFT Group tokens.
*
* Note: You can pass in an optional second Boolean argument. The default
* `false` will use the normal waterfall validation method. Set to `true`,
* SLP UTXOs will be validated with the whitelist filtered SLPDB. This will
* result is many more UTXOs in the `nullUtxos` array.
*
* @apiExample Example usage:
* (async () => {
* try {
* let utxos = await bchjs.Utxo.get('simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj');
* console.log(utxos);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* [
* {
* "address": "bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv",
* "bchUtxos": [
* {
* "height": 674513,
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "tx_pos": 3,
* "value": 38134,
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "vout": 3,
* "isValid": false
* }
* ],
* "nullUtxos": [],
* "slpUtxos": {
* "type1": {
* "mintBatons": [
* {
* "height": 674512,
* "tx_hash": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tx_pos": 2,
* "value": 546,
* "txid": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "vout": 2,
* "utxoType": "minting-baton",
* "tokenId": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tokenTicker": "SLPTEST",
* "tokenName": "SLP Test Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 8,
* "tokenType": 1,
* "isValid": true
* }
* ],
* "tokens": [
* {
* "height": 674512,
* "tx_hash": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tx_pos": 1,
* "value": 546,
* "txid": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "100",
* "tokenId": "acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230",
* "tokenTicker": "SLPTEST",
* "tokenName": "SLP Test Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 8,
* "tokenType": 1,
* "isValid": true
* }
* ]
* },
* "nft": {
* "groupMintBatons": [
* {
* "height": 674513,
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "tx_pos": 2,
* "value": 546,
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "vout": 2,
* "utxoType": "minting-baton",
* "transactionType": "mint",
* "tokenId": "a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395",
* "tokenType": 129,
* "tokenTicker": "NFTGT",
* "tokenName": "NFT Test Group Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "mintBatonVout": 2,
* "isValid": true
* }
* ],
* "groupTokens": [
* {
* "height": 674513,
* "tx_hash": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "tx_pos": 1,
* "value": 546,
* "txid": "705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "10",
* "transactionType": "mint",
* "tokenId": "a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395",
* "tokenType": 129,
* "tokenTicker": "NFTGT",
* "tokenName": "NFT Test Group Token",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "mintBatonVout": 2,
* "isValid": true
* }
* ],
* "tokens": [
* {
* "height": 674512,
* "tx_hash": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "tx_pos": 1,
* "value": 546,
* "txid": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "vout": 1,
* "utxoType": "token",
* "tokenQty": "1",
* "tokenId": "eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b",
* "tokenTicker": "NFT004",
* "tokenName": "NFT Child",
* "tokenDocumentUrl": "https://FullStack.cash",
* "tokenDocumentHash": "",
* "decimals": 0,
* "tokenType": 65,
* "isValid": true
* }
* ]
* }
* }
* }
* ]
*
*
*/
async get (address, useWhitelist = false) {
try {
// Convert address to an array if it is a string.
if (typeof address === 'string') address = [address]
// Throw an error if there are more than 20 addresses passed in at a time.
if (address.length > 20) throw new Error('Too many elements, 20 max.')
// Covert each address to a BCH address.
const addr = address.map(elem => this.slp.Address.toCashAddress(elem))
// Get the UTXOs associated with the address.
const utxoData = await this.electrumx.utxo(addr)
// console.log(`utxoData: ${JSON.stringify(utxoData, null, 2)}`)
// Hydate the utxos with token information.
let hydratedUtxos
if (useWhitelist) {
hydratedUtxos = await this.slp.Utils.hydrateUtxosWL(utxoData.utxos)
} else {
hydratedUtxos = await this.slp.Utils.hydrateUtxos(utxoData.utxos)
}
// console.log(`hydratedUtxos: ${JSON.stringify(hydratedUtxos, null, 2)}`)
const retAry = [] // Return array
// Loop through each address.
for (let i = 0; i < hydratedUtxos.slpUtxos.length; i++) {
const thisAddr = hydratedUtxos.slpUtxos[i]
const addrObj = {
address: thisAddr.address
}
// Filter out the different types of UTXOs.
addrObj.bchUtxos = thisAddr.utxos.filter(elem => elem.isValid === false)
addrObj.nullUtxos = thisAddr.utxos.filter(elem => elem.isValid === null)
// Break down the SLP UTXOs.
addrObj.slpUtxos = {
type1: {},
nft: {}
}
// Token Type 1 Minting Batons.
addrObj.slpUtxos.type1.mintBatons = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs1 = elem.tokenType === 1
const isMintingBaton = elem.utxoType === 'minting-baton'
return isValid && tokenTypeIs1 && isMintingBaton
})
// Token Type 1 tokens.
addrObj.slpUtxos.type1.tokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs1 = elem.tokenType === 1
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs1 && isToken
})
// NFT Group Minting Batons
addrObj.slpUtxos.nft.groupMintBatons = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs129 = elem.tokenType === 129
const isMintingBaton = elem.utxoType === 'minting-baton'
return isValid && tokenTypeIs129 && isMintingBaton
})
// NFT Group tokens
addrObj.slpUtxos.nft.groupTokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs129 = elem.tokenType === 129
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs129 && isToken
})
// NFT (Child) tokens
addrObj.slpUtxos.nft.tokens = thisAddr.utxos.filter(elem => {
const isValid = elem.isValid === true
const tokenTypeIs65 = elem.tokenType === 65
const isToken = elem.utxoType === 'token'
return isValid && tokenTypeIs65 && isToken
})
retAry.push(addrObj)
}
return retAry
} catch (err) {
console.error('Error in bchjs.utxo.get()')
if (err.error) throw new Error(err.error)
throw err
}
}
/**
* @api Utxo.findBiggestUtxo() findBiggestUtxo()
* @apiName findBiggestUtxo
* @apiGroup UTXO
* @apiDescription Get the biggest UTXO in an array.
*
* Given an array of BCH UTXOs, this method will return the biggest UTXO.
* This is often the simplest way to pick a UTXO for generating a transaction.
*
* @apiExample Example usage:
* (async () => {
* try {
* const utxos = await bchjs.Utxo.get('bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z');
* const utxo = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
* console.log(utxo);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* {
* "height": 655431,
* "tx_hash": "7a091716f8137e94f87e7760648cd34a17e32754ef95f7c7bda38a635c9b2b1b",
* "tx_pos": 0,
* "value": 800,
* "txid": "7a091716f8137e94f87e7760648cd34a17e32754ef95f7c7bda38a635c9b2b1b",
* "vout": 0,
* "isValid": false,
* "satoshis": 800
* }
*/
// Returns the utxo with the biggest balance from an array of utxos.
findBiggestUtxo (utxos) {
let largestAmount = 0
let largestIndex = 0
if (!Array.isArray(utxos)) {
throw new Error('utxos input to findBiggestUtxo() must be an array')
}
for (let i = 0; i < utxos.length; i++) {
const thisUtxo = utxos[i]
// Give Elecrumx utxos a satoshis property.
if (thisUtxo.value) {
if (!thisUtxo.satoshis) thisUtxo.satoshis = Number(thisUtxo.value)
}
if (!thisUtxo.satoshis) {
throw new Error(
'UTXOs require a satoshis or value property for findBiggestUtxo()'
)
}
if (thisUtxo.satoshis > largestAmount) {
largestAmount = thisUtxo.satoshis
largestIndex = i
}
}
return utxos[largestIndex]
}
}
module.exports = UTXO
@@ -14,9 +14,9 @@
const assert = require('chai').assert
// const RESTURL = `https://abc.fullstack.cash/v4/`
const RESTURL = 'https://bchn.fullstack.cash/v4/'
// const RESTURL = `http://localhost:3000/v4/`
// const RESTURL = `https://abc.fullstack.cash/v5/`
const RESTURL = 'https://bchn.fullstack.cash/v5/'
// const RESTURL = `http://localhost:3000/v5/`
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL })
@@ -13,9 +13,9 @@ const PRO_PASS = 'testpassword'
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`,
restURL: 'https://abc.fullstack.cash/v4/',
// restURL: `http://localhost:3000/v4/`,
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https://abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
authPass: PRO_PASS
})
+2 -2
View File
@@ -18,8 +18,8 @@ const JWT_TOKEN =
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
restURL: 'https://api.fullstack.cash/v4/',
// restURL: `http://localhost:3000/v4/`,
restURL: 'https://api.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN
})
@@ -18,9 +18,9 @@ const JWT_TOKEN =
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`,
restURL: 'https:/abc.fullstack.cash/v4/',
// restURL: `http://localhost:3000/v4/`,
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https:/abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN
})
@@ -65,7 +65,7 @@ describe('#full node rate limits', () => {
it('should throw error for more than 100 RPM to fullnode', async () => {
try {
console.log('This test usually doesn\'t pass, because of latency.')
console.log("This test usually doesn't pass, because of latency.")
for (let i = 0; i < 100; i++) await bchjs.Control.getNetworkInfo()
} catch (err) {
// console.log(`validating after 10th call`)
+3 -3
View File
@@ -14,9 +14,9 @@ const JWT_TOKEN =
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v4/`,
restURL: 'https://abc.fullstack.cash/v4/',
// restURL: `http://localhost:3000/v4/`,
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https://abc.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
apiToken: JWT_TOKEN
})
+38 -1
View File
@@ -22,7 +22,7 @@ util.inspect.defaultOptions = {
describe('#blockchain', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#getBestBlockHash', () => {
@@ -276,6 +276,43 @@ describe('#blockchain', () => {
assert.equal(result, null)
})
})
describe('#getBlock', () => {
it('should get block information with default verbosity', async () => {
const blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
const result = await bchjs.Blockchain.getBlock(blockhash)
assert.hasAllKeys(result, [
'hash',
'confirmations',
'size',
'height',
'version',
'versionHex',
'merkleroot',
'tx',
'time',
'mediantime',
'nonce',
'bits',
'difficulty',
'chainwork',
'nTx',
'previousblockhash',
'nextblockhash'
])
assert.isArray(result.tx)
})
it('should get block information with verbosity 0', async () => {
const blockhash =
'0000000000000000008e8d83cba6d45a9314bc2ef4538d4e0577c6bed8593536'
const verbosity = 0
const result = await bchjs.Blockchain.getBlock(blockhash, verbosity)
assert.isString(result)
})
})
})
function sleep (ms) {
@@ -8,7 +8,7 @@
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
const bchjs = new BCHJS({ restURL: process.env.RESTURL })
// Inspect utility used for debugging.
const util = require('util')
@@ -20,7 +20,7 @@ util.inspect.defaultOptions = {
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
/*
@@ -44,6 +44,7 @@ describe('#rawtransaction', () => {
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'bad-txns-inputs-missingorspent')
// assert.include(err.error, 'Missing inputs')
}
})
@@ -61,6 +62,7 @@ describe('#rawtransaction', () => {
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'bad-txns-inputs-missingorspent')
// assert.include(err.error, 'Missing inputs')
}
})
})
+5 -2
View File
@@ -25,9 +25,9 @@ describe('#SLP', () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
bchjs = new BCHJS()
bchjs = new BCHJS({ restURL: process.env.RESTURL })
})
describe('#util', () => {
@@ -113,6 +113,7 @@ describe('#SLP', () => {
// Token send on BCHN network
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
// assert.equal(data[4].isValid, true)
assert.equal(data[4].isValid, null)
// Token send on ABC network
@@ -180,6 +181,7 @@ describe('#SLP', () => {
assert.equal(result[0].valid, null)
assert.equal(result[1].valid, null)
assert.equal(result[2].valid, true)
// assert.equal(result[2].valid, null)
assert.equal(result[3].valid, true)
assert.equal(result[4].valid, null)
})
@@ -209,6 +211,7 @@ describe('#SLP', () => {
assert.equal(result[0].valid, null)
assert.equal(result[1].valid, null)
assert.equal(result[2].valid, true)
// assert.equal(result[2].valid, null)
assert.equal(result[3].valid, true)
assert.equal(result[4].valid, null)
assert.equal(result[5].valid, true)
@@ -0,0 +1,38 @@
/*
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#UTXO', () => {
beforeEach(async () => {
// sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -20,7 +20,7 @@ util.inspect.defaultOptions = {
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
/*
@@ -25,12 +25,37 @@ describe('#SLP', () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS()
})
describe('#util', () => {
describe('#decodeOpReturn', () => {
it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [
@@ -186,11 +211,13 @@ describe('#SLP', () => {
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid()
// True in validateTxid() (no whitelist SLPDB) but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
// Note: This should change from null to true once SLPDB finishes indexing.
assert.equal(result[4].txid, txids[4])
// True when no whitelist SLPDB is used.
assert.equal(result[4].valid, true)
})
})
@@ -0,0 +1,105 @@
/*
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#UTXO', () => {
beforeEach(async () => {
// sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle an array of addresses', async () => {
const addr = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
]
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle NFTs and minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
assert.isArray(result[0].slpUtxos.type1.mintBatons)
assert.isArray(result[0].slpUtxos.type1.tokens)
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
assert.isArray(result[0].slpUtxos.nft.groupTokens)
assert.isArray(result[0].slpUtxos.nft.tokens)
})
})
describe('#findBiggestUtxo', () => {
it('should sort UTXOs from Electrumx', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
it('should sort UTXOs from Utxos.get()', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const utxos = await bchjs.Utxo.get(addr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+25
View File
@@ -0,0 +1,25 @@
/*
Integration tests for bchjs dsproof library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#DSProof', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#getDSProof', () => {
it('should get TX info from the full node', async () => {
const txid =
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
const result = await bchjs.DSProof.getDSProof(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, null)
})
})
})
@@ -20,7 +20,7 @@ util.inspect.defaultOptions = {
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
/*
+51 -1
View File
@@ -25,12 +25,37 @@ describe('#SLP', () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS()
})
describe('#util', () => {
describe('#decodeOpReturn', () => {
it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [
@@ -239,6 +264,31 @@ describe('#SLP', () => {
})
})
})
describe('#nft1', () => {
describe('#listNFTGroupChildren', () => {
it('should return array of children GENESIS transactions IDs', async () => {
const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
const result = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftChildren')
assert.isArray(result.nftChildren)
})
})
describe('#parentNFTGroup', () => {
it('should return parent NFT group information', async () => {
const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
const result = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftGroup')
assert.property(result.nftGroup, 'id')
assert.property(result.nftGroup, 'name')
assert.property(result.nftGroup, 'symbol')
assert.property(result.nftGroup, 'documentUri')
assert.property(result.nftGroup, 'versionType')
assert.property(result.nftGroup, 'initialTokenQty')
})
})
})
})
// Promise-based sleep function
@@ -5,7 +5,7 @@
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../src/bch-js')
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
// Inspect utility used for debugging.
@@ -18,7 +18,7 @@ util.inspect.defaultOptions = {
describe('#util', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#validateAddress', () => {
@@ -81,7 +81,8 @@ describe('#util', () => {
it('should throw error on array size rate limit', async () => {
try {
const dataMock = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
const dataMock =
'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
@@ -0,0 +1,125 @@
/*
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#UTXO', () => {
beforeEach(async () => {
// sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle an array of addresses', async () => {
const addr = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
]
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should handle NFTs and minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
assert.isArray(result[0].slpUtxos.type1.mintBatons)
assert.isArray(result[0].slpUtxos.type1.tokens)
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
assert.isArray(result[0].slpUtxos.nft.groupTokens)
assert.isArray(result[0].slpUtxos.nft.tokens)
})
it('should use the whitelist when flag is set', async () => {
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const useWhitelist = true
const result = await bchjs.Utxo.get(addr, useWhitelist)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
// Most token UTXOs should end up in the nullUtxos array.
assert.isAbove(result[0].bchUtxos.length, 0)
assert.isAbove(result[0].nullUtxos.length, 1)
})
})
describe('#findBiggestUtxo', () => {
it('should sort UTXOs from Electrumx', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const electrumxUtxos = await bchjs.Electrumx.utxo(addr)
// console.log(`Electrumx utxos: ${JSON.stringify(electrumxUtxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(electrumxUtxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
it('should sort UTXOs from Utxos.get()', async () => {
const addr = 'bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z'
const utxos = await bchjs.Utxo.get(addr)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = bchjs.Utxo.findBiggestUtxo(utxos[0].bchUtxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -12,11 +12,11 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
@@ -29,7 +29,7 @@ util.inspect.defaultOptions = {
describe('#blockchain', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#getBestBlockHash', () => {
+2 -2
View File
@@ -7,14 +7,14 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
describe('#control', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#getNetworkInfo', () => {
+15 -7
View File
@@ -5,7 +5,7 @@ const util = require('util')
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
@@ -16,7 +16,7 @@ describe('#ElectrumX', () => {
beforeEach(async () => {
sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
afterEach(() => sandbox.restore())
@@ -68,7 +68,9 @@ describe('#ElectrumX', () => {
it('should throw error on array size rate limit', async () => {
try {
const addr = []
for (let i = 0; i < 25; i++) { addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2') }
for (let i = 0; i < 25; i++) {
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
// const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${util.inspect(result)}`)
@@ -121,7 +123,9 @@ describe('#ElectrumX', () => {
it('should throw error on array size rate limit', async () => {
try {
const addr = []
for (let i = 0; i < 25; i++) { addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2') }
for (let i = 0; i < 25; i++) {
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
const result = await bchjs.Electrumx.balance(addr)
@@ -175,7 +179,9 @@ describe('#ElectrumX', () => {
it('should throw error on array size rate limit', async () => {
try {
const addr = []
for (let i = 0; i < 25; i++) { addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2') }
for (let i = 0; i < 25; i++) {
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
const result = await bchjs.Electrumx.transactions(addr)
@@ -236,7 +242,9 @@ describe('#ElectrumX', () => {
it('should throw error on array size rate limit', async () => {
try {
const addr = []
for (let i = 0; i < 25; i++) { addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2') }
for (let i = 0; i < 25; i++) {
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${util.inspect(result)}`)
@@ -355,7 +363,7 @@ describe('#ElectrumX', () => {
assert.property(err, 'success')
assert.equal(err.success, false)
assert.include(
err.error,
err.error.error,
'the transaction was rejected by network rules'
)
}
-147
View File
@@ -1,147 +0,0 @@
const chai = require('chai')
const assert = chai.assert
const sinon = require('sinon')
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ ninsightURL: 'https://trest.bitcoin.com/v2' })
describe('#Ninsight', () => {
let sandbox
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#utxo', () => {
it('should GET utxos for a single address', async () => {
const addr = 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr'
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result[0], 'utxos')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'slpAddress')
assert.property(result[0], 'scriptPubKey')
assert.property(result[0], 'asm')
assert.isArray(result[0].utxos)
assert.property(result[0].utxos[0], 'txid')
assert.property(result[0].utxos[0], 'vout')
assert.property(result[0].utxos[0], 'amount')
assert.property(result[0].utxos[0], 'satoshis')
assert.property(result[0].utxos[0], 'height')
assert.property(result[0].utxos[0], 'confirmations')
})
it('should POST utxo details for an array of addresses', async () => {
const addr = [
'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
'bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul'
]
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isArray(result[0].utxos)
assert.property(result[0], 'utxos')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'slpAddress')
assert.property(result[0], 'scriptPubKey')
assert.property(result[0], 'asm')
// assert.hasAnyKeys(result[0][0], [
// "txid",
// "vout",
// "value",
// "height",
// "confirmations"
// ])
})
})
describe('#transactions', () => {
it('should POST transactions history for a single address', async () => {
const addr = 'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr'
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'txs')
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], 'txid')
assert.property(result[0].txs[0], 'vin')
assert.property(result[0].txs[0], 'vout')
})
it('should POST transactions history for an array of addresses', async () => {
const addr = [
'bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr',
'bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul'
]
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'txs')
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], 'txid')
assert.property(result[0].txs[0], 'vin')
assert.property(result[0].txs[0], 'vout')
})
})
describe('#txDetails', () => {
it('should POST transactions details for a single address', async () => {
const txid =
'76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea'
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.property(result[0], 'version')
assert.property(result[0], 'locktime')
assert.property(result[0], 'vin')
assert.property(result[0], 'vout')
assert.property(result[0], 'blockhash')
assert.property(result[0], 'blockheight')
assert.property(result[0], 'confirmations')
assert.property(result[0], 'time')
assert.property(result[0], 'blocktime')
assert.property(result[0], 'valueOut')
assert.property(result[0], 'size')
})
it('should POST transactions details for an array of addresses', async () => {
const txid = [
'f191d1062789d7071da6f2168ba306869a829294f6b1c09d03492db4ca3a8e77',
'76856d82e00b2696acd8d989e1fa6c46b431005046a285ce905814cac0ff8fea'
]
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.property(result[0], 'vin')
assert.property(result[0], 'vout')
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -10,11 +10,11 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
@@ -27,7 +27,7 @@ util.inspect.defaultOptions = {
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#decodeRawTransaction', () => {
+3 -3
View File
@@ -7,11 +7,11 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
@@ -25,7 +25,7 @@ util.inspect.defaultOptions = {
describe('#SLP', () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#util', () => {
@@ -1,6 +1,6 @@
#!/bin/bash
export RESTURL=https://free-test.fullstack.cash/v4/
export RESTURL=https://free-test.fullstack.cash/v5/
export NETWORK=testnet
export IS_USING_FREE_TIER=true
+3 -3
View File
@@ -8,11 +8,11 @@ const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v4/'
: 'https://testnet3.fullstack.cash/v5/'
// if (process.env.RESTURL) RESTURL = process.env.RESTURL
const BCHJS = require('../../../../src/bch-js')
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v4/` })
// const bchjs = new BCHJS({ restURL: `https://testnet.bchjs.cash/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
@@ -25,7 +25,7 @@ util.inspect.defaultOptions = {
describe('#util', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#validateAddress', () => {
+1 -1
View File
@@ -9,7 +9,7 @@ const bchjs = new BCHJS()
describe('#control', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#getNetworkInfo', () => {
+4 -2
View File
@@ -11,7 +11,7 @@ describe('#ElectrumX', () => {
beforeEach(async () => {
sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
afterEach(() => sandbox.restore())
@@ -359,10 +359,12 @@ describe('#ElectrumX', () => {
try {
await bchjs.Electrumx.broadcast(txHex)
} catch (err) {
// console.log('err: ', err)
assert.property(err, 'success')
assert.equal(err.success, false)
assert.include(
err.error,
err.error.error,
'the transaction was rejected by network rules'
)
}
+1 -1
View File
@@ -5,7 +5,7 @@ const bchjs = new BCHJS()
describe('#Encryption', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#getPubKey', () => {
-138
View File
@@ -1,138 +0,0 @@
const chai = require('chai')
const assert = chai.assert
const sinon = require('sinon')
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS({ ninsightURL: 'https://rest.bitcoin.com/v2' })
describe('#Ninsight', () => {
let sandbox
beforeEach(async () => {
sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
})
afterEach(() => sandbox.restore())
describe('#utxo', () => {
it('should GET utxos for a single address', async () => {
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result[0], 'utxos')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'slpAddress')
assert.property(result[0], 'scriptPubKey')
assert.property(result[0], 'asm')
assert.isArray(result[0].utxos)
assert.property(result[0].utxos[0], 'txid')
assert.property(result[0].utxos[0], 'vout')
assert.property(result[0].utxos[0], 'amount')
assert.property(result[0].utxos[0], 'satoshis')
assert.property(result[0].utxos[0], 'height')
assert.property(result[0].utxos[0], 'confirmations')
})
it('should POST utxo details for an array of addresses', async () => {
const addr = [
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7',
'bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr'
]
const result = await bchjs.Ninsight.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isArray(result[0].utxos)
assert.property(result[0], 'utxos')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'slpAddress')
assert.property(result[0], 'scriptPubKey')
assert.property(result[0], 'asm')
})
})
describe('#transactions', () => {
it('should POST transactions history for a single address', async () => {
const addr = 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'txs')
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], 'txid')
assert.property(result[0].txs[0], 'vin')
assert.property(result[0].txs[0], 'vout')
})
it('should POST transactions history for an array of addresses', async () => {
const addr = [
'bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7',
'bitcoincash:qz0us0z6ucpqt07jgpad0shgh7xmwxyr3ynlcsq0wr'
]
const result = await bchjs.Ninsight.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'cashAddress')
assert.property(result[0], 'legacyAddress')
assert.property(result[0], 'txs')
assert.isArray(result[0].txs)
assert.property(result[0].txs[0], 'txid')
assert.property(result[0].txs[0], 'vin')
assert.property(result[0].txs[0], 'vout')
})
})
describe('#txDetails', () => {
it('should POST transactions details for a single address', async () => {
const txid = 'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33'
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.property(result[0], 'version')
assert.property(result[0], 'locktime')
assert.property(result[0], 'vin')
assert.property(result[0], 'vout')
assert.property(result[0], 'blockhash')
assert.property(result[0], 'blockheight')
assert.property(result[0], 'confirmations')
assert.property(result[0], 'time')
assert.property(result[0], 'blocktime')
assert.property(result[0], 'valueOut')
assert.property(result[0], 'size')
})
it('should POST transactions details for an array of addresses', async () => {
const txid = [
'fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33',
'4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251'
]
const result = await bchjs.Ninsight.txDetails(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.property(result[0], 'vin')
assert.property(result[0], 'vout')
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+9 -1
View File
@@ -4,7 +4,7 @@ const bchjs = new BCHJS()
describe('#price', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#current', () => {
@@ -33,6 +33,14 @@ describe('#price', () => {
assert.isNumber(result)
})
})
describe('#getBchUsd', () => {
it('should get the USD price of BCH', async () => {
const result = await bchjs.Price.getBchUsd()
console.log(result)
assert.isNumber(result)
})
})
describe('#rates', () => {
it('should get the price of BCH in several currencies', async () => {
+36 -31
View File
@@ -1,6 +1,5 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
Integration tests for the bchjs.
TODO
*/
@@ -20,7 +19,7 @@ util.inspect.defaultOptions = {
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#decodeRawTransaction', () => {
@@ -192,8 +191,7 @@ describe('#rawtransaction', () => {
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
assert.include(err.message, 'Array too large')
}
})
})
@@ -252,9 +250,13 @@ describe('#rawtransaction', () => {
describe('#_getInputAddrs', () => {
it('should return an array of input addresses', async () => {
// const txid = '32233db13f2ae6d82b6262f335643dccf09fc0bcfcef4bc3fbe023355f02e112'
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const txid =
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const txDetails = await bchjs.RawTransactions.getRawTransaction(txid, true)
const txDetails = await bchjs.RawTransactions.getRawTransaction(
txid,
true
)
const result = await bchjs.RawTransactions._getInputAddrs(txDetails)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -263,43 +265,46 @@ describe('#rawtransaction', () => {
assert.equal(result.length, 1)
assert.property(result[0], 'vin')
assert.property(result[0], 'address')
assert.property(result[0], 'value')
})
})
describe('#getTxData', () => {
it('should return tx data with input addresses', async () => {
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const txid =
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const result = await bchjs.RawTransactions.getTxData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
})
})
describe('#getTxDataSlp', () => {
it('should return tx data with SLP information', async () => {
const txid = 'b438855cfcab64516b44097d7212df9cdb99226c8d7c7ab504d35fcfd834cb5b'
const result = await bchjs.RawTransactions.getTxDataSlp(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'tokenQty')
assert.equal(result.vin[0].tokenQty, null)
assert.equal(result.vin[1].tokenQty, '100000000000')
})
it('should handle non-slp tx', async () => {
const txid = '04a6aca328af2445327015895b8da9766093b1989b52e477559759eb8072fc0a'
const result = await bchjs.RawTransactions.getTxDataSlp(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, false)
})
})
// describe('#getTxDataSlp', () => {
// it('should return tx data with SLP information', async () => {
// const txid = 'b438855cfcab64516b44097d7212df9cdb99226c8d7c7ab504d35fcfd834cb5b'
//
// const result = await bchjs.RawTransactions.getTxDataSlp(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.property(result.vin[0], 'address')
// assert.property(result.vin[0], 'tokenQty')
//
// assert.equal(result.vin[0].tokenQty, null)
// assert.equal(result.vin[1].tokenQty, '100000000000')
// })
//
// it('should handle non-slp tx', async () => {
// const txid = '04a6aca328af2445327015895b8da9766093b1989b52e477559759eb8072fc0a'
//
// const result = await bchjs.RawTransactions.getTxDataSlp(txid)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.equal(result, false)
// })
// })
})
function sleep (ms) {
+95 -2
View File
@@ -24,7 +24,7 @@ describe('#SLP', () => {
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(1000)
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS()
})
@@ -639,7 +639,7 @@ describe('#SLP', () => {
})
})
describe('#hydrateUtxos', () => {
describe('#hydrateUtxos-', () => {
it('should hydrate UTXOs', async () => {
const utxos = [
{
@@ -766,6 +766,99 @@ describe('#SLP', () => {
})
})
describe('#hydrateUtxosWL', () => {
it('should hydrate UTXOs', async () => {
const utxos = [
{
utxos: [
{
txid:
'89b3f0c84efe8b01b24e2d7ac08636de5781f31dbb84478e3de868ca0a7ed93a',
vout: 1,
value: 546
}
]
}
]
const result = await bchjs.SLP.Utils.hydrateUtxosWL(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, 1)
// 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], 'isValid')
assert.equal(result.slpUtxos[0].utxos[0].isValid, true)
})
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.hydrateUtxosWL(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, null)
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)
}
})
it('should handle null SLPDB validations', async () => {
const utxos = [
{
height: 665577,
tx_hash:
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419',
tx_pos: 1,
value: 546
},
{
height: 665577,
tx_hash:
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.hydrateUtxosWL([{ utxos: utxos }])
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data.slpUtxos)
assert.equal(data.slpUtxos[0].isValid, null)
})
})
describe('#validateTxid', () => {
// This test is not necessary.
it('should handle a null response from SLPDB', async () => {
@@ -0,0 +1,81 @@
/*
Integration tests for the transaction.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
describe('#Transaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#get', () => {
it('should get details about a non-SLP transaction', async () => {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[0].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, false)
})
it('should get details about a SLP transaction', async () => {
const txid =
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vout[0], 'tokenQty')
assert.equal(result.vout[0].tokenQty, null)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result.vin[0], 'tokenQty')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, true)
})
// it('should get problematic transaction', async () => {
// const txid = 'a55515de32577e296c512840bcaabed5823bb773fb4f8fd8e5197cc96cbc54d1'
//
// const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// })
// TX a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe has
// an input TXID (bd84bc1dd5ecd976165892306992401272f6bedeb37d7b2cdbf74fc4a55967a6)
// that is also a valid SLP tx, but is unrelated. Both TXs pass DAG validation,
// but for separate tokens.
it('should get problematic transaction', async () => {
const txid = 'a19f2f395a8b0e15b6202944c56834367d128f1e3630486a4756de53424a46fe'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// The token ID should equal the txid for this Vin.
assert.equal(result.vin[2].txid, result.vin[2].tokenId)
})
})
})
+28 -7
View File
@@ -61,16 +61,40 @@ describe('#Blockchain', () => {
it('should get block by hash', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
sandbox.stub(axios, 'get').returns(resolved)
sandbox.stub(axios, 'post').returns(resolved)
bchjs.Blockchain.getBlock(
const blockhash =
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
)
bchjs.Blockchain.getBlock(blockhash)
.then(result => {
assert.deepStrictEqual(data, result)
})
.then(done, done)
})
it('should throw error if blockhash is not provided', async () => {
try {
await bchjs.Blockchain.getBlock()
assert2.fail('Unexpected result')
} catch (err) {
assert2.include(err.message, 'blockhash must be a string')
}
})
it('should handle response error', async () => {
try {
const error = new Error()
error.response = {
data: 'Test Error'
}
sandbox.stub(axios, 'post').throws(error)
const blockhash =
'00000000c937983704a73af28acdec37b049d214adbda81d7e2a3dd146f6ed09'
await bchjs.Blockchain.getBlock(blockhash)
assert2.fail('Unexpected result')
} catch (err) {
assert2.include(err, 'Test Error')
}
})
})
describe('#getBlockchainInfo', () => {
@@ -355,10 +379,7 @@ describe('#Blockchain', () => {
try {
await bchjs.Blockchain.getTxOut('badtxid')
} catch (err) {
assert2.include(
err.message,
'txid needs to be a proper transaction ID'
)
assert2.include(err.message, 'txid needs to be a proper transaction ID')
}
})
+4 -8
View File
@@ -5,11 +5,11 @@ const bchjs = new BCHJS()
const sinon = require('sinon')
describe('#Control', () => {
describe('#getNetworkInfo', () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
describe('#getNetworkInfo', () => {
it('should get info', done => {
const data = {
version: 170000,
@@ -36,10 +36,6 @@ describe('#Control', () => {
})
describe('#getMemoryInfo', () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
it('should get memory info', done => {
const data = {
locked: {
+66
View File
@@ -0,0 +1,66 @@
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
// Unit under test (uut)
const BCHJS = require('../../src/bch-js')
const mockData = require('./fixtures/dsproof-mock')
let bchjs
const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
describe('#DSProof', () => {
let sandbox
beforeEach(() => {
bchjs = new BCHJS()
sandbox = sinon.createSandbox()
})
afterEach(() => sandbox.restore())
describe('#getDSProof', () => {
it('should throw error if input is not provided', async () => {
try {
await bchjs.DSProof.getDSProof()
assert.equal(false, true, 'unexpected error')
} catch (err) {
assert.include(err.message, 'txid is required')
}
})
it('should throw error txid is invalid', async () => {
try {
await bchjs.DSProof.getDSProof('txid')
assert.equal(false, true, 'unexpected error')
} catch (err) {
assert.include(err.message, 'txid must be of length 64')
}
})
it('should handle error', async () => {
try {
sandbox.stub(bchjs.DSProof.axios, 'get').throws(new Error('test error'))
await bchjs.DSProof.getDSProof(txid)
assert.equal(false, true, 'unexpected error')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should get double spend proof', async () => {
try {
sandbox
.stub(bchjs.DSProof.axios, 'get')
.resolves({ data: mockData.dsproof })
const result = await bchjs.DSProof.getDSProof(txid)
assert.property(result, 'dspid')
assert.property(result, 'txid')
assert.property(result, 'outpoint')
assert.property(result, 'path')
assert.property(result, 'descendants')
assert.property(result.outpoint, 'txid')
assert.property(result.outpoint, 'vout')
} catch (error) {
assert.equal(false, true, 'unexpected error')
}
})
})
})
+19
View File
@@ -0,0 +1,19 @@
/*
Mock data used for unit testing.
*/
const dsproof = {
dspid: '6234fff2a9dbcaa50e2c50d74e1d725a2bed1757d7a05a2db7a82df659554397',
txid: 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e',
outpoint: {
txid: '17ebef34f7e9a0692317dd6fbb43ffccff45b7d688a2a754dbccfc5e6d93ce3e',
vout: 1
},
path: ['ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'],
descendants: [
'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
]
}
module.exports = {
dsproof
}
+127 -1
View File
@@ -119,6 +119,33 @@ const mockTokens = [
}
]
const mockNftChildrenList = {
nftChildren: [
'45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9',
'928ce61fe1006b1325a0ba0dce700bf83986a6f0691ba26e121c9ac035d12a55'
]
}
const mockNftGroup = {
nftGroup: {
decimals: 0,
timestamp: '2021-05-03 10:36:01',
timestamp_unix: 1620038161,
versionType: 129,
documentUri: 'psfoundation.cash',
symbol: 'PSF.TEST.GROUP',
name: 'PSF Test NFT Group',
containsBaton: true,
id: '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a',
documentHash: null,
initialTokenQty: 1000000,
blockCreated: 686117,
totalMinted: null,
totalBurned: null,
circulatingSupply: null
}
}
const balancesForAddress = [
{
tokenId: 'df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb',
@@ -1290,10 +1317,106 @@ const slpdbStatus = {
}
}
const txDetailsSLPNftChild = {
txid: 'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b',
hash: 'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b',
version: 2,
size: 453,
locktime: 0,
vin: [
{
txid: 'a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395',
vout: 1,
scriptSig: {
asm:
'3045022100890ff2f80f3c184419ba03a575b9d159c0a5edfa817e4548cfdb6c74d16ce8d202205a8237d2139196716c273fe80506896d369a0022bb54401e2eadb364286a9f07[ALL|FORKID] 03136d9ec06adef1c9154105950e82d470d290aa019ed34a5e3bbd0b679805623b',
hex:
'483045022100890ff2f80f3c184419ba03a575b9d159c0a5edfa817e4548cfdb6c74d16ce8d202205a8237d2139196716c273fe80506896d369a0022bb54401e2eadb364286a9f07412103136d9ec06adef1c9154105950e82d470d290aa019ed34a5e3bbd0b679805623b'
},
sequence: 4294967295
},
{
txid: 'a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395',
vout: 3,
scriptSig: {
asm:
'304402203905a61ec4e3567f7cad7eb514132e5b32d8a1d77c8838baac1b34b735282a5f022044da6130b5cc41b65615b17df6610087fe1baedd1a3a9f37552b56ea8d36d026[ALL|FORKID] 03136d9ec06adef1c9154105950e82d470d290aa019ed34a5e3bbd0b679805623b',
hex:
'47304402203905a61ec4e3567f7cad7eb514132e5b32d8a1d77c8838baac1b34b735282a5f022044da6130b5cc41b65615b17df6610087fe1baedd1a3a9f37552b56ea8d36d026412103136d9ec06adef1c9154105950e82d470d290aa019ed34a5e3bbd0b679805623b'
},
sequence: 4294967295
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm:
'OP_RETURN 5262419 65 47454e45534953 4e4654303034 4e4654204368696c64 68747470733a2f2f46756c6c537461636b2e63617368 0 0 0 0000000000000001',
hex:
'6a04534c500001410747454e45534953064e4654303034094e4654204368696c641668747470733a2f2f46756c6c537461636b2e636173684c0001004c00080000000000000001',
type: 'nulldata'
}
},
{
value: 0.00000546,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 f6fc6bce702ef779d2373aa4356a32113d95e674 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914f6fc6bce702ef779d2373aa4356a32113d95e67488ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv']
}
},
{
value: 0.0003923,
n: 2,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 f6fc6bce702ef779d2373aa4356a32113d95e674 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914f6fc6bce702ef779d2373aa4356a32113d95e67488ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv']
}
}
],
hex:
'020000000295e3d0920df2e9467bf4c6dee49d5ba37f2e6f255c72b8d561079f0f8a45a2a9010000006b483045022100890ff2f80f3c184419ba03a575b9d159c0a5edfa817e4548cfdb6c74d16ce8d202205a8237d2139196716c273fe80506896d369a0022bb54401e2eadb364286a9f07412103136d9ec06adef1c9154105950e82d470d290aa019ed34a5e3bbd0b679805623bffffffff95e3d0920df2e9467bf4c6dee49d5ba37f2e6f255c72b8d561079f0f8a45a2a9030000006a47304402203905a61ec4e3567f7cad7eb514132e5b32d8a1d77c8838baac1b34b735282a5f022044da6130b5cc41b65615b17df6610087fe1baedd1a3a9f37552b56ea8d36d026412103136d9ec06adef1c9154105950e82d470d290aa019ed34a5e3bbd0b679805623bffffffff030000000000000000476a04534c500001410747454e45534953064e4654303034094e4654204368696c641668747470733a2f2f46756c6c537461636b2e636173684c0001004c0008000000000000000122020000000000001976a914f6fc6bce702ef779d2373aa4356a32113d95e67488ac3e990000000000001976a914f6fc6bce702ef779d2373aa4356a32113d95e67488ac00000000',
blockhash: '000000000000000001360fe42629f4136e9adec53546b278de0e7ef788eed328',
confirmations: 2113,
time: 1613181364,
blocktime: 1613181364
}
const mock429Error = new Error('Request failed with status code 429')
mock429Error.response = {
status: 429,
statusText: 'Too Many Requests',
data: {
error:
'Too many requests. Your limits are currently 100 requests per minute. Increase rate limits at https://fullstack.cash'
}
}
const mock503Error = new Error('Request failed with status code 503')
mock503Error.response = {
status: 503,
statusText: 'Service Unavailable',
data: {
error: 'Some error message'
}
}
module.exports = {
mockList,
mockToken,
mockTokens,
mockNftChildrenList,
mockNftGroup,
balancesForAddress,
balancesForAddresses,
mockRawTx,
@@ -1311,6 +1434,7 @@ module.exports = {
txDetailsSLPGenesisNoBaton,
txDetailsSLPSendAlt,
txDetailsSLPNftGenesis,
txDetailsSLPNftChild,
mockTxDetails,
mockDualValidation,
mockDualOpData,
@@ -1320,5 +1444,7 @@ module.exports = {
mockValidateTxid3Invalid,
whitelist,
slpdbStatus,
mockValidateTxidArray
mockValidateTxidArray,
mock429Error,
mock503Error
}
+795
View File
@@ -0,0 +1,795 @@
/*
Mocking data used in the transaction-unit.js tests.
*/
const nonSlpTxDetails = {
txid: '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
hash: '2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
version: 2,
size: 225,
locktime: 0,
vin: [
{
txid: '5f09d317e24c5d376f737a2711f3bd1d381abdb41743fff3819b4f76382e1eac',
vout: 1,
scriptSig: {
asm:
'3044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c[ALL|FORKID] 038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9',
hex:
'473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9'
},
sequence: 4294967295,
address: 'bitcoincash:qqxy8hycqe89j7wa79gnggq6z3gaqu2uvqy26xehfe',
value: 0.00047504
}
],
vout: [
{
value: 0.00001,
n: 0,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 2fe2c4c5ef359bb2fe1a849f891cecffbcfb4f77 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9']
}
},
{
value: 0.00046256,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 2dbf5e1804c39a497b908c876097d63210c84902 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9142dbf5e1804c39a497b908c876097d63210c8490288ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqkm7hscqnpe5jtmjzxgwcyh6ceppjzfqg3jdn422e']
}
}
],
hex:
'0200000001ac1e2e38764f9b81f3ff4317b4bd1a381dbdf311277a736f375d4ce217d3095f010000006a473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9ffffffff02e8030000000000001976a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788acb0b40000000000001976a9142dbf5e1804c39a497b908c876097d63210c8490288ac00000000',
blockhash: '0000000000000000010903a1fc4274499037c9339be9ec7338ee980331c20ce5',
confirmations: 77741,
time: 1569792892,
blocktime: 1569792892
}
const slpTxDetails = {
txid: '266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1',
hash: '266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1',
version: 2,
size: 479,
locktime: 0,
vin: [
{
txid: 'abc685f1f2a95f51e5e05a350f3fb9c74676e9f78c835b2a019c888ac0a2a736',
vout: 2,
scriptSig: {
asm:
'3045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f7[ALL|FORKID] 0245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07ac',
hex:
'483045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f741210245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07ac'
},
sequence: 4294967295,
address: 'bitcoincash:qzv7t2pzn2d0pklnetdjt65crh6fe8vnhuwvhsk2nn',
value: 0.00000546
},
{
txid: '58c8576404c01c23a224053307399483d3a070599b3e9eb6d45be9714b8d6856',
vout: 1,
scriptSig: {
asm:
'30430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db12753[ALL|FORKID] 0209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505',
hex:
'4630430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db1275341210209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505'
},
sequence: 4294967295,
address: 'bitcoincash:qppzuxemgqyxf07nz3kan33gmc83mf3z3yz295c4s7',
value: 0.68369626
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm:
'OP_RETURN 5262419 1 1145980243 497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7 0000000005f5e100 00005ad7e49d9100',
hex:
'6a04534c500001010453454e4420497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7080000000005f5e1000800005ad7e49d9100',
type: 'nulldata'
}
},
{
value: 0.00000546,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 36be3b7d185a85b6cf6fc61d63c16f2f10e54260 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91436be3b7d185a85b6cf6fc61d63c16f2f10e5426088ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqmtuwmarpdgtdk0dlrp6c7pduh3pe2zvqrkys2ex8']
}
},
{
value: 0.00000546,
n: 2,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 99e5a8229a9af0dbf3cadb25ea981df49c9d93bf OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91499e5a8229a9af0dbf3cadb25ea981df49c9d93bf88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qzv7t2pzn2d0pklnetdjt65crh6fe8vnhuwvhsk2nn']
}
},
{
value: 0.68368564,
n: 3,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 422e1b3b400864bfd3146dd9c628de0f1da62289 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914422e1b3b400864bfd3146dd9c628de0f1da6228988ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qppzuxemgqyxf07nz3kan33gmc83mf3z3yz295c4s7']
}
}
],
hex:
'020000000236a7a2c08a889c012a5b838cf7e97646c7b93f0f355ae0e5515fa9f2f185c6ab020000006b483045022100e5f0f6f1212fcbb10eedb7fdc38fca6e86629b4e7e8356a3ad7371a109fc37a602204bfff37d1a34d2e2908b81c23677706fb59ff4ab639fa3299da6c303de74e1f741210245b9b3586fab3cfd46db6d116c4588004fe7fe9798216ccb8e55a89bcebc07acffffffff56688d4b71e95bd4b69e3e9b5970a0d383943907330524a2231cc0046457c85801000000694630430220784f6d81fa8f54db8a4948259e8c15972a0285f8b1640c433d4e9f606dc38f0c021f14eecc2e8af2efede0867ce459c400dde54186a0e64babdbe89f795db1275341210209ebe6d9da5043945ed1d81bec0fcace299eba05e5f46b72d6838c790d31c505ffffffff040000000000000000406a04534c500001010453454e4420497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7080000000005f5e1000800005ad7e49d910022020000000000001976a91436be3b7d185a85b6cf6fc61d63c16f2f10e5426088ac22020000000000001976a91499e5a8229a9af0dbf3cadb25ea981df49c9d93bf88acb4381304000000001976a914422e1b3b400864bfd3146dd9c628de0f1da6228988ac00000000',
blockhash: '0000000000000000015284202422a688554b7fc80c54f18122847a99c4f79607',
confirmations: 76722,
time: 1570392893,
blocktime: 1570392893
}
const mockOpReturnData01 = {
tokenType: 1,
txType: 'SEND',
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['100000000', '99883300000000']
}
const mockOpReturnData02 = {
tokenType: 1,
txType: 'GENESIS',
ticker: 'TOK-CH',
name: 'TokyoCash',
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
documentUri: '',
documentHash: '',
decimals: 8,
mintBatonVout: 0,
qty: '2100000000000000'
}
const mockOpReturnData03 = {
tokenType: 1,
txType: 'SEND',
tokenId: '497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['1000000000', '99883400000000']
}
const genesisTestInputTx = {
txid: '874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e',
hash: '874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e',
version: 1,
size: 480,
locktime: 543408,
vin: [
{
txid: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
vout: 1,
scriptSig: {
asm:
'30440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f55[ALL|FORKID] 028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11',
hex:
'4730440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f554121028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11'
},
sequence: 4294967294,
address: 'bitcoincash:qp2jesd06k8ycj4wvkpl9lcwaemtr04f5yphjsa07v',
value: 0.00000546
},
{
txid: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
vout: 3,
scriptSig: {
asm:
'3045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519[ALL|FORKID] 02cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bc',
hex:
'483045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519412102cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bc'
},
sequence: 4294967294,
address: 'bitcoincash:qppj3euc36x5u6twr5cxrrea2rca53vsfu3dxwr86j',
value: 0.00172192
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm:
'OP_RETURN 5262419 1 1145980243 323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35 00000000004c4b40 00000000004c4b40',
hex:
'6a04534c500001010453454e4420323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d350800000000004c4b400800000000004c4b40',
type: 'nulldata'
}
},
{
value: 0.00000546,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 0a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e0 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9140a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e088ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qq98fnuup7eldhtzc067ang76mss29pguqh7qv9eac']
}
},
{
value: 0.00000546,
n: 2,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 d4548261e1be0de7e50b7511597799ec4af2b173 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914d4548261e1be0de7e50b7511597799ec4af2b17388ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qr29fqnpuxlqmel9pd63zkthn8ky4u43wv0v7pg5mn']
}
},
{
value: 0.00171165,
n: 3,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 d4548261e1be0de7e50b7511597799ec4af2b173 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914d4548261e1be0de7e50b7511597799ec4af2b17388ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qr29fqnpuxlqmel9pd63zkthn8ky4u43wv0v7pg5mn']
}
}
],
hex:
'0100000002359dab0d9c8ddc788b14c0b51493d195c9fbd9f2203d091663350bae351e3a32010000006a4730440220268dacee1117975d904dd0d45ef8de42b86030d825a9522bae196a38bbf6b271022001ae1ce2536ab300040e597bcfaa8ef9fb2beaf702d0842f3161aae8e9867f554121028ff9e32b0dbc82c1d5e0fc945b2537b00420513b10684726f312f1b717c0ae11feffffff359dab0d9c8ddc788b14c0b51493d195c9fbd9f2203d091663350bae351e3a32030000006b483045022100fa241bb2de46f68688451bfcae3f165b724e3ccf13b219e7bf2d8d2df7712ad60220353017d6e581a06efce478adfcd2047cea2f92531e283845f3d0a345ef101519412102cc48ad10516f97e914b8836ff25448d07ad96ebb4704c6a828339880280831bcfeffffff040000000000000000406a04534c500001010453454e4420323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d350800000000004c4b400800000000004c4b4022020000000000001976a9140a74cf9c0fb3f6dd62c3f5eecd1ed6e1051428e088ac22020000000000001976a914d4548261e1be0de7e50b7511597799ec4af2b17388ac9d9c0200000000001976a914d4548261e1be0de7e50b7511597799ec4af2b17388acb04a0800',
blockhash: '000000000000000000292a9c6150fce48e2edd8df346948494fe6249e6e7f63b',
confirmations: 163095,
time: 1534271330,
blocktime: 1534271330
}
const mintTestInputTx = {
txid: '4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa',
hash: '4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa',
version: 1,
size: 585,
locktime: 543613,
vin: [
{
txid: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
vout: 1,
scriptSig: {
asm:
'3045022100e3d5f9a48f9aa1cf7e9ed1992e0281e7300b734ba0e4bd5bb9265b2be60bd71002200ccde53ce7ea9da9df3e834f234f3bea65c2e58c96f8436f93333fd8946a1db2[ALL|FORKID] 02056220984cc2cf5261a27d4f66d31c9ef601a688ca1a5ab81e55b6f0d311be74',
hex:
'483045022100e3d5f9a48f9aa1cf7e9ed1992e0281e7300b734ba0e4bd5bb9265b2be60bd71002200ccde53ce7ea9da9df3e834f234f3bea65c2e58c96f8436f93333fd8946a1db2412102056220984cc2cf5261a27d4f66d31c9ef601a688ca1a5ab81e55b6f0d311be74'
},
sequence: 4294967294,
address: 'bitcoincash:qpaf9wltgmpjlg2vxwwu7zdw5y4z7m277ckxn8cufl',
value: 0.00000546,
tokenQtyStr: '43545.34534',
tokenQty: 43545.34534
},
{
txid: 'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34',
vout: 1,
scriptSig: {
asm:
'3045022100bb90d52be9b568f643fdb1a302e9f063be27a9f914e2a6b192cbabf2cbdeaf9302203c275fc8d1b82390bc1726390d361c5266056bddd54dec23efa207c79eacea4a[ALL|FORKID] 024146cfcd0c02e99d6451dc48ad0f97114aeda18fe55c52a10bfc0490f314e6a2',
hex:
'483045022100bb90d52be9b568f643fdb1a302e9f063be27a9f914e2a6b192cbabf2cbdeaf9302203c275fc8d1b82390bc1726390d361c5266056bddd54dec23efa207c79eacea4a4121024146cfcd0c02e99d6451dc48ad0f97114aeda18fe55c52a10bfc0490f314e6a2'
},
sequence: 4294967294,
address: 'bitcoincash:qqf89zzwd0fqc3npkks997r5l7dpfjf9kgx2rqu500',
value: 0.00000546,
tokenQtyStr: '2.34123',
tokenQty: 2.34123
},
{
txid: 'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34',
vout: 3,
scriptSig: {
asm:
'3044022029ab770c249f467b40bb90410429f6d657f9be299baa19a6838d04d972436b450220143660297e8e836bc16d317649fad8c310bdadb4fb61d4e9f6ba11203abe5dae[ALL|FORKID] 02eed4ac9dda3405d9b1ccbbf09f8056f3e7c615924274bd5643238b543d0a1d56',
hex:
'473044022029ab770c249f467b40bb90410429f6d657f9be299baa19a6838d04d972436b450220143660297e8e836bc16d317649fad8c310bdadb4fb61d4e9f6ba11203abe5dae412102eed4ac9dda3405d9b1ccbbf09f8056f3e7c615924274bd5643238b543d0a1d56'
},
sequence: 4294967294,
address: 'bitcoincash:qqt30r33k0jx3sxe34tmupaujpaljnglmvqgrrfp2x',
value: 0.00054848,
tokenQtyStr: '2.34123',
tokenQty: 2.34123
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm:
'OP_RETURN 5262419 1 1145980243 938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8 0000000103907f11',
hex:
'6a04534c500001010453454e4420938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8080000000103907f11',
type: 'nulldata'
},
tokenQty: null
},
{
value: 0.00000546,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 059775ff94f65c04e8a6847e74eb9809d8cd779b OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914059775ff94f65c04e8a6847e74eb9809d8cd779b88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqzewa0ljnm9cp8g56z8ua8tnqya3nthnvhv5hpu8y']
},
tokenQtyStr: '43547.68657',
tokenQty: 43547.68657
},
{
value: 0.00054808,
n: 2,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 0e330dc9009e1e07831f5a22d4ade8977ab674c8 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9140e330dc9009e1e07831f5a22d4ade8977ab674c888ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qq8rxrwfqz0pupurradz949dazth4dn5eqs3mhrucv']
},
tokenQty: null
}
],
hex:
'0100000003f8cda53b43b0ea49991a9bc6c74eb901d2a8b964bc7b8987d76789618ec18c93010000006b483045022100e3d5f9a48f9aa1cf7e9ed1992e0281e7300b734ba0e4bd5bb9265b2be60bd71002200ccde53ce7ea9da9df3e834f234f3bea65c2e58c96f8436f93333fd8946a1db2412102056220984cc2cf5261a27d4f66d31c9ef601a688ca1a5ab81e55b6f0d311be74feffffff34dbf62a45295ae11c2a45f44368218ec64498ac3f7e1434c1993515f53c9dee010000006b483045022100bb90d52be9b568f643fdb1a302e9f063be27a9f914e2a6b192cbabf2cbdeaf9302203c275fc8d1b82390bc1726390d361c5266056bddd54dec23efa207c79eacea4a4121024146cfcd0c02e99d6451dc48ad0f97114aeda18fe55c52a10bfc0490f314e6a2feffffff34dbf62a45295ae11c2a45f44368218ec64498ac3f7e1434c1993515f53c9dee030000006a473044022029ab770c249f467b40bb90410429f6d657f9be299baa19a6838d04d972436b450220143660297e8e836bc16d317649fad8c310bdadb4fb61d4e9f6ba11203abe5dae412102eed4ac9dda3405d9b1ccbbf09f8056f3e7c615924274bd5643238b543d0a1d56feffffff030000000000000000376a04534c500001010453454e4420938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8080000000103907f1122020000000000001976a914059775ff94f65c04e8a6847e74eb9809d8cd779b88ac18d60000000000001976a9140e330dc9009e1e07831f5a22d4ade8977ab674c888ac7d4b0800',
blockhash: '00000000000000000140f0d813052da59c811a936494a8d8b2d60b215d19e3dc',
confirmations: 167007,
time: 1534391953,
blocktime: 1534391953,
isValidSLPTx: true,
tokenTxType: 'SEND',
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
tokenTicker: 'Bubb2',
tokenName: 'the new bubbles!',
tokenDecimals: 5,
tokenUri: '',
tokenDocHash: ''
}
const genesisTestOpReturnData01 = {
tokenType: 1,
txType: 'SEND',
tokenId: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
amounts: ['5000000', '5000000']
}
const genesisTestOpReturnData02 = {
tokenType: 1,
txType: 'GENESIS',
ticker: '',
name: '',
tokenId: '323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35',
documentUri: '',
documentHash: '',
decimals: 0,
mintBatonVout: 2,
qty: '10000000'
}
const mintTestOpReturnData01 = {
tokenType: 1,
txType: 'SEND',
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
amounts: ['4354768657']
}
const mintTestOpReturnData02 = {
tokenType: 1,
txType: 'GENESIS',
ticker: 'Bubb2',
name: 'the new bubbles!',
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
documentUri: '',
documentHash: '',
decimals: 5,
mintBatonVout: 2,
qty: '4354534534'
}
const mintTestOpReturnData03 = {
tokenType: 1,
txType: 'MINT',
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
mintBatonVout: 2,
qty: '234123'
}
const sendTestInputTx01 = {
txid: '6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a',
hash: '6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a',
version: 1,
size: 627,
locktime: 543956,
vin: [
{
txid: 'b36b0c7485ad569b98cc9b9614dc68a5208495f22ec3b00effcf963b135a5215',
vout: 1,
scriptSig: {
asm:
'3045022100934ca9732bf6c1b146f09e6198142a79a80788b576c6b94e2052df5bcdc86ad902205ef6205192200ea93f6c97f966b838ee785529b694eb61e576e3452470613847[ALL|FORKID] 02b23484341cf36ec00184e045d54e6ffca97da7a8f8a6fd719cead192132306b1',
hex:
'483045022100934ca9732bf6c1b146f09e6198142a79a80788b576c6b94e2052df5bcdc86ad902205ef6205192200ea93f6c97f966b838ee785529b694eb61e576e3452470613847412102b23484341cf36ec00184e045d54e6ffca97da7a8f8a6fd719cead192132306b1'
},
sequence: 4294967294,
address: 'bitcoincash:qqxj7p2jatt8h3tcpadyxw8a2mr7myqk2qm57u4rdf',
value: 0.00000546
},
{
txid: 'b36b0c7485ad569b98cc9b9614dc68a5208495f22ec3b00effcf963b135a5215',
vout: 3,
scriptSig: {
asm:
'3044022004d4a95658ef140561ae3a936fb01511778a5376a499bcd6f47b64074a6ca4430220273a4bf553ca17c7e10f393582f980e17a6a98ada867633faad9dbcc595dfd23[ALL|FORKID] 0324fe3b895cfa09523e8ec529eb15c681e3a20f96df85175c54a952b68ac018df',
hex:
'473044022004d4a95658ef140561ae3a936fb01511778a5376a499bcd6f47b64074a6ca4430220273a4bf553ca17c7e10f393582f980e17a6a98ada867633faad9dbcc595dfd2341210324fe3b895cfa09523e8ec529eb15c681e3a20f96df85175c54a952b68ac018df'
},
sequence: 4294967294,
address: 'bitcoincash:qrcdncgpnv3s8lekcamv4yekp8sghnk37qtsxdyzla',
value: 0.00153976
},
{
txid: '95d460512143b636bbc5780d8b27b04fca3bfd2f22003ab48da594e2bab9cfc1',
vout: 2,
scriptSig: {
asm:
'3044022079f239216f8d806a084221e841227e55d23a1b90aaf5b5ab54d6f865fbeacf8b022053568f10afc00459af083e4c77d0b85229eb8973a1de406aaa699557f05c7fed[ALL|FORKID] 02035f98ff79d6f3a8ec5a2738c103bbb7796e94fcaffdfaae8f5b7a7fae5d93e3',
hex:
'473044022079f239216f8d806a084221e841227e55d23a1b90aaf5b5ab54d6f865fbeacf8b022053568f10afc00459af083e4c77d0b85229eb8973a1de406aaa699557f05c7fed412102035f98ff79d6f3a8ec5a2738c103bbb7796e94fcaffdfaae8f5b7a7fae5d93e3'
},
sequence: 4294967294,
address: 'bitcoincash:qrcukahsm53lsywcdarehh73vqazpdu8zv547nym9g',
value: 0.00000546
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm:
'OP_RETURN 5262419 1 1145980243 550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35 00005af3107a4000 004657febe8d8000',
hex:
'6a04534c500001010453454e4420550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b350800005af3107a400008004657febe8d8000',
type: 'nulldata'
}
},
{
value: 0.00000546,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 268dadd7c7becc11782ad3d956342442d8eae92a OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914268dadd7c7becc11782ad3d956342442d8eae92a88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqngmtwhc7lvcytc9tfaj435y3pd36hf9gtdq8nsuu']
}
},
{
value: 0.00000546,
n: 2,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 9fb45b370f9ff38a775ee95cabcad7615817924e OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9149fb45b370f9ff38a775ee95cabcad7615817924e88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qz0mgkehp70l8znhtm54e2726as4s9ujfc9s2szctr']
}
},
{
value: 0.00153347,
n: 3,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 9fb45b370f9ff38a775ee95cabcad7615817924e OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9149fb45b370f9ff38a775ee95cabcad7615817924e88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qz0mgkehp70l8znhtm54e2726as4s9ujfc9s2szctr']
}
}
],
hex:
'010000000315525a133b96cfff0eb0c32ef2958420a568dc14969bcc989b56ad85740c6bb3010000006b483045022100934ca9732bf6c1b146f09e6198142a79a80788b576c6b94e2052df5bcdc86ad902205ef6205192200ea93f6c97f966b838ee785529b694eb61e576e3452470613847412102b23484341cf36ec00184e045d54e6ffca97da7a8f8a6fd719cead192132306b1feffffff15525a133b96cfff0eb0c32ef2958420a568dc14969bcc989b56ad85740c6bb3030000006a473044022004d4a95658ef140561ae3a936fb01511778a5376a499bcd6f47b64074a6ca4430220273a4bf553ca17c7e10f393582f980e17a6a98ada867633faad9dbcc595dfd2341210324fe3b895cfa09523e8ec529eb15c681e3a20f96df85175c54a952b68ac018dffeffffffc1cfb9bae294a58db43a00222ffd3bca4fb0278b0d78c5bb36b643215160d495020000006a473044022079f239216f8d806a084221e841227e55d23a1b90aaf5b5ab54d6f865fbeacf8b022053568f10afc00459af083e4c77d0b85229eb8973a1de406aaa699557f05c7fed412102035f98ff79d6f3a8ec5a2738c103bbb7796e94fcaffdfaae8f5b7a7fae5d93e3feffffff040000000000000000406a04534c500001010453454e4420550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b350800005af3107a400008004657febe8d800022020000000000001976a914268dadd7c7becc11782ad3d956342442d8eae92a88ac22020000000000001976a9149fb45b370f9ff38a775ee95cabcad7615817924e88ac03570200000000001976a9149fb45b370f9ff38a775ee95cabcad7615817924e88acd44c0800',
blockhash: '000000000000000001882548bbd9a41bc53a039634f2afa4a12d653434347d04',
confirmations: 166670,
time: 1534593905,
blocktime: 1534593905
}
const sendTestOpReturnData01 = {
tokenType: 1,
txType: 'SEND',
tokenId: '550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35',
amounts: ['100000000000000', '19800000000000000']
}
const sendTestOpReturnData02 = {
tokenType: 1,
txType: 'GENESIS',
ticker: 'USDT',
name: 'Tether Ltd. US dollar backed tokens',
tokenId: '550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35',
documentUri:
'https://tether.to/wp-content/uploads/2016/06/TetherWhitePaper.pdf',
documentHash:
'DQ\u001e3\u0006ppM\u0001\u0017pW(;\u0003,wy19\u0016',
decimals: 8,
mintBatonVout: 2,
qty: '10000000000000000'
}
const sendTestOpReturnData03 = {
tokenType: 1,
txType: 'MINT',
tokenId: '550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35',
mintBatonVout: 2,
qty: '10000000000000000'
}
const sendTestOpReturnData04 = {
tokenType: 1,
txType: 'SEND',
tokenId: '550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35',
amounts: ['100000000000000', '9900000000000000']
}
const mintTestInputTx02 = {
txid: 'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34',
hash: 'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34',
version: 1,
size: 473,
locktime: 543613,
vin: [
{
txid: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
vout: 3,
scriptSig: {
asm:
'3045022100ac43a871e0f84787001db745e4801b789fd9cd7efa247727ebb5c0c7b66d597302205316328b6501759e9a15ec65202219a433172d153f9f1b68f65f3a63494ba6f0[ALL|FORKID] 0345a5f5dd11c987b346c18c245f0df6d305369984b9b3c7ba465c29875466e374',
hex:
'483045022100ac43a871e0f84787001db745e4801b789fd9cd7efa247727ebb5c0c7b66d597302205316328b6501759e9a15ec65202219a433172d153f9f1b68f65f3a63494ba6f041210345a5f5dd11c987b346c18c245f0df6d305369984b9b3c7ba465c29875466e374'
},
sequence: 4294967294,
address: 'bitcoincash:qqj0durd4qzdddvl2u6sen8n8h6mljtywugqctt9km',
value: 0.00056266
},
{
txid: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
vout: 2,
scriptSig: {
asm:
'304402202ce2d9d63bc6ab7765d267540fd3429bf7a8d1b50e975391fff3a4506289461a02205daf3f761a9b02d141f22136a6fe019062182c9da1b1d3a5245ff745f1236e45[ALL|FORKID] 02c00b05bc633a48e074bc54dd195b19a470ad6252ac8d306c82a2bd729c1c4c69',
hex:
'47304402202ce2d9d63bc6ab7765d267540fd3429bf7a8d1b50e975391fff3a4506289461a02205daf3f761a9b02d141f22136a6fe019062182c9da1b1d3a5245ff745f1236e45412102c00b05bc633a48e074bc54dd195b19a470ad6252ac8d306c82a2bd729c1c4c69'
},
sequence: 4294967294,
address: 'bitcoincash:qzf72qccdgjrpjpewq8dazlze6jyxzku8q3cauna4t',
value: 0.00000546
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm:
'OP_RETURN 5262419 1 1414416717 938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8 2 000000000003928b',
hex:
'6a04534c50000101044d494e5420938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8010208000000000003928b',
type: 'nulldata'
}
},
{
value: 0.00000546,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 1272884e6bd20c4661b5a052f874ff9a14c925b2 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9141272884e6bd20c4661b5a052f874ff9a14c925b288ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqf89zzwd0fqc3npkks997r5l7dpfjf9kgx2rqu500']
}
},
{
value: 0.00000546,
n: 2,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 d1eaebf8d1face5bd866e36bd94f43e2ffceef71 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914d1eaebf8d1face5bd866e36bd94f43e2ffceef7188ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qrg746lc68avuk7cvm3khk20g030lnh0wy5h2k2fqr']
}
},
{
value: 0.00054848,
n: 3,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 17178e31b3e468c0d98d57be07bc907bf94d1fdb OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91417178e31b3e468c0d98d57be07bc907bf94d1fdb88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qqt30r33k0jx3sxe34tmupaujpaljnglmvqgrrfp2x']
}
}
],
hex:
'0100000002f8cda53b43b0ea49991a9bc6c74eb901d2a8b964bc7b8987d76789618ec18c93030000006b483045022100ac43a871e0f84787001db745e4801b789fd9cd7efa247727ebb5c0c7b66d597302205316328b6501759e9a15ec65202219a433172d153f9f1b68f65f3a63494ba6f041210345a5f5dd11c987b346c18c245f0df6d305369984b9b3c7ba465c29875466e374fefffffff8cda53b43b0ea49991a9bc6c74eb901d2a8b964bc7b8987d76789618ec18c93020000006a47304402202ce2d9d63bc6ab7765d267540fd3429bf7a8d1b50e975391fff3a4506289461a02205daf3f761a9b02d141f22136a6fe019062182c9da1b1d3a5245ff745f1236e45412102c00b05bc633a48e074bc54dd195b19a470ad6252ac8d306c82a2bd729c1c4c69feffffff040000000000000000396a04534c50000101044d494e5420938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8010208000000000003928b22020000000000001976a9141272884e6bd20c4661b5a052f874ff9a14c925b288ac22020000000000001976a914d1eaebf8d1face5bd866e36bd94f43e2ffceef7188ac40d60000000000001976a91417178e31b3e468c0d98d57be07bc907bf94d1fdb88ac7d4b0800',
blockhash: '00000000000000000140f0d813052da59c811a936494a8d8b2d60b215d19e3dc',
confirmations: 168436,
time: 1534391953,
blocktime: 1534391953
}
const mintTestOpReturnData04 = {
tokenType: 1,
txType: 'MINT',
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
mintBatonVout: 2,
qty: '234123'
}
const mintTestOpReturnData05 = {
tokenType: 1,
txType: 'GENESIS',
ticker: 'Bubb2',
name: 'the new bubbles!',
tokenId: '938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8',
documentUri: '',
documentHash: '',
decimals: 5,
mintBatonVout: 2,
qty: '4354534534'
}
const genesisTestInputTx02 = {
txid: '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf',
hash: '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf',
version: 1,
size: 444,
locktime: 571211,
vin: [
{
txid: 'f3e056874846b6f491a1ccac3da81a3411c61d8466835446a52efa68c0d11804',
vout: 0,
scriptSig: {
asm:
'3044022022fb67bd9b35810db3fa04da4014cc93063c5ed15e8febeca09d32c03397068502206fbb86498919ca8a9be6940c904c944cd923691391a4a7d92e0c841605d46b2e[ALL|FORKID] 03fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bf',
hex:
'473044022022fb67bd9b35810db3fa04da4014cc93063c5ed15e8febeca09d32c03397068502206fbb86498919ca8a9be6940c904c944cd923691391a4a7d92e0c841605d46b2e412103fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bf'
},
sequence: 4294967294,
address: 'bitcoincash:qz6rcxjw34yuy5fngu7fngnyy55t0v802cmsmjvsp2',
value: 0.00001111
},
{
txid: '87bf0efc1630f5179b73496f9f885b141cb4c8366432a91ba678298432b197ea',
vout: 0,
scriptSig: {
asm:
'3045022100d5fbab461c19c7b56245287eb15510c86872ceb405ede7322f5266efa6cc32ea022074b557bcef9a129fb31cb2e7c2ada9fe51d5cf924ae55d1d2a29368ca1f15ef2[ALL|FORKID] 03fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bf',
hex:
'483045022100d5fbab461c19c7b56245287eb15510c86872ceb405ede7322f5266efa6cc32ea022074b557bcef9a129fb31cb2e7c2ada9fe51d5cf924ae55d1d2a29368ca1f15ef2412103fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bf'
},
sequence: 4294967294,
address: 'bitcoincash:qz6rcxjw34yuy5fngu7fngnyy55t0v802cmsmjvsp2',
value: 0.007
}
],
vout: [
{
value: 0,
n: 0,
scriptPubKey: {
asm:
'OP_RETURN 5262419 1 47454e45534953 5350494345 5370696365 7370696365736c7040676d61696c2e636f6d 0 8 0 016345785d8a0000',
hex:
'6a04534c500001010747454e45534953055350494345055370696365127370696365736c7040676d61696c2e636f6d4c0001084c0008016345785d8a0000',
type: 'nulldata'
}
},
{
value: 0.00000546,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 70e5c384ae563b4fd6cfc5f7f8df43ea427d9e10 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91470e5c384ae563b4fd6cfc5f7f8df43ea427d9e1088ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qpcwtsuy4etrkn7kelzl07xlg04yylv7zqru2cqwvm']
}
},
{
value: 0.0070012,
n: 2,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 df305a87d98cd64e1bee825d2167a16445ecdfdd OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914df305a87d98cd64e1bee825d2167a16445ecdfdd88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qr0nqk58mxxdvnsma6p96gt859jytmxlm5t0v7de4q']
}
}
],
hex:
'01000000020418d1c068fa2ea546548366841dc611341aa83daccca191f4b646488756e0f3000000006a473044022022fb67bd9b35810db3fa04da4014cc93063c5ed15e8febeca09d32c03397068502206fbb86498919ca8a9be6940c904c944cd923691391a4a7d92e0c841605d46b2e412103fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bffeffffffea97b132842978a61ba9326436c8b41c145b889f6f49739b17f53016fc0ebf87000000006b483045022100d5fbab461c19c7b56245287eb15510c86872ceb405ede7322f5266efa6cc32ea022074b557bcef9a129fb31cb2e7c2ada9fe51d5cf924ae55d1d2a29368ca1f15ef2412103fbe2c56094dc336f6244eac2e45d3d25a8e97059f2cc7de31cbf179e73dcc9bffeffffff0300000000000000003e6a04534c500001010747454e45534953055350494345055370696365127370696365736c7040676d61696c2e636f6d4c0001084c0008016345785d8a000022020000000000001976a91470e5c384ae563b4fd6cfc5f7f8df43ea427d9e1088acd8ae0a00000000001976a914df305a87d98cd64e1bee825d2167a16445ecdfdd88ac4bb70800',
blockhash: '000000000000000002278c188c52b536d4ed84a1970d8ff59043e082cef7748a',
confirmations: 141226,
time: 1551042875,
blocktime: 1551042875
}
const genesisTestOpReturn03 = {
tokenType: 1,
txType: 'GENESIS',
ticker: 'SPICE',
name: 'Spice',
tokenId: '4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf',
documentUri: 'spiceslp@gmail.com',
documentHash: '',
decimals: 8,
mintBatonVout: 0,
qty: '100000000000000000'
}
module.exports = {
nonSlpTxDetails,
slpTxDetails,
mockOpReturnData01,
mockOpReturnData02,
mockOpReturnData03,
genesisTestInputTx,
mintTestInputTx,
genesisTestOpReturnData01,
genesisTestOpReturnData02,
mintTestOpReturnData01,
mintTestOpReturnData02,
mintTestOpReturnData03,
sendTestInputTx01,
sendTestOpReturnData01,
sendTestOpReturnData02,
sendTestOpReturnData03,
sendTestOpReturnData04,
mintTestInputTx02,
mintTestOpReturnData04,
mintTestOpReturnData05,
genesisTestInputTx02,
genesisTestOpReturn03
}
+289
View File
@@ -0,0 +1,289 @@
const mockUtxoData = {
success: true,
utxos: [
{
utxos: [
{
height: 569108,
tx_hash:
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
tx_pos: 1,
value: 546
},
{
height: 674331,
tx_hash:
'5e86cd911110e4f5db0cc3e8f459d5e8850b49adf57059a71daee674b2867b31',
tx_pos: 0,
value: 1000
}
],
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
}
]
}
const mockHydratedUtxos = {
slpUtxos: [
{
utxos: [
{
height: 569108,
tx_hash:
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
tx_pos: 1,
value: 546,
txid:
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
vout: 1,
utxoType: 'token',
transactionType: 'send',
tokenId:
'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
tokenTicker: 'sleven',
tokenName: 'sleven',
tokenDocumentUrl: 'sleven',
tokenDocumentHash: '',
decimals: 7,
tokenType: 1,
tokenQty: '1',
isValid: true
},
{
height: 674331,
tx_hash:
'5e86cd911110e4f5db0cc3e8f459d5e8850b49adf57059a71daee674b2867b31',
tx_pos: 0,
value: 1000,
txid:
'5e86cd911110e4f5db0cc3e8f459d5e8850b49adf57059a71daee674b2867b31',
vout: 0,
isValid: false
}
],
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
}
]
}
const mockTwoHydratedAddrs = {
slpUtxos: [
{
utxos: [
{
height: 569108,
tx_hash:
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
tx_pos: 1,
value: 546,
txid:
'384e1b8197e8de7d38f98317af2cf5f6bcb50007c46943b3498a6fab6e8aeb7c',
vout: 1,
utxoType: 'token',
transactionType: 'send',
tokenId:
'a436c8e1b6bee3d701c6044d190f76f774be83c36de8d34a988af4489e86dd37',
tokenTicker: 'sleven',
tokenName: 'sleven',
tokenDocumentUrl: 'sleven',
tokenDocumentHash: '',
decimals: 7,
tokenType: 1,
tokenQty: '1',
isValid: true
},
{
height: 674331,
tx_hash:
'5e86cd911110e4f5db0cc3e8f459d5e8850b49adf57059a71daee674b2867b31',
tx_pos: 0,
value: 1000,
txid:
'5e86cd911110e4f5db0cc3e8f459d5e8850b49adf57059a71daee674b2867b31',
vout: 0,
isValid: false
}
],
address: 'bitcoincash:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvg8nfhq4m'
},
{
utxos: [
{
height: 602405,
tx_hash:
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
tx_pos: 0,
value: 1000,
txid:
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7',
vout: 0,
isValid: false
}
],
address: 'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
}
]
}
const mockEveryUtxoType = {
slpUtxos: [
{
utxos: [
{
height: 674512,
tx_hash:
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
tx_pos: 1,
value: 546,
txid:
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
vout: 1,
utxoType: 'token',
tokenQty: '100',
tokenId:
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
tokenTicker: 'SLPTEST',
tokenName: 'SLP Test Token',
tokenDocumentUrl: 'https://FullStack.cash',
tokenDocumentHash: '',
decimals: 8,
tokenType: 1,
isValid: true
},
{
height: 674512,
tx_hash:
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
tx_pos: 2,
value: 546,
txid:
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
vout: 2,
utxoType: 'minting-baton',
tokenId:
'acbb0d3ceef55aa3e5fafc19335ae4bf2f8edba3c0567547dfd402391db32230',
tokenTicker: 'SLPTEST',
tokenName: 'SLP Test Token',
tokenDocumentUrl: 'https://FullStack.cash',
tokenDocumentHash: '',
decimals: 8,
tokenType: 1,
isValid: true
},
{
height: 674512,
tx_hash:
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b',
tx_pos: 1,
value: 546,
txid:
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b',
vout: 1,
utxoType: 'token',
tokenQty: '1',
tokenId:
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b',
tokenTicker: 'NFT004',
tokenName: 'NFT Child',
tokenDocumentUrl: 'https://FullStack.cash',
tokenDocumentHash: '',
decimals: 0,
tokenType: 65,
isValid: true
},
{
height: 674513,
tx_hash:
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
tx_pos: 1,
value: 546,
txid:
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
vout: 1,
utxoType: 'token',
tokenQty: '10',
transactionType: 'mint',
tokenId:
'a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395',
tokenType: 129,
tokenTicker: 'NFTGT',
tokenName: 'NFT Test Group Token',
tokenDocumentUrl: 'https://FullStack.cash',
tokenDocumentHash: '',
decimals: 0,
mintBatonVout: 2,
isValid: true
},
{
height: 674513,
tx_hash:
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
tx_pos: 2,
value: 546,
txid:
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
vout: 2,
utxoType: 'minting-baton',
transactionType: 'mint',
tokenId:
'a9a2458a0f9f0761d5b8725c256f2e7fa35b9de4dec6f47b46e9f20d92d0e395',
tokenType: 129,
tokenTicker: 'NFTGT',
tokenName: 'NFT Test Group Token',
tokenDocumentUrl: 'https://FullStack.cash',
tokenDocumentHash: '',
decimals: 0,
mintBatonVout: 2,
isValid: true
},
{
height: 674513,
tx_hash:
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
tx_pos: 3,
value: 38134,
txid:
'705bcc442e5a2770e560b528f52a47b1dcc9ce9ab6a8de9dfdefa55177f00d04',
vout: 3,
isValid: false
}
],
address: 'bitcoincash:qrm0c67wwqh0w7wjxua2gdt2xggnm90xws00a3lezv'
}
]
}
const electrumxUtxos = {
success: true,
utxos: [
{
height: 0,
tx_hash:
'192f5037bb3822afd92d6b6ab51842a5dcfbe6bff783290057342da1f27ed414',
tx_pos: 0,
value: 600
},
{
height: 0,
tx_hash:
'f913646f7c180f8de020cb1387951272e8a3f764a0f88e68f7fc1145a0bf02e9',
tx_pos: 0,
value: 700
},
{
height: 0,
tx_hash:
'7a091716f8137e94f87e7760648cd34a17e32754ef95f7c7bda38a635c9b2b1b',
tx_pos: 0,
value: 800
}
]
}
module.exports = {
mockUtxoData,
mockHydratedUtxos,
mockTwoHydratedAddrs,
mockEveryUtxoType,
electrumxUtxos
}
+22
View File
@@ -67,4 +67,26 @@ describe('#price', () => {
assert.isNumber(result)
})
})
describe('#getBchUsd', () => {
it('should get the USD price of BCH', async () => {
sandbox.stub(bchjs.Price.axios, 'get').resolves({ data: { usd: 510.39 } })
const result = await bchjs.Price.getBchUsd()
// console.log(result)
assert.isNumber(result)
})
it('should handle error', async () => {
try {
sandbox.stub(bchjs.Price.axios, 'get').throws(new Error('test error'))
await bchjs.Price.getBchUsd()
// console.log(result)
assert.equal(false, true, 'unexpected error')
} catch (err) {
assert.include(err.message, 'test error')
}
})
})
})
+15 -7
View File
@@ -1,6 +1,5 @@
/*
TODO:
-Create a mocking library of data to compare unit and integration tests.
Unit tests for raw-transactions.js library.
*/
// Public npm libraries
@@ -172,10 +171,15 @@ describe('#RawTransactions', () => {
describe('#getTxData', () => {
it('should return tx data with input addresses', async () => {
// Mock dependencies
sandbox.stub(bchjs.RawTransactions, 'getRawTransaction').resolves(mockData.mockTx)
sandbox.stub(bchjs.RawTransactions, '_getInputAddrs').resolves(mockData.mockGetInputAddrsOutput)
sandbox
.stub(bchjs.RawTransactions, 'getRawTransaction')
.resolves(mockData.mockTx)
sandbox
.stub(bchjs.RawTransactions, '_getInputAddrs')
.resolves(mockData.mockGetInputAddrsOutput)
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const txid =
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const result = await bchjs.RawTransactions.getTxData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -191,7 +195,10 @@ describe('#RawTransactions', () => {
} catch (err) {
// console.log(err)
assert2.include(err.message, 'Input must be a string or array of strings')
assert2.include(
err.message,
'Input to raw-transaction.js/getTxData() must be a string containg a TXID.'
)
}
})
@@ -202,7 +209,8 @@ describe('#RawTransactions', () => {
.stub(bchjs.RawTransactions, 'getRawTransaction')
.rejects(new Error('test error'))
const txid = '05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
const txid =
'05f7d4a4e25f53d63a360434eb54f221abf159112b7fffc91da1072a079cded3'
await bchjs.RawTransactions.getTxData(txid)
+93 -2
View File
@@ -4,7 +4,7 @@
const assert = require('chai').assert
const sinon = require('sinon')
// const axios = require("axios")
const axios = require('axios')
// Default to unit tests unless some other value for TEST is passed.
if (!process.env.TEST) process.env.TEST = 'unit'
@@ -14,7 +14,7 @@ const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
// Mock data used for unit tests
// const mockData = require("./fixtures/slp/mock-utils")
const mockData = require('./fixtures/slp/mock-utils')
// Default to unit tests unless some other value for TEST is passed.
if (!process.env.TEST) process.env.TEST = 'unit'
@@ -253,4 +253,95 @@ describe('#SLP NFT1', () => {
assert.isNumber(result.outputs)
})
})
describe('#listNFTGroupChildren', () => {
it('should throw an error for improper input', async () => {
try {
const groupId = 12345
await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(
err.message,
'groupId must be a string'
)
}
})
it('should handle API response error', async () => {
try {
const error = new Error()
error.response = {
error: 'NFT group does not exists'
}
sandbox.stub(axios, 'get').throws(error)
const groupId = 'non-exists'
await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
} catch (err) {
assert.include(
err.response,
{ error: 'NFT group does not exists' }
)
}
})
it('should return array of children GENESIS transactions IDs', async () => {
sandbox.stub(axios, 'get').resolves({ data: mockData.mockNftChildrenList })
const groupId = '68cd33ecd909068fbea318ae5ff1d6207cf754e53b191327d6d73b6916424c0a'
const result = await bchjs.SLP.NFT1.listNFTGroupChildren(groupId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftChildren')
assert.isArray(result.nftChildren)
})
})
describe('#parentNFTGroup', () => {
it('should throw an error for improper input', async () => {
try {
const tokenId = 12345
await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: `, err)
assert.include(
err.message,
'tokenId must be a string'
)
}
})
it('should handle API response error', async () => {
try {
const error = new Error()
error.response = {
error: 'NFT child does not exists'
}
sandbox.stub(axios, 'get').throws(error)
const tokenId = 'non-exists'
await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
} catch (err) {
assert.include(
err.response,
{ error: 'NFT child does not exists' }
)
}
})
it('should return parent NFT group information', async () => {
sandbox.stub(axios, 'get').resolves({ data: mockData.mockNftGroup })
const tokenId = '45a30085691d6ea586e3ec2aa9122e9b0e0d6c3c1fd357decccc15d8efde48a9'
const result = await bchjs.SLP.NFT1.parentNFTGroup(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'nftGroup')
assert.property(result.nftGroup, 'id')
assert.property(result.nftGroup, 'name')
assert.property(result.nftGroup, 'symbol')
assert.property(result.nftGroup, 'documentUri')
assert.property(result.nftGroup, 'versionType')
assert.property(result.nftGroup, 'initialTokenQty')
})
})
})
+430 -63
View File
@@ -10,7 +10,7 @@ let uut
const REST_URL = process.env.RESTURL
? process.env.RESTURL
: 'https://bchn.fullstack.cash/v4/'
: 'https://bchn.fullstack.cash/v5/'
// Mock data used for unit tests
const mockDataLib = require('./fixtures/slp/mock-utils')
@@ -273,8 +273,8 @@ describe('#SLP Utils', () => {
try {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.nonSLPTxDetailsWithoutOpReturn })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.nonSLPTxDetailsWithoutOpReturn] })
const txid =
'3793d4906654f648e659f384c0f40b19c8f10c1e9fb72232a9b8edd61abaa1ec'
@@ -292,8 +292,8 @@ describe('#SLP Utils', () => {
try {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.nonSLPTxDetailsWithOpReturn })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.nonSLPTxDetailsWithOpReturn] })
const txid =
'2ff74c48a5d657cf45f699601990bffbbe7a2a516d5480674cbf6c6a4497908f'
@@ -310,8 +310,8 @@ describe('#SLP Utils', () => {
it('should decode a genesis transaction', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.txDetailsSLPGenesis })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.txDetailsSLPGenesis] })
const txid =
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90'
@@ -336,8 +336,8 @@ describe('#SLP Utils', () => {
it('should decode a mint transaction', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.txDetailsSLPMint })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.txDetailsSLPMint] })
const txid =
'65f21bbfcd545e5eb515e38e861a9dfe2378aaa2c4e458eb9e59e4d40e38f3a4'
@@ -357,8 +357,8 @@ describe('#SLP Utils', () => {
it('should decode a send transaction', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.txDetailsSLPSend })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.txDetailsSLPSend] })
const txid =
'4f922565af664b6fdf0a1ba3924487344be721b3d8815c62cafc8a51e04a8afa'
@@ -372,8 +372,8 @@ describe('#SLP Utils', () => {
it('should properly decode a Genesis transaction with no minting baton', async () => {
// Mock the call to the REST API.
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.txDetailsSLPGenesisNoBaton })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.txDetailsSLPGenesisNoBaton] })
const txid =
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
@@ -387,8 +387,8 @@ describe('#SLP Utils', () => {
it('should decode a send transaction with alternate encoding', async () => {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.txDetailsSLPSendAlt })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.txDetailsSLPSendAlt] })
const txid =
'd94357179775425ebc59c93173bd6dc9854095f090a2eb9dcfe9797398bc8eae'
@@ -411,8 +411,8 @@ describe('#SLP Utils', () => {
try {
// Mock the call to the REST API
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.mockInvalidSlpSend })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.mockInvalidSlpSend] })
const txid =
'a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415'
@@ -428,8 +428,8 @@ describe('#SLP Utils', () => {
it('should decode a NFT Parent transaction', async () => {
// Mock the call to the REST API.
sandbox
.stub(uut.Utils.axios, 'get')
.resolves({ data: mockData.txDetailsSLPNftGenesis })
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.txDetailsSLPNftGenesis] })
const txid =
'4ef6eb92950a13a69e97c2c02c7967d806aa874c0e2a6b5546a8880f2cd14bc4'
@@ -453,33 +453,383 @@ describe('#SLP Utils', () => {
assert.equal(data.qty, 1)
})
// it("should decode a NFT Child transaction", async () => {
// // Mock the call to the REST API.
// // sandbox
// // .stub(uut.Utils.axios, "get")
// // .resolves({ data: mockData.txDetailsSLPNftGenesis })
//
// const txid =
// "3de3766b10506c9156533f1639979e49d1884521543c13e4af73647df1ed3f76"
//
// const data = await uut.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
//
// // assert.property(data, "tokenType")
// // assert.property(data, "txType")
// // assert.property(data, "ticker")
// // assert.property(data, "name")
// // assert.property(data, "tokenId")
// // assert.property(data, "documentUri")
// // assert.property(data, "documentHash")
// // assert.property(data, "decimals")
// // assert.property(data, "mintBatonVout")
// // assert.property(data, "qty")
// //
// // assert.equal(data.tokenType, 129)
// // assert.equal(data.mintBatonVout, 2)
// // assert.equal(data.qty, 1)
// })
it('should decode a NFT Child transaction', async () => {
// Mock the call to the REST API.
sandbox
.stub(uut.Utils.axios, 'post')
.resolves({ data: [mockData.txDetailsSLPNftChild] })
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await uut.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
// 429 means the user is exceeding the rate limits.
it('should throw error when axios returns 429 status', async () => {
try {
// Mock the call to the REST API
sandbox.stub(uut.Utils.axios, 'post').rejects(mockData.mock429Error)
const txid =
'a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415'
await uut.Utils.decodeOpReturn(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.fail('Unexpect result')
} catch (err) {
// console.log('err: ', err)
assert.property(err, 'message')
assert.property(err, 'response')
assert.equal(err.response.status, 429)
assert.equal(err.response.statusText, 'Too Many Requests')
assert.include(err.response.data.error, 'Too many requests')
}
})
// 503 means the service is down.
it('should throw error when axios returns 503 status', async () => {
try {
// Mock the call to the REST API
sandbox.stub(uut.Utils.axios, 'post').rejects(mockData.mock503Error)
const txid =
'a60a522cc11ad7011b74e57fbabbd99296e4b9346bcb175dcf84efb737030415'
await uut.Utils.decodeOpReturn(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.fail('Unexpect result')
} catch (err) {
// console.log('err: ', err)
assert.property(err, 'message')
assert.property(err, 'response')
assert.equal(err.response.status, 503)
assert.equal(err.response.statusText, 'Service Unavailable')
assert.property(err.response, 'data')
}
})
})
describe('#_hydrateUtxo', () => {
// // 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 () => {
// Mock the call to REST API
// sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
// Stub the calls to decodeOpReturn.
sandbox.stub(uut.Utils, 'decodeOpReturn').resolves({
tokenType: 1,
txType: 'GENESIS',
ticker: 'SLPSDK',
name: 'SLP SDK example using BITBOX',
tokenId:
'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90',
documentUri: 'developer.bitcoin.com',
documentHash: '',
decimals: 8,
mintBatonVout: 2,
qty: '50700000000'
})
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 uut.Utils._hydrateUtxo(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// assert.equal(data[0], false, "Change UTXO marked as false.")
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], 'isValid')
assert.equal(data[0].isValid, 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')
})
// 429 means the user is exceeding the rate limits.
it('should return isValid=null for 429 rate limit error', async () => {
// Force decodeOpReturn() to throw a 429 error.
sandbox.stub(uut.Utils, 'decodeOpReturn').rejects(mockData.mock429Error)
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 result = await uut.Utils._hydrateUtxo(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result[0].isValid, null)
assert.equal(result[1].isValid, null)
})
// 503 means the server is down or is not responding.
it('should return isValid=null for 503 rate limit error', async () => {
// Force decodeOpReturn() to throw a 429 error.
sandbox.stub(uut.Utils, 'decodeOpReturn').rejects(mockData.mock503Error)
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 result = await uut.Utils._hydrateUtxo(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result[0].isValid, null)
assert.equal(result[1].isValid, null)
})
it('should return details for a simple SEND SLP token utxo', async () => {
// Mock the call to REST API
// Stub the calls to decodeOpReturn.
sandbox
.stub(uut.Utils, 'decodeOpReturn')
.onCall(0)
.resolves({
tokenType: 1,
txType: 'SEND',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['200000000', '99887500000000']
})
.onCall(1)
.resolves({
tokenType: 1,
txType: 'GENESIS',
ticker: 'TOK-CH',
name: 'TokyoCash',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
documentUri: '',
documentHash: '',
decimals: 8,
mintBatonVout: 0,
qty: '2100000000000000'
})
// sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
const utxos = [
{
txid:
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
const data = await uut.Utils._hydrateUtxo(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, null)
})
// I don't know if this is the ideal behavior, but this is the behavior
// that is in production, so I wanted to capture it in a test case. This
// behavior can always be changed in the future.
it('should throw error if 429 when querying Genesis transaction', async () => {
try {
// Mock the call to REST API
// Stub the calls to decodeOpReturn.
sandbox
.stub(uut.Utils, 'decodeOpReturn')
.onCall(0)
.resolves({
tokenType: 1,
txType: 'SEND',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['200000000', '99887500000000']
})
.onCall(1)
.rejects(mockData.mock429Error)
const utxos = [
{
txid:
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
await uut.Utils._hydrateUtxo(utxos)
assert.fail('Unexpected result')
} catch (err) {
// console.log('error: ', err)
assert.property(err, 'message')
assert.property(err, 'response')
assert.equal(err.response.status, 429)
assert.equal(err.response.statusText, 'Too Many Requests')
assert.include(err.response.data.error, 'Too many requests')
}
})
it('should add delay if delay is specified', async () => {
// Mock the call to REST API
// Stub the calls to decodeOpReturn.
sandbox
.stub(uut.Utils, 'decodeOpReturn')
.onCall(0)
.resolves({
tokenType: 1,
txType: 'SEND',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
amounts: ['200000000', '99887500000000']
})
.onCall(1)
.resolves({
tokenType: 1,
txType: 'GENESIS',
ticker: 'TOK-CH',
name: 'TokyoCash',
tokenId:
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7',
documentUri: '',
documentHash: '',
decimals: 8,
mintBatonVout: 0,
qty: '2100000000000000'
})
// sandbox.stub(uut.Utils, 'waterfallValidateTxid').resolves(true)
const utxos = [
{
txid:
'fde117b1f176b231e2fa9a6cb022e0f7c31c288221df6bcb05f8b7d040ca87cb',
vout: 1,
amount: 0.00000546,
satoshis: 546,
height: 596089,
confirmations: 748
}
]
const usrObj = {
utxoDelay: 100
}
await uut.Utils._hydrateUtxo(utxos, usrObj)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// TODO: This test should realy assert that the test took at least 100mS
// to complete. However, as-is, it exercises the code path, so not
// throwing an error can be considered a pass.
assert.equal(true, true)
})
})
describe('#tokenUtxoDetails', () => {
@@ -1716,6 +2066,21 @@ describe('#SLP Utils', () => {
})
})
describe('#hydrateUtxosWL', () => {
it('should throw an error if input is not an array', async () => {
try {
const utxos = 1234
await uut.Utils.hydrateUtxosWL(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.')
}
})
})
describe('#validateTxid2', () => {
it('should throw an error if the input is an array', async () => {
try {
@@ -1916,23 +2281,25 @@ describe('#SLP Utils', () => {
assert.equal(result, true)
})
it('catches and throws an error', async () => {
try {
const txid = 'fakeTxid'
// Force an error
sandbox
.stub(uut.Utils, 'validateTxid3')
.rejects(new Error('fake error'))
await await uut.Utils.waterfallValidateTxid(txid)
assert.fail('Unexpected result')
} catch (err) {
// console.log(`err.message: ${err.message}`)
assert.include(err.message, 'fake error')
}
})
// CT 08-09-21 Changed behavior. validateTxid3() is now wrapped in a
// try/catch statement that exits quietly if there is an error.
// it('catches and throws an error', async () => {
// try {
// const txid = 'fakeTxid'
//
// // Force an error
// sandbox
// .stub(uut.Utils, 'validateTxid3')
// .rejects(new Error('fake error'))
//
// await await uut.Utils.waterfallValidateTxid(txid)
//
// assert.fail('Unexpected result')
// } catch (err) {
// // console.log(`err.message: ${err.message}`)
// assert.include(err.message, 'fake error')
// }
// })
})
describe('#getWhitelist', () => {
+708
View File
@@ -0,0 +1,708 @@
/*
Unit tests for the transaction.js library.
*/
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
const cloneDeep = require('lodash.clonedeep')
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
const mockDataLib = require('./fixtures/transaction-mock.js')
describe('#TransactionLib', () => {
let sandbox, mockData
beforeEach(() => {
sandbox = sinon.createSandbox()
mockData = cloneDeep(mockDataLib)
})
afterEach(() => sandbox.restore())
describe('#get', () => {
it('should throw an error if txid is not specified', async () => {
try {
await bchjs.Transaction.get()
assert.fail('Unexpected code path!')
} catch (err) {
assert.include(
err.message,
'Input to Transaction.get() must be a string containing a TXID.'
)
}
})
it('should get details about a non-SLP transaction', async () => {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.nonSlpTxDetails)
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[0].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, false)
})
it('should get details about a SLP transaction', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.slpTxDetails)
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.mockOpReturnData01)
.onCall(1)
.resolves(mockData.mockOpReturnData02)
.onCall(2)
.resolves(mockData.mockOpReturnData03)
.onCall(3)
.rejects(new Error('No OP_RETURN'))
sandbox
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
.resolves(true)
const txid =
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vout[0], 'tokenQty')
assert.equal(result.vout[0].tokenQty, null)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result.vin[0], 'tokenQty')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, true)
})
it('should catch and throw error on network error', async () => {
try {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
// Force an error
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.rejects(new Error('test error'))
await bchjs.Transaction.get(txid)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
// This test case was created in response to a bug. When the input TX
// was a Genesis SLP transaction, the inputs of the transaction were not
// being hydrated properly.
it('should get input details when input is a genesis tx', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.genesisTestInputTx)
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.genesisTestOpReturnData01)
.onCall(1)
.resolves(mockData.genesisTestOpReturnData02)
.onCall(2)
.resolves(mockData.genesisTestOpReturnData02)
.onCall(3)
.resolves(mockData.genesisTestOpReturnData02)
sandbox
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
.resolves(true)
const txid =
'874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vout[0], 'tokenQty')
assert.equal(result.vout[0].tokenQty, null)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result.vin[0], 'tokenQty')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, true)
// Assert inputs values unique to a Genesis input have the proper values.
assert.equal(result.vin[0].tokenQty, 10000000)
assert.equal(result.vin[1].tokenQty, null)
})
it('should get input details when input is a mint tx', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.mintTestInputTx)
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.mintTestOpReturnData01)
.onCall(1)
.resolves(mockData.mintTestOpReturnData02)
.onCall(2)
.resolves(mockData.mintTestOpReturnData02)
.onCall(3)
.resolves(mockData.mintTestOpReturnData03)
.onCall(4)
.resolves(mockData.mintTestOpReturnData03)
sandbox
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
.resolves(true)
const txid =
'4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vout[0], 'tokenQty')
assert.equal(result.vout[0].tokenQty, null)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result.vin[0], 'tokenQty')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, true)
// Assert inputs values unique to a Mint input have the proper values.
assert.equal(result.vin[0].tokenQty, 43545.34534)
assert.equal(result.vin[1].tokenQty, 2.34123)
assert.equal(result.vin[2].tokenQty, null)
})
// This test case was generated from the problematic transaction that
// used inputs in a 'non-standard' way.
it('should correctly assign quantities to mixed mint inputs', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.sendTestInputTx01)
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.sendTestOpReturnData01)
.onCall(1)
.resolves(mockData.sendTestOpReturnData02)
.onCall(2)
.resolves(mockData.sendTestOpReturnData03)
.onCall(3)
.resolves(mockData.sendTestOpReturnData03)
.onCall(4)
.resolves(mockData.sendTestOpReturnData04)
sandbox
.stub(bchjs.Transaction.slpUtils, 'waterfallValidateTxid')
.resolves(true)
const txid =
'6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a'
const result = await bchjs.Transaction.get(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vout[0], 'tokenQty')
assert.equal(result.vout[0].tokenQty, null)
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
assert.property(result.vin[0], 'tokenQty')
assert.property(result, 'isValidSLPTx')
assert.equal(result.isValidSLPTx, true)
// Assert inputs values unique to a Mint input have the proper values.
assert.equal(result.vin[0].tokenQty, 100000000)
assert.equal(result.vin[1].tokenQty, null)
assert.equal(result.vin[2].tokenQty, 99000000)
})
})
describe('#get3', () => {
it('should throw an error if txid is not specified', async () => {
try {
await bchjs.Transaction.get3()
assert.fail('Unexpected code path!')
} catch (err) {
assert.include(
err.message,
'Input to Transaction.get() must be a string containing a TXID.'
)
}
})
it('should get details about a non-SLP transaction', async () => {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.nonSlpTxDetails)
sandbox
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
.resolves({ height: 602405 })
sandbox.stub(bchjs.Transaction, 'getTokenInfo').resolves(false)
const result = await bchjs.Transaction.get3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[0].scriptPubKey, 'addresses')
// Assert that added properties exist.
assert.property(result.vin[0], 'address')
assert.property(result.vin[0], 'value')
// Assert blockheight is added
assert.equal(result.blockheight, 602405)
assert.equal(result.isSlpTx, false)
})
it('should get details about a SLP SEND tx with SEND input', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.slpTxDetails)
sandbox
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
.resolves({ height: 603424 })
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.mockOpReturnData01)
.onCall(1)
.resolves(mockData.mockOpReturnData02)
.onCall(2)
.resolves(mockData.mockOpReturnData03)
.onCall(3)
.rejects(new Error('No OP_RETURN'))
const txid =
'266844d53e46bbd7dd37134688dffea6e54d944edff27a0add63dd0908839bc1'
const result = await bchjs.Transaction.get3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert outputs have expected properties
assert.equal(result.vout[0].tokenQty, null)
assert.equal(result.vout[0].tokenQtyStr, null)
assert.equal(result.vout[1].tokenQty, 1)
assert.equal(result.vout[1].tokenQtyStr, '1')
assert.equal(result.vout[2].tokenQty, 998833)
assert.equal(result.vout[2].tokenQtyStr, '998833')
assert.equal(result.vout[3].tokenQty, null)
assert.equal(result.vout[3].tokenQtyStr, null)
// Assert that inputs have expected properties
assert.equal(result.vin[0].tokenQtyStr, '998834')
assert.equal(result.vin[0].tokenQty, 998834)
assert.equal(
result.vin[0].tokenId,
'497291b8a1dfe69c8daea50677a3d31a5ef0e9484d8bebb610dac64bbc202fb7'
)
assert.equal(result.vin[1].tokenQtyStr, '0')
assert.equal(result.vin[1].tokenQty, 0)
assert.equal(result.vin[1].tokenId, null)
// Assert blockheight is added
assert.equal(result.blockheight, 603424)
assert.equal(result.isSlpTx, true)
})
it('should catch and throw error on network error', async () => {
try {
const txid =
'2b37bdb3b63dd0bca720437754a36671431a950e684b64c44ea910ea9d5297c7'
// Force an error
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.rejects(new Error('test error'))
await bchjs.Transaction.get3(txid)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
// This test case was created in response to a bug. When the input TX
// was a Genesis SLP transaction, the inputs of the transaction were not
// being hydrated properly.
it('should get details about a SLP SEND tx with GENSIS input', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.genesisTestInputTx)
sandbox
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
.resolves({ height: 543409 })
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.genesisTestOpReturnData01)
.onCall(1)
.resolves(mockData.genesisTestOpReturnData02)
.onCall(2)
.resolves(mockData.genesisTestOpReturnData02)
.onCall(3)
.resolves(mockData.genesisTestOpReturnData02)
const txid =
'874306bda204d3a5dd15e03ea5732cccdca4c33a52df35162cdd64e30ea7f04e'
const result = await bchjs.Transaction.get3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert outputs have expected properties and values
assert.equal(result.vout[0].tokenQty, null)
assert.equal(result.vout[0].tokenQtyStr, null)
assert.equal(result.vout[1].tokenQty, 5000000)
assert.equal(result.vout[1].tokenQtyStr, '5000000')
assert.equal(result.vout[2].tokenQty, 5000000)
assert.equal(result.vout[2].tokenQtyStr, '5000000')
assert.equal(result.vout[3].tokenQty, null)
assert.equal(result.vout[3].tokenQtyStr, null)
// Assert inputs have expected properties and values
assert.equal(result.vin[0].tokenQty, 10000000)
assert.equal(result.vin[0].tokenQtyStr, '10000000')
assert.equal(
result.vin[0].tokenId,
'323a1e35ae0b356316093d20f2d9fbc995d19314b5c0148b78dc8d9c0dab9d35'
)
assert.equal(result.vin[1].tokenQty, 0)
assert.equal(result.vin[1].tokenQtyStr, '0')
assert.equal(result.vin[1].tokenId, null)
// Assert blockheight is added
assert.equal(result.blockheight, 543409)
assert.equal(result.isSlpTx, true)
})
it('should get details about a SLP SEND tx with MINT (and GENESIS) input', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.mintTestInputTx)
sandbox
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
.resolves({ height: 543614 })
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.mintTestOpReturnData01)
.onCall(1)
.resolves(mockData.mintTestOpReturnData02)
.onCall(2)
.resolves(mockData.mintTestOpReturnData02)
.onCall(3)
.resolves(mockData.mintTestOpReturnData03)
.onCall(4)
.resolves(mockData.mintTestOpReturnData03)
const txid =
'4640a734063ea79fa587a3cac38a70a2f6f3db0011e23514024185982110d0fa'
const result = await bchjs.Transaction.get3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert expected output properties and values exist.
assert.equal(result.vout[0].tokenQty, null)
assert.equal(result.vout[1].tokenQty, 43547.68657)
assert.equal(result.vout[1].tokenQtyStr, '43547.68657')
assert.equal(result.vout[2].tokenQty, null)
// Assert expected input properties and values exist.
assert.equal(result.vin[0].tokenQty, 43545.34534)
assert.equal(result.vin[0].tokenQtyStr, '43545.34534')
assert.equal(
result.vin[0].tokenId,
'938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8'
)
assert.equal(result.vin[1].tokenQty, 2.34123)
assert.equal(result.vin[1].tokenQtyStr, '2.34123')
assert.equal(
result.vin[1].tokenId,
'938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8'
)
assert.equal(result.vin[2].tokenQty, 0)
assert.equal(result.vin[2].tokenQtyStr, '0')
assert.equal(result.vin[2].tokenId, null)
// Assert blockheight is added
assert.equal(result.blockheight, 543614)
assert.equal(result.isSlpTx, true)
})
// This test case was generated from the problematic transaction that
// used inputs in a 'non-standard' way.
it('should get details about a SLP SEND tx with MINT (and SEND) input', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.sendTestInputTx01)
sandbox
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
.resolves({ height: 543957 })
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.sendTestOpReturnData01)
.onCall(1)
.resolves(mockData.sendTestOpReturnData02)
.onCall(2)
.resolves(mockData.sendTestOpReturnData03)
.onCall(3)
.resolves(mockData.sendTestOpReturnData03)
.onCall(4)
.resolves(mockData.sendTestOpReturnData04)
const txid =
'6bc111fbf5b118021d68355ca19a0e77fa358dd931f284b2550f79a51ab4792a'
const result = await bchjs.Transaction.get3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert the outputs have expected properties and values.
assert.equal(result.vout[0].tokenQty, null)
assert.equal(result.vout[1].tokenQty, 1000000)
assert.equal(result.vout[1].tokenQtyStr, '1000000')
assert.equal(result.vout[2].tokenQty, 198000000)
assert.equal(result.vout[2].tokenQtyStr, '198000000')
assert.equal(result.vout[3].tokenQty, null)
// Assert the inputs have expected properties and values.
assert.equal(result.vin[0].tokenQty, 100000000)
assert.equal(result.vin[0].tokenQtyStr, '100000000')
assert.equal(
result.vin[0].tokenId,
'550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35'
)
assert.equal(result.vin[1].tokenQty, 0)
assert.equal(result.vin[1].tokenQtyStr, 0)
assert.equal(result.vin[1].tokenId, null)
assert.equal(result.vin[2].tokenQty, 99000000)
assert.equal(result.vin[2].tokenQtyStr, '99000000')
assert.equal(
result.vin[2].tokenId,
'550d19eb820e616a54b8a73372c4420b5a0567d8dc00f613b71c5234dc884b35'
)
// Assert blockheight is added
assert.equal(result.blockheight, 543957)
assert.equal(result.isSlpTx, true)
})
// This was a problematic TX
it('should process MINT TX with GENESIS input', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.mintTestInputTx02)
sandbox
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
.resolves({ height: 543614 })
sandbox
.stub(bchjs.Transaction.slpUtils, 'decodeOpReturn')
.onCall(0)
.resolves(mockData.mintTestOpReturnData04)
.onCall(1)
.resolves(mockData.mintTestOpReturnData05)
.onCall(2)
.resolves(mockData.mintTestOpReturnData05)
.onCall(3)
.resolves(mockData.mintTestOpReturnData05)
.onCall(4)
.resolves(mockData.mintTestOpReturnData05)
.onCall(5)
.resolves(mockData.mintTestOpReturnData05)
const txid =
'ee9d3cf5153599c134147e3fac9844c68e216843f4452a1ce15a29452af6db34'
const result = await bchjs.Transaction.get3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert outputs have expected properties and values
assert.equal(result.vout[0].tokenQty, 0)
assert.equal(result.vout[0].tokenQty, '0')
assert.equal(result.vout[1].tokenQty, 2.34123)
assert.equal(result.vout[1].tokenQty, '2.34123')
assert.equal(result.vout[2].tokenQty, 0)
assert.equal(result.vout[2].tokenQty, '0')
assert.equal(result.vout[2].isMintBaton, true)
assert.equal(result.vout[3].tokenQty, 0)
assert.equal(result.vout[3].tokenQty, '0')
// Assert inputs have expected properties and values
assert.equal(result.vin[0].tokenQty, 0)
assert.equal(result.vin[0].tokenQtyStr, '0')
assert.equal(result.vin[0].tokenId, null)
assert.equal(result.vin[1].tokenQty, 0)
assert.equal(result.vin[1].tokenQtyStr, '0')
assert.equal(
result.vin[1].tokenId,
'938cc18e618967d787897bbc64b9a8d201b94ec7c69b1a9949eab0433ba5cdf8'
)
assert.equal(result.vin[1].isMintBaton, true)
// Assert added TX data exists.
assert.equal(result.blockheight, 543614)
assert.equal(result.isSlpTx, true)
})
// It should process a GENESIS tx
it('should process a GENESIS tx', async () => {
// Mock dependencies
sandbox
.stub(bchjs.Transaction.rawTransaction, 'getTxData')
.resolves(mockData.genesisTestInputTx02)
sandbox
.stub(bchjs.Transaction.blockchain, 'getBlockHeader')
.resolves({ height: 571212 })
sandbox
.stub(bchjs.Transaction, 'getTokenInfo')
.onCall(0)
.resolves(mockData.genesisTestOpReturn03)
.onCall(1)
.resolves(mockData.genesisTestOpReturn03)
.onCall(2)
.resolves(false)
.onCall(3)
.resolves(false)
.onCall(4)
.resolves(false)
const txid =
'4de69e374a8ed21cbddd47f2338cc0f479dc58daa2bbe11cd604ca488eca0ddf'
const result = await bchjs.Transaction.get3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Assert that there are stanardized properties.
assert.property(result, 'txid')
assert.property(result, 'vin')
assert.property(result, 'vout')
assert.property(result.vout[0], 'value')
assert.property(result.vout[1].scriptPubKey, 'addresses')
// Assert output have expected properties and values
assert.equal(result.vout[0].tokenQty, 0)
assert.equal(result.vout[0].tokenQtyStr, '0')
assert.equal(result.vout[0].isMintBaton, true)
assert.equal(result.vout[1].tokenQty, 1000000000)
assert.equal(result.vout[1].tokenQtyStr, '1000000000')
assert.equal(result.vout[2].tokenQty, 0)
assert.equal(result.vout[2].tokenQtyStr, '0')
// Assert input have expected properties and values
assert.equal(result.vin[0].tokenQty, 0)
assert.equal(result.vin[0].tokenQtyStr, '0')
assert.equal(result.vin[0].tokenId, null)
assert.equal(result.vin[1].tokenQty, 0)
assert.equal(result.vin[1].tokenQtyStr, '0')
assert.equal(result.vin[1].tokenId, null)
// Assert added TX data exists.
assert.equal(result.blockheight, 571212)
assert.equal(result.isSlpTx, true)
})
})
})
+140
View File
@@ -1,4 +1,5 @@
const assert = require('assert')
const assert2 = require('chai').assert
const axios = require('axios')
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
@@ -32,4 +33,143 @@ describe('#Util', () => {
.then(done, done)
})
})
describe('#floor8', () => {
it('should floor a number to 8 decimals', () => {
const num = 1.234567891111
const result = bchjs.Util.floor8(num)
// console.log(result)
assert2.equal(result, 1.23456789)
})
it('should throw an error for non-number input', () => {
try {
const num = 'string'
bchjs.Util.floor8(num)
assert2.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(err)
assert2.include(err.message, 'input must be a number')
}
})
it('should not effect a number with less than 8 decimals', () => {
const num = 1.23
const result = bchjs.Util.floor8(num)
// console.log(result)
assert2.equal(result, 1.23)
})
})
describe('#floor2', () => {
it('should round a number to 2 decimals', () => {
const num = 1.234567891111
const result = bchjs.Util.floor2(num)
// console.log(result)
assert2.equal(result, 1.23)
})
it('should throw an error for non-number input', () => {
try {
const num = 'string'
bchjs.Util.floor2(num)
assert2.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(err)
assert2.include(err.message, 'input must be a number')
}
})
it('should not effect a number with less than 8 decimals', () => {
const num = 1.2
const result = bchjs.Util.floor2(num)
// console.log(result)
assert2.equal(result, 1.2)
})
})
describe('#chunk20', () => {
const thirtyFiveElements = [
0,
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34
]
it('should split 35 elements into 2 arrays', () => {
const result = bchjs.Util.chunk20(thirtyFiveElements)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.isArray(result)
assert2.isArray(result[0])
assert2.equal(result.length, 2)
assert2.equal(result[0].length, 20)
})
it('should return accurately with an array of less than 20 elements', () => {
const elems = [0, 1, 2, 3, 4, 5]
const result = bchjs.Util.chunk20(elems)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert2.isArray(result)
assert2.isArray(result[0])
assert2.equal(result.length, 1)
assert2.equal(result[0].length, 6)
})
it('should throw an error for non-array input', () => {
try {
bchjs.Util.chunk20('string')
assert2.equal(true, false, 'Unexpected result')
} catch (err) {
// console.log(err)
assert2.include(err.message, 'input must be an array')
}
})
})
})
+191
View File
@@ -0,0 +1,191 @@
/*
Unit tests for the utxo.js library.
*/
const sinon = require('sinon')
const assert = require('chai').assert
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
const mockData = require('./fixtures/utxo-mocks')
describe('#utxo', () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
afterEach(() => sandbox.restore())
describe('#get', () => {
it('should get hydrated and filtered UTXOs for an address', async () => {
// Mock dependencies.
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves(mockData.mockUtxoData)
sandbox
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
.resolves(mockData.mockHydratedUtxos)
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should catch and throw an error', async () => {
try {
// Force an error
sandbox
.stub(bchjs.Utxo.electrumx, 'utxo')
.rejects(new Error('test error'))
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
await bchjs.Utxo.get(addr)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'test error')
}
})
it('should handle an array of addresses', async () => {
// Mock dependencies.
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves({ utxos: {} })
sandbox
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
.resolves(mockData.mockTwoHydratedAddrs)
const addr = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9'
]
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result.length, 2)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
it('should throw an error for array of 21 elements', async () => {
try {
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const addrs = []
for (let i = 0; i < 21; i++) {
addrs.push(addr)
}
await bchjs.Utxo.get(addrs)
assert.fail('Unexpected code path')
} catch (err) {
assert.include(err.message, 'Too many elements, 20 max.')
}
})
it('should handle NFTs and minting batons', async () => {
// Mock dependencies.
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves({ utxos: {} })
sandbox
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxos')
.resolves(mockData.mockEveryUtxoType)
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
assert.isArray(result[0].slpUtxos.type1.mintBatons)
assert.isArray(result[0].slpUtxos.type1.tokens)
assert.isArray(result[0].slpUtxos.nft.groupMintBatons)
assert.isArray(result[0].slpUtxos.nft.groupTokens)
assert.isArray(result[0].slpUtxos.nft.tokens)
})
it('should use the whitelist when flag is set', async () => {
// Mock dependencies.
sandbox.stub(bchjs.Utxo.electrumx, 'utxo').resolves(mockData.mockUtxoData)
sandbox
.stub(bchjs.Utxo.slp.Utils, 'hydrateUtxosWL')
.resolves(mockData.mockHydratedUtxos)
const addr = 'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
const useWhitelist = true
const result = await bchjs.Utxo.get(addr, useWhitelist)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'address')
assert.property(result[0], 'bchUtxos')
assert.property(result[0], 'nullUtxos')
assert.property(result[0], 'slpUtxos')
assert.isArray(result[0].bchUtxos)
assert.isArray(result[0].nullUtxos)
})
})
describe('#findBiggestUtxo', () => {
it('should throw error for non-array input', async () => {
try {
await bchjs.Utxo.findBiggestUtxo({})
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.include(
err.message,
'utxos input to findBiggestUtxo() must be an array'
)
}
})
it('should throw an error if input does not have a value or satoshis property', async () => {
try {
const badUtxos = [
{
height: 0,
tx_hash:
'192f5037bb3822afd92d6b6ab51842a5dcfbe6bff783290057342da1f27ed414',
tx_pos: 0
}
]
await bchjs.Utxo.findBiggestUtxo(badUtxos)
} catch (err) {
assert.include(
err.message,
'UTXOs require a satoshis or value property for findBiggestUtxo()'
)
}
})
it('should sort UTXOs from Electrumx', async () => {
const result = bchjs.Utxo.findBiggestUtxo(mockData.electrumxUtxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'satoshis')
assert.equal(result.satoshis, 800)
})
})
})