Compare commits

..
151 Commits
Author SHA1 Message Date
Chris Troutner 3fb5377acd Merge pull request #274 from Permissionless-Software-Foundation/ct-unstable
fix(package.json): Fixing minor issue with package.json that was caus…
2025-11-26 08:37:15 -08:00
Chris Troutner a1ba2ac2f7 Merge branch 'master' into ct-unstable 2025-11-26 08:33:46 -08:00
Chris Troutner 7dc29230da fix(package.json): Fixing minor issue with package.json that was causing warning 2025-11-26 08:30:50 -08:00
Chris Troutner 3b013b6215 Merge pull request #273 from Permissionless-Software-Foundation/ct-unstable
Updating README
2025-11-25 17:17:28 -08:00
Chris Troutner 9129c594dd Merge branch 'v7' into ct-unstable 2025-11-25 17:12:50 -08:00
Chris Troutner 64d99531de fix(README): Updating README 2025-11-25 17:12:32 -08:00
Chris Troutner c9a8c5008e Merge pull request #272 from Permissionless-Software-Foundation/v7
V7
2025-11-25 17:02:01 -08:00
Chris Troutner 7f08789c6a Updating to major version 7 2025-11-25 16:57:48 -08:00
Chris Troutner 63dcddb3ab Fixing unit tests 2025-11-25 16:54:26 -08:00
Chris Troutner c2f150bf81 fix(deps): Updating dependencies 2025-11-25 16:40:38 -08:00
Chris Troutner 889ffcfe1d integration test 2025-11-25 16:36:10 -08:00
Chris Troutner 26a6175889 Got x402 integration tests working 2025-11-25 13:27:13 -08:00
Chris Troutner 07de91afa2 feat(BCHJSWIF): Env var lets user pass WIF private key as env var 2025-11-25 12:09:46 -08:00
Chris Troutner 246da25f91 Added x402 support 2025-11-25 10:59:30 -08:00
Chris Troutner f24d36ccee feat(auth): Added Bearer basic auth, removed other auth patterns 2025-11-25 08:46:18 -08:00
Chris Troutner f80cce96b1 Adjusting package.json scripts 2025-11-25 08:31:11 -08:00
Chris Troutner 1c6939ab43 fix(integration tests): Only supporting BCH, all tests in one directory 2025-11-23 10:30:23 -08:00
Chris Troutner bcb236a66b Fixed last integration test 2025-11-17 10:42:37 -08:00
Chris Troutner b66ff3cb59 fixing unit tests 2025-11-17 10:36:14 -08:00
Chris Troutner 2dad11180b feat(util): Removing util.validateAddress functions 2025-11-17 10:28:27 -08:00
Chris Troutner 22e13ef654 fix(dsproof): Fixed integration test 2025-11-17 10:20:55 -08:00
Chris Troutner 4dabdac792 fix(price): Refactored price and integration tests 2025-11-17 10:13:53 -08:00
Chris Troutner 857b57240e Adding new fulcrum test 2025-11-17 07:45:52 -08:00
Chris Troutner 7b9aba3738 fixed broken fulcrum test 2025-11-17 07:44:30 -08:00
Chris Troutner 2c49af2de3 feat(psf-bch-api): Using psf-bch-api instead of legacy bch-api 2025-11-17 07:37:21 -08:00
Chris Troutner 3c0590ba23 Removing XEC and Testnet integration tests 2025-11-17 07:24:47 -08:00
Chris Troutner b20a034bc9 fix(apidoc): Fixing contrast in api docs 2025-11-16 19:40:15 -08:00
Chris Troutner 12e5143d9a fix(lint): Fixing linting issue 2025-11-16 13:47:24 -08:00
Chris Troutner 8e3fa6dcf1 feat(ESM): Converted code base to ESM format, away from CommonJS 2025-11-16 13:37:04 -08:00
Chris Troutner 76442208a2 Removing unneeded dev dependency 2025-11-16 12:00:44 -08:00
Chris Troutner 2cf8905852 Removing more unnecessary dev dependencies 2025-11-16 11:53:25 -08:00
Chris Troutner d134aff9f4 Removing unneeded dev dependency 2025-11-16 11:47:29 -08:00
Chris Troutner 005b628301 Updating more dependencies 2025-11-16 11:44:45 -08:00
Chris Troutner f3c2168435 Updating more dependencies 2025-11-16 11:38:56 -08:00
Chris Troutner 71b8bb089d Updated more dependencies 2025-11-16 11:34:26 -08:00
Chris Troutner 2333229e54 Upgraded some npm dependencies 2025-11-16 10:55:44 -08:00
Chris Troutner 13c69593b2 Merge pull request #271 from Permissionless-Software-Foundation/ct-unstable
fix(llm): Adding LLM summary of library
2025-11-06 08:54:19 -08:00
Chris Troutner 8316c4eabb fix(llm): Adding LLM summary of library 2025-11-06 08:52:32 -08:00
Chris Troutner 30cf010e80 Merge pull request #270 from Permissionless-Software-Foundation/ct-unstable
fix(axios): Updating axios
2025-10-07 17:42:39 -07:00
Chris Troutner f28eeea5b0 fix(axios): Updating axios 2025-10-07 17:41:12 -07:00
Chris Troutner 9758eb9995 Merge pull request #269 from Permissionless-Software-Foundation/ct-unstable
fix(utxo.get()): Handling corner-case when SLP indexer is not returni…
2025-06-29 14:38:18 -07:00
Chris Troutner 4260a74d91 fix(utxo.get()): Handling corner-case when SLP indexer is not returning data 2025-06-29 14:36:44 -07:00
Chris Troutner 01625abb37 Merge pull request #267 from Permissionless-Software-Foundation/ct-unstable
feat(getPsffppPrice): New endpoint to get PSFFPP pin price
2024-09-03 19:29:59 -07:00
Chris Troutner b34040cd08 feat(getPsffppPrice): New endpoint to get PSFFPP pin price 2024-09-03 19:28:22 -07:00
Chris Troutner 01a4d0f1aa Adding integration-test-pearson.sh to gitignore 2023-06-02 15:03:25 -07:00
Chris Troutner 7c7c6395e1 Merge pull request #265 from Permissionless-Software-Foundation/ct-unstable
fix(nft): Removing NFT parent and child functions from SLPDB that hav…
2023-06-02 06:27:00 -07:00
Chris Troutner 3408a8fd3b fix(nft): Removing NFT parent and child functions from SLPDB that have been replaced with psf-slp-indexer 2023-06-02 06:24:57 -07:00
Chris Troutner 86a1cadfac Merge pull request #264 from Permissionless-Software-Foundation/ct-unstable
fix(Utxo.get()): Updating documentation
2023-04-09 05:57:08 -07:00
Chris Troutner 7000a83a37 fix(Utxo.get()): Updating documentation 2023-04-09 05:54:22 -07:00
Chris Troutner b331450473 Merge pull request #263 from Permissionless-Software-Foundation/ct-unstable
fix(getPubKey): Debugging errors with getPubKey()
2023-04-08 12:20:32 -07:00
Chris Troutner af7c8ffa6d fix(getPubKey): Debugging errors with getPubKey() 2023-04-08 12:17:40 -07:00
Chris Troutner 2e04548ade Merge pull request #262 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Fixing integration tests that broke with infoUtxo impleme…
2023-04-01 16:38:05 -07:00
Chris Troutner 412fa5e1ce fix(tests): Fixing integration tests that broke with infoUtxo implementation 2023-04-01 16:36:07 -07:00
Chris Troutner a08a4236d6 Merge pull request #261 from Permissionless-Software-Foundation/ct-unstable
feat(Utxo.get()): Creating infoUtxo category for utxos less than 1000…
2023-03-31 13:43:19 -07:00
Chris Troutner e700a22560 feat(Utxo.get()): Creating infoUtxo category for utxos less than 1000 sats 2023-03-31 13:41:37 -07:00
Chris Troutner 4573c63ca3 Merge pull request #260 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Commenting out integration tests that fails due to 429 er…
2023-03-14 06:48:37 -07:00
Chris Troutner bd18e8a755 fix(tests): Commenting out integration tests that fails due to 429 errors 2023-03-14 06:28:50 -07:00
Chris Troutner cf8f48ffe1 Merge pull request #259 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Commenting out integration tests that are commonly failing
2023-03-11 17:28:44 -08:00
Chris Troutner fc96b04a64 fix(tests): Commenting out integration tests that are commonly failing 2023-03-11 17:26:00 -08:00
Chris Troutner 661a759f80 Merge pull request #258 from Permissionless-Software-Foundation/ct-unstable
feat(tx history): Updating to reflect changes in bch-api
2023-02-07 10:03:22 -08:00
Chris Troutner ba915e84cf feat(tx history): Updating to reflect changes in bch-api 2023-02-07 10:01:41 -08:00
Chris Troutner eae7172e3d Merge pull request #257 from Permissionless-Software-Foundation/ct-unstable
fix(getTokenData2()): Adding updateCache input argument
2023-01-26 17:49:48 -08:00
Chris Troutner 6dd2a0590f fix(getTokenData2()): Adding updateCache input argument 2023-01-26 17:48:11 -08:00
Chris Troutner 3152e87b5d Merge pull request #256 from Permissionless-Software-Foundation/electron
fix(bitcoincashjs-lib): Updating to v4.0.3
2023-01-22 09:41:03 -08:00
Chris Troutner 4b66f3841d fix(bitcoincashjs-lib): Updating to v4.0.3 2023-01-22 09:39:03 -08:00
Chris Troutner 9f98a47682 Merge pull request #255 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Fixing broken integration tests
2023-01-13 07:21:15 -08:00
Chris Troutner 0833e8ca78 fix(tests): Fixing broken integration tests 2023-01-13 07:13:47 -08:00
Chris Troutner e01a18b65e Merge pull request #253 from Permissionless-Software-Foundation/ct-unstable
fix(encryption): Adding documentation for the encryption library
2022-12-14 10:09:43 -08:00
Chris Troutner 055cd7feca fix(encryption): Adding documentation for the encryption library 2022-12-14 10:05:41 -08:00
Chris Troutner 906d9b2094 Merge pull request #252 from Permissionless-Software-Foundation/ct-unstable
fix(utxo.get()): Detecting out-of-sync SLP indexer, protecting tokens
2022-11-01 15:01:44 -07:00
Chris Troutner 9326fd6e59 fix(utxo.get()): Detecting out-of-sync SLP indexer, protecting tokens 2022-11-01 14:58:28 -07:00
Chris Troutner 1d91eed5df Fixing script in package.json 2022-10-16 13:23:16 -07:00
Chris Troutner 62e56c832b Merge pull request #251 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Removing integration tests that trips rate limit
2022-10-13 10:33:55 -07:00
Chris Troutner d5b7e4ab1d fix(tests): Removing integration tests that trips rate limit 2022-10-13 10:31:22 -07:00
Chris Troutner 2137a75fce Merge pull request #250 from Permissionless-Software-Foundation/ct-unstable
fix(getTokenData2): Adding api documentation
2022-10-06 20:43:14 -07:00
Chris Troutner 8966cac210 fix(getTokenData2): Adding api documentation 2022-10-06 20:40:22 -07:00
Chris Troutner 9d32f828bc Cleaning up screen spam 2022-10-06 20:33:36 -07:00
Chris Troutner 91a7106818 Merge pull request #249 from Permissionless-Software-Foundation/ct-unstable
feat(getTokenData2): Adding new endpoint for token icons and other media
2022-10-06 20:27:00 -07:00
Chris Troutner 44e671dfbe fix(price): Removing dead price endpoint tests 2022-10-06 20:24:17 -07:00
Chris Troutner 979fb481de feat(getTokenData2): Adding token media endpoint 2022-10-06 18:30:39 -07:00
Chris Troutner 66253ed5a6 feat(bchjs.Price.current): Removing dead endpoint 2022-10-06 17:58:00 -07:00
Chris Troutner 6ef5ac0f7c feat(getTokenData2): Adding new endpoint for token icons and other media 2022-10-06 17:57:11 -07:00
Chris Troutner 21c322d0df Merge pull request #247 from Permissionless-Software-Foundation/ct-unstable
fix(getTokenData): Adding ability to retrieve tx history
2022-07-12 14:48:22 -07:00
Chris Troutner 1b0e9ef0ff fix(getTokenData): Adding ability to retrieve tx history 2022-07-12 14:24:09 -07:00
Chris Troutner 78776bee93 Merge pull request #246 from Permissionless-Software-Foundation/ct-unstable
fix(deps): npm audit shows now vulns
2022-07-11 07:24:47 -07:00
Chris Troutner a6e3155f37 fix(deps): npm audit shows now vulns 2022-07-11 07:20:20 -07:00
Chris Troutner 47f60bccde Merge pull request #245 from Permissionless-Software-Foundation/ct-unstable
fix(ecash): Adding token support for ecash
2022-07-11 07:11:36 -07:00
Chris Troutner d0a3b27682 fix(ecash): Adding token support for ecash 2022-07-11 07:09:44 -07:00
Chris Troutner 372d1b099d Merge pull request #244 from Permissionless-Software-Foundation/ct-unstable
fix(address): Adding toEtokenAddress() function
2022-07-10 09:50:44 -07:00
Chris Troutner dbfaf9c4ab fix(address): Adding toEtokenAddress() function 2022-07-10 09:47:49 -07:00
Chris Troutner eaa08f74e7 Merge pull request #243 from Permissionless-Software-Foundation/ct-unstable
fix(test): Updating integration test
2022-05-15 07:53:53 -07:00
Chris Troutner 9ac620ee64 fix(test): Updating integration test 2022-05-15 07:50:57 -07:00
Chris Troutner ac0a4b0407 Merge pull request #242 from Permissionless-Software-Foundation/ct-unstable
feat(Utxo.isValid()): can test if a UTXO has been spent
2022-05-10 09:34:37 -07:00
Chris Troutner 686012037f feat(Utxo.isValid()): can test if a UTXO has been spent 2022-05-10 09:30:57 -07:00
Chris Troutner cfb7b9f779 Merge pull request #241 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Fixing broken UTXO.get() test
2022-05-09 10:35:21 -07:00
Chris Troutner 06e5af4b5d fix(tests): Fixing broken UTXO.get() test 2022-05-09 10:33:06 -07:00
Chris Troutner 249e93a3f4 Merge pull request #240 from Permissionless-Software-Foundation/ct-unstable
fix(test): Updating UTXO integration tests
2022-05-09 10:25:58 -07:00
Chris Troutner 761a2d226c fix(test): Updating UTXO integration tests 2022-05-09 10:24:00 -07:00
Chris Troutner 5e5e636831 Merge pull request #239 from Permissionless-Software-Foundation/ct-unstable
fix(tests): Fixing broken integration tests for token data
2022-05-09 09:49:02 -07:00
Chris Troutner 2adc2dfebb fix(tests): Fixing broken integration tests for token data 2022-05-09 09:45:28 -07:00
Chris Troutner f9bc44ccb7 Merge pull request #238 from Permissionless-Software-Foundation/ct-unstable
fix(token data): Returning CID rather than content itself
2022-05-07 20:59:55 -07:00
Chris Troutner 298b8f56ff Merge branch 'temp-token-data' into ct-unstable 2022-05-07 20:54:49 -07:00
Chris Troutner 139da8cf7b fix(token data): Returning CID rather than content itself 2022-05-07 20:54:20 -07:00
Chris Troutner 08e03c14d8 Merge pull request #234 from Permissionless-Software-Foundation/dh-psfSlpIndexer
feat(slpindexer): Added bchjs.PsfSlpIndexer.getTokenData()
2022-05-07 20:25:15 -07:00
Chris Troutner 7da010e7af Using config.restURL instead of hard-coded URL 2022-05-07 20:20:30 -07:00
Chris Troutner 7b6eebe7c7 Merge branch 'master' into dh-psfSlpIndexer 2022-05-07 20:05:25 -07:00
Chris Troutner 843f5eacef Merge pull request #237 from Permissionless-Software-Foundation/ct-unstable
fix(Utxo.get()): Added additional integration tests
2022-05-05 12:08:46 -07:00
Chris Troutner 416e302ca5 fix(Utxo.get()): Added additional integration tests 2022-05-05 12:06:59 -07:00
Chris Troutner 1c66da70b6 Merge pull request #236 from Permissionless-Software-Foundation/ct-unstable
fix(mintNFTGroupOpReturn()): Updating to use UTXOs from psf-slp-indexer
2022-05-05 10:45:59 -07:00
Chris Troutner 59e92adf0b fix(mintNFTGroupOpReturn()): Updating to use UTXOs from psf-slp-indexer 2022-05-05 10:43:40 -07:00
Chris Troutner f369411ccb Merge pull request #235 from Permissionless-Software-Foundation/ct-unstable
Updating Utxo.get()
2022-05-05 10:05:36 -07:00
Chris Troutner 0504cf7b8d fix(Utxo.get()): Adding filtering for Group batons 2022-05-05 10:03:35 -07:00
Chris Troutner 88ae47cade fix(Utxo.get()): Adding prototype NFT filtering 2022-05-04 17:44:53 -07:00
Daniel Gonzalez d144b13500 feat(slpindexer): Added bchjs.PsfSlpIndexer.getTokenData() 2022-05-04 18:29:54 -04:00
Chris Troutner 30d58bc337 Merge pull request #233 from Permissionless-Software-Foundation/ct-unstable
fix(readme): Adding backup information
2022-04-30 19:48:24 -07:00
Chris Troutner 651ae3f6ca fix(readme): Adding backup information 2022-04-30 19:37:30 -07:00
Chris Troutner 04cd90f0f4 Merge pull request #232 from Permissionless-Software-Foundation/ct-unstable
Updating README
2022-04-30 16:09:24 -07:00
Chris Troutner 552ee31b79 Adding QR to readme 2022-04-30 16:07:17 -07:00
Chris Troutner affa9619bb fix(README): Updating README 2022-04-30 16:04:22 -07:00
Chris Troutner 39bff131af fix(generateMintOpReturn): Adding additional input validation 2022-04-24 10:28:46 -07:00
Chris Troutner a84fecfe41 Merge pull request #231 from Permissionless-Software-Foundation/ct-unstable
fix(tokentype1.js): Fixing bug in mint OP_RETURN
2022-04-24 09:57:52 -07:00
Chris Troutner 0736c9f2a1 Merge branch 'master' into ct-unstable 2022-04-24 09:55:00 -07:00
Chris Troutner aa898b31e7 fix(tokentype1.js): Fixing bug in mint OP_RETURN 2022-04-24 09:54:14 -07:00
Chris Troutner 29c3381cc5 Merge pull request #230 from Permissionless-Software-Foundation/ct-unstable
fix(Utxo.get()): Hydrating mint batons for tokens
2022-04-24 09:16:59 -07:00
Chris Troutner f7ba6bb3ae fix(Utxo.get()): Hydrating mint batons for tokens 2022-04-24 09:15:10 -07:00
Chris Troutner 62056e1ac4 Merge pull request #229 from Permissionless-Software-Foundation/ct-unstable
fix(tokentype1.js): Mint OP_RETURN works with output from psf-slp-ind…
2022-04-24 08:57:56 -07:00
Chris Troutner a60e2fa2da fix(tokentype1.js): Mint OP_RETURN works with output from psf-slp-indexer 2022-04-24 08:51:49 -07:00
Chris Troutner 37fe679800 Merge pull request #228 from Permissionless-Software-Foundation/ct-unstable
fix(util.js): Fixing small bug with Util.validateAddress()
2022-04-23 17:41:04 -07:00
Chris Troutner 4250d841c5 Merge branch 'master' into ct-unstable 2022-04-23 17:36:16 -07:00
Chris Troutner 31079afc35 fix(util.js): Fixing small bug with Util.validateAddress() 2022-04-23 17:18:15 -07:00
Chris Troutner 62b44d5058 Merge pull request #227 from gitter-badger/gitter-badge
Add a Gitter chat badge to README.md
2022-04-05 05:31:20 -07:00
The Gitter Badger 150295cb43 Add Gitter badge 2022-04-05 12:10:09 +00:00
Chris Troutner 5de2f71ea5 Merge pull request #226 from Permissionless-Software-Foundation/ct-unstable
fix(Transaction.get()): Handling coinbase txs
2022-03-31 07:28:02 -07:00
Chris Troutner 50a1d60229 fix(Transaction.get()): Handling coinbase txs 2022-03-31 07:24:07 -07:00
Chris Troutner f3e25a0338 Merge pull request #225 from Permissionless-Software-Foundation/ct-unstable
fix(npm): Ran npm audit fix. 0 vulns
2022-03-29 16:56:09 -07:00
Chris Troutner 640c582724 fix(npm): Ran npm audit fix. 0 vulns 2022-03-29 16:53:49 -07:00
Chris Troutner b472fa2673 Merge pull request #224 from Permissionless-Software-Foundation/ct-unstable
fix(Utxo.get()): Fixing typo
2022-03-29 16:22:23 -07:00
Chris Troutner ea83335fd2 fix(Utxo.get()): Moving code to where it needs to be 2022-03-29 16:20:03 -07:00
Chris Troutner 3050819784 fix(Utxo.get()): Fixing typo 2022-03-29 16:15:47 -07:00
Chris Troutner 4ca9b1c00b Merge pull request #223 from Permissionless-Software-Foundation/ct-unstable
fix(Utxo.get()): Adding property expected by SLP.tokentype1 lib
2022-03-29 16:05:22 -07:00
Chris Troutner bbc1a45a9e fix(Utxo.get()): Adding property expected by SLP.tokentype1 lib 2022-03-29 15:59:55 -07:00
Chris Troutner 469d08e2b1 Merge pull request #222 from Permissionless-Software-Foundation/ct-unstable
Adding API docs for Transaction lib
2022-03-19 07:03:10 -07:00
Chris Troutner dcadad189b fix(getTokenInfo): Adding API documentation 2022-03-19 07:00:27 -07:00
Chris Troutner 02f81ee203 fix(transactoin): Adding API documentation 2022-03-19 06:58:49 -07:00
Chris Troutner 972bf604d1 Merge pull request #221 from Permissionless-Software-Foundation/ct-unstable
feat(slp): Removing more legacy code that dependended on SLPDB
2022-03-19 06:52:11 -07:00
Chris Troutner 82f084edec feat(slp): Removing more legacy code that dependended on SLPDB 2022-03-19 06:49:43 -07:00
Chris Troutner 4764463fc5 Merge pull request #220 from Permissionless-Software-Foundation/ct-unstable
Removing SLPDB endpoints
2022-03-14 15:54:49 -07:00
Chris Troutner 857458db57 feat(slpdb): Removing endpoints that expect SLPDB interface 2022-03-14 15:50:21 -07:00
Chris Troutner 5f8f05d194 Working on removing slpdb endpoints 2022-03-14 15:13:41 -07:00
Chris Troutner e455d9efec fix(dependencies): Ensuring I'm using exact versions. npm audit = 0 vulns 2022-03-14 14:49:28 -07:00
Chris Troutner ad4e05c289 Bumping to v6.0.0 2022-03-14 14:43:52 -07:00
117 changed files with 23224 additions and 28649 deletions
+2
View File
@@ -4,6 +4,8 @@ wallet-info.txt
wallet.json
integration-test-sweet.sh
integration-test-ss.sh
integration-test-bitfinex.sh
integration-test-pearson.sh
docs/
coverage/
+8
View File
@@ -0,0 +1,8 @@
[
{
"srcDir": "",
"destDir": "",
"files": "**/*.js",
"command": "npm run lint"
}
]
+1 -2
View File
@@ -1,8 +1,7 @@
Copyright 2021 Permissionless Software Foundation contributors
Copyright 2022-2026 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.
+31 -49
View File
@@ -3,26 +3,25 @@
[![Version](https://img.shields.io/npm/v/@psf/bch-js)](https://www.npmjs.com/package/@psf/bch-js)
[![Downloads/week](https://img.shields.io/npm/dw/@psf/bch-js)](https://npmjs.org/package/@psf/bch-js)
[![License](https://img.shields.io/npm/l/@psf/bch-js)](https://github.com/Permissionless-Software-Foundation/bch-js/blob/master/LICENSE.md)
[![js-standard-style](https://img.shields.io/badge/javascript-standard%20code%20style-green.svg?style=flat-square)](https://github.com/feross/standard)
[![js-standard-style](https://img.shields.io/badge/javascript-standard%20code%20style-green.svg?style=flat-square)](https://github.com/feross/standard) [![Join the chat at https://gitter.im/Permissionless-Software-Foundation/bch-js](https://badges.gitter.im/Permissionless-Software-Foundation/bch-js.svg)](https://gitter.im/Permissionless-Software-Foundation/bch-js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[bch-js](https://www.npmjs.com/package/@psf/bch-js) is a JavaScript npm library for creating web and mobile apps that can interact with the Bitcoin Cash (BCH) blockchains. It can be used for free, but requires an account on [FullStack.cash](https://fullstack.cash) for increased rate limits. Learn more from [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) about Full Stack Bitcoin Cash development.
[bch-js](https://www.npmjs.com/package/@psf/bch-js) is a JavaScript npm library for creating web and mobile apps that can interact with the Bitcoin Cash (BCH) blockchain. bch-js contains a toolbox of handy tools, and an easy API for talking with [psf-bch-api REST API](https://github.com/Permissionless-Software-Foundation/psf-bch-api). [FullStack.cash](https://fullstack.cash) offers paid cloud access to psf-bch-api. You can run your own infrastructure by following documentation on [CashStack.info](https://cashstack.info).
### Quick Start Videos:
Here are two YouTube walk-through videos to help you get started:
YouTube walk-through videos to help you get started:
- [Introduction to bch-js and the bch-js-examples repository](https://youtu.be/GD2i1ZUiyrk)
- [Working with the FullStack.cash JWT token](https://youtu.be/GD2i1ZUiyrk)
### Quick Links
- [npm Library](https://www.npmjs.com/package/@psf/bch-js)
- [Documentation](https://bchjs.fullstack.cash/)
- [Examples](https://github.com/Permissionless-Software-Foundation/bch-js-examples)
- [bchn.fullstack.cash](https://bchn.fullstack.cash) - The REST API this library talks to by default.
- [x402-bch.fullstack.cash](https://x402-bch.fullstack.cash) - The REST API this library talks to by default.
- [FullStack.cash](https://fullstack.cash) - cloud-based infrastructure for application developers.
- [FullStack.cash Account](https://fullstack.cash/login) - Get your API key to unlock increased rate limits.
- [Permissionless Software Foundation](https://psfoundation.cash) - The organization that maintains this library.
- [CashStack.info](https://cashstack.info) - bch-js is part of the Cash Stack, a JavaScript framework for writing web 2 and web 3 business applications.
### Quick Notes
@@ -30,66 +29,37 @@ Here are two YouTube walk-through videos to help you get started:
- Instantiate the library in your code:
```
const BCHJS = require("@psf/bch-js")
```javascript
import BCHJS from "@psf/bch-js"
let bchjs = new BCHJS() // Defaults to BCHN network.
```
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:
the [psf-bch-api](https://github.com/Permissionless-Software-Foundation/psf-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/v5/
- ABC Mainnet REST API server: https://abc.fullstack.cash/v5/
- BCHN Mainnet REST API server: https://x402-bch.fullstack.cash/v7/
- Check server status: https://metrics.fullstack.cash
### API Key
The [bch-api](https://github.com/Permissionless-Software-Foundation/bch-api) REST API hosted by [FullStack.cash](https://fullstack.cash) uses JWT tokens to pay for increased
rate limits when interacting with the back end server. See [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) if you want to understand the system-as-a-whole. The JWT token can be fed to bch-js _implicitly_ or _explicitly_.
### Web Apps
- Implicitly: bch-js will detect your JWT token if you set the `BCHJSTOKEN` environment variable.
- Explicitly: You can directly feed in the JWT token with the `apiToken` property when instantiating the library. Here is an example:
[minimal-slp-wallet](https://www.npmjs.com/package/minimal-slp-wallet) is a minimal wallet 'engine' that incorporates bch-js. It's compiled with Browserify for front end apps.
```
const BCHJS = require("@psf/bch-js")
let bchjs = new BCHJS({
restURL: 'https://bchn.fullstack.cash/v5/',
apiToken: 'eyJhbGciO...' // Your JWT token here.
})
```
[This gist](https://gist.github.com/christroutner/6cb9d1b615f3f9363af79723157bc434) shows how to include minimal-slp-wallet into a basic web page without using a framework.
### Gatsby
bch-js is included in this [gatsby-ipfs-template](https://github.com/Permissionless-Software-Foundation/gatsby-ipfs-template) for building uncensorable web apps that can interact with the blockchain. When building a Gatsby (or other front-end app that uses Webpack), you'll need to add these lines to your `gatsby-node.js` file, as per [this issue](https://github.com/gatsbyjs/gatsby/issues/564):
```
exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
node: {
fs: 'empty'
}
})
}
```
This is because the new IPFS class in bch-js uses the fs library for uploading files, which is not supported by Gatsby.
We also provide [minimal-slp-wallet-web](https://www.npmjs.com/package/minimal-slp-wallet-web) as a basic Bitcoin Cash wallet with SLP support, for front end projects. bch-js is encapsulated inside the instantiation of the library Class.
[bch-wallet-web3-spa](https://github.com/Permissionless-Software-Foundation/bch-wallet-web3-spa) is a React web app template using bch-js and minimal-slp-wallet.
## Features
- [ECMAScript 2017 standard JavaScript](https://en.wikipedia.org/wiki/ECMAScript#8th_Edition_-_ECMAScript_2017) used instead of TypeScript. Works
natively with node.js v10 or higher.
- Full SLP tokens support: bch-js has full support for all SLP token functionality, including send, mint, and genesis transactions. It also fully support all aspects of [non-fugible tokans (NFTs)](https://www.youtube.com/watch?v=vvlpYUx6HRs).
- Full SLP tokens support: bch-js has full support for all SLP token functionality, including send, mint, and genesis transactions. It also fully supports all aspects of [non-fugible tokans (NFTs)](https://www.youtube.com/watch?v=vvlpYUx6HRs).
- [Semantic Release](https://github.com/semantic-release/semantic-release) for
continuous delivery using semantic versioning.
- [Greenkeeper](https://greenkeeper.io/) automatic dependency management for
automatically maintaining the latest, most secure dependencies.
- [IPFS uploads](https://ipfs.io) of all files and dependencies, to backup
- [IPFS](https://ipfs.io) and [Radicle](https://radicle.xyz) uploads of all files and dependencies, to backup
dependencies in case they are ever inaccessible from GitHub or npm.
## Documentation:
@@ -109,14 +79,26 @@ live in the same repository. To generate the documentation:
Have questions? Need help? Join our community support
[Telegram channel](https://t.me/bch_js_toolkit)
## IPFS Releases
## Donate
This open source software is developed and maintained by the [Permissionless Software Foundation](https://psfoundation.cash). If this library provides value to you, please consider making a donation to support the PSF developers:
<div align="center">
<img src="./img/donation-qr.png" />
<p>bitcoincash:qqsrke9lh257tqen99dkyy2emh4uty0vky9y0z0lsr</p>
</div>
## IPFS & Radicle Releases
Copies of this repository are also published on [IPFS](https://ipfs.io).
- v4.5.4: QmWv3pxJy3MH8vU5nLUVyzqFxfNKXLQQEnz1rgStNuQijd
- v6.2.10: `bafybeifsioj3ba77u2763nsyuzq53gtbdxsnqpoipvdl4immj6ytznjaoy`
- (with dependencies, node v14.18.2 and npm v8.8.0): `bafybeihfendd4oj6uxvvecm7sluobwwhpb5wdcxhvhmx56e667nxdncd4a`
They are also posted to the Radicle:
- v6.2.10: `rad:git:hnrkkroqnbfwj6uxpfjuhspoxnfm4i8e6oqwy`
## License
[MIT](LICENSE.md)
test
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
# Script to improve contrast in auto-generated API documentation
# The code examples in api-doc use grey-on-grey colors that are hard to read.
# This fixes that issue by improving text contrast for better readability.
# Run this script *after* running 'npm run docs'
FILE="./docs/assets/prism.css"
# Check if file exists
if [ ! -f "$FILE" ]; then
echo "Error: $FILE not found. Make sure to run 'npm run docs' first."
exit 1
fi
# Create a backup
#cp "$FILE" "$FILE.bak"
# Improve contrast:
# - Change default text color from #ccc to #e8e8e8 (brighter light gray)
# - Change comment color from #999 to #b5b5b5 (lighter for better visibility)
# - Change punctuation color from #ccc to #e8e8e8 (consistent with text)
# Replace default text color in code[class*="language-"] and pre[class*="language-"]
sed -i 's/color: #ccc;/color: #000000;/g' "$FILE"
# Replace comment color
sed -i 's/color: #999;/color: #b5b5b5;/g' "$FILE"
# Replace punctuation color (already handled by first sed, but explicit for clarity)
# The punctuation token also uses #ccc, so the first sed command handles it
echo "✓ Improved contrast in prism.css:"
echo " - Default text: #ccc → #e8e8e8"
echo " - Comments: #999 → #b5b5b5"
echo " - Punctuation: #ccc → #e8e8e8"
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

+10451
View File
File diff suppressed because it is too large Load Diff
+10664 -19519
View File
File diff suppressed because it is too large Load Diff
+41 -50
View File
@@ -1,33 +1,25 @@
{
"name": "@psf/bch-js",
"version": "5.0.0",
"description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
"version": "7.0.0",
"type": "module",
"description": "A JavaScript library for working with Bitcoin Cash, eCash, and SLP Tokens",
"author": "Chris Troutner <chris.troutner@gmail.com>",
"contributors": [
"Gabriel Cardona <gabriel@bitcoin.com>",
"Stoyan Zhekov <zh@zhware.net>"
],
"main": "src/bch-js",
"main": "src/bch-js.js",
"scripts": {
"test": "nyc mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
"test:integration": "npm run test:integration: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:bchn:slpdb": "export TESTSLP=1 && export RESTURL=https://bchn.fullstack.cash/v5/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:local:abc": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:local:bchn": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:local:testnet": "RESTURL=http://localhost:4000/v5/ mocha --timeout 30000 test/integration/chains/testnet",
"test:integration:decatur:bchn": "export RESTURL=http://192.168.2.129:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
"test:integration:decatur:abc": "export RESTURL=http://192.168.2.141:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
"test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v5/ && mocha --timeout 30000 test/integration/",
"test:temp": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
"test:temp2": "mocha --timeout=30000 -g '#TransactionLib' test/unit/",
"test:temp3": "export RESTURL=https://bchn.fullstack.cash/v5/ && mocha --timeout 30000 -g '#DSProof' test/integration/chains/",
"test:temp4": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#decodeOpReturn' test/integration/",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "nyc --reporter=html mocha --timeout 25000 test/unit/",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs",
"test": "c8 mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
"test:integration": "npm run test:integration:local:noauth",
"test:integration:nft": "export RESTURL=https://bchn.fullstack.cash/v6/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 -g '#nft1' test/integration/slp.js",
"test:integration:bchn": "export RESTURL=https://bchn.fullstack.cash/v6/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 test/integration/",
"test:integration:local:noauth": "export RESTURL=http://localhost:5942/v6/ && mocha --timeout 30000 test/integration/",
"test:integration:local:auth": "export RESTURL=http://5.78.147.3:5942/v6/ && export BCHJSBEARERTOKEN=temp01 && mocha --timeout 30000 test/integration/",
"test:integration:decatur": "export RESTURL=http://192.168.2.127:5942/v6/ && mocha --timeout 30000 test/integration/",
"test:integration:x402": "export RESTURL=http://localhost:5942/v6/ && export BCHJSWIF=L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4 && mocha --timeout 30000 test/integration/",
"coverage": "nyc --reporter=html mocha --timeout 25000 test/unit/",
"docs": "./node_modules/.bin/apidoc -i src/ -o docs && ./fix-docs-contrast.sh",
"lint": "standard --env mocha --fix"
},
"license": "MIT",
@@ -40,47 +32,41 @@
"@chris.troutner/bip32-utils": "1.0.5",
"@psf/bip21": "2.0.1",
"@psf/bitcoincash-ops": "2.0.0",
"@psf/bitcoincashjs-lib": "4.0.2",
"@psf/bitcoincashjs-lib": "4.0.3",
"@psf/coininfo": "4.0.0",
"axios": "^0.21.4",
"axios": "1.13.2",
"bc-bip68": "1.0.5",
"bchaddrjs-slp": "0.2.5",
"bchaddrjs-slp": "0.2.14",
"bigi": "1.4.2",
"bignumber.js": "9.0.0",
"bip-schnorr": "0.3.0",
"bip38": "2.0.2",
"bip39": "3.0.2",
"bignumber.js": "9.3.1",
"bip-schnorr": "0.6.7",
"bip38": "3.1.1",
"bip39": "3.1.0",
"bip66": "1.1.5",
"bitcoinjs-message": "2.0.0",
"bitcoinjs-message": "2.2.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",
"ini": "6.0.0",
"randombytes": "2.1.0",
"safe-buffer": "5.2.1",
"satoshi-bitcoin": "1.0.5",
"slp-mdm": "0.0.7",
"slp-parser": "0.0.4",
"wif": "2.0.6"
"wif": "2.0.6",
"x402-bch-axios": "1.1.1"
},
"devDependencies": {
"apidoc": "^0.50.5",
"assert": "2.0.0",
"apidoc": "1.2.0",
"assert": "2.1.0",
"c8": "10.1.3",
"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",
"husky": "4.3.8",
"lodash.clonedeep": "4.5.0",
"mocha": "^9.2.1",
"node-mocks-http": "1.7.0",
"mocha": "11.7.5",
"nyc": "15.1.0",
"semantic-release": "^19.0.2",
"semantic-release": "24.2.9",
"sinon": "9.2.2",
"standard": "^16.0.4"
"standard": "17.1.2"
},
"apidoc": {
"title": "bch-js",
@@ -90,5 +76,10 @@
"hooks": {
"pre-commit": "npm run lint"
}
},
"standard": {
"parserOptions": {
"ecmaVersion": "latest"
}
}
}
+42 -24
View File
@@ -1,7 +1,7 @@
// const axios = require("axios")
const Bitcoin = require('@psf/bitcoincashjs-lib')
const cashaddr = require('ecashaddrjs')
const coininfo = require('@psf/coininfo')
// import axios from "axios"
import Bitcoin from '@psf/bitcoincashjs-lib'
import cashaddr from 'ecashaddrjs'
import coininfo from '@psf/coininfo'
class Address {
constructor (config) {
@@ -10,22 +10,11 @@ class Address {
else tmp.restURL = config.restURL
this.restURL = tmp.restURL
this.apiToken = tmp.apiToken
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
}
@@ -133,7 +122,7 @@ class Address {
* @api Address.toEcashAddress() toEcashAddress()
* @apiName toEcashAddress
* @apiGroup Address
* @apiDescription Convert legacy to cashAddress format
* @apiDescription Convert legacy to eCash (XEC) format
*
* @apiExample Example usage:
* // mainnet
@@ -158,6 +147,35 @@ class Address {
return ecashAddress.split(':')[1]
}
/**
* @api Address.toEtokenAddress() toEtokenAddress()
* @apiName toEtokenAddress
* @apiGroup Address
* @apiDescription Convert legacy to eToken (XEC) format
*
* @apiExample Example usage:
* // mainnet
* bchjs.Address.toEtokenAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2')
* // etoken:qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
*
* // mainnet no prefix
* bchjs.Address.toEtokenAddress('bitcoincash:qq50d800hgunr8u4trz3uuppspk3mds0dy9978plt2', false)
* // qq50d800hgunr8u4trz3uuppspk3mds0dyug2v69da
*
*/
toEtokenAddress (address, prefix = true) {
const decoded = this._decode(address)
const etokenAddress = cashaddr.encode(
'etoken',
decoded.type,
decoded.hash
)
if (prefix) return etokenAddress
return etokenAddress.split(':')[1]
}
/**
* @api Address.ecashtoCashAddress() ecashtoCashAddress()
* @apiName ecashtoCashAddress
@@ -283,28 +301,28 @@ class Address {
return {
prefix: 'bitcoincash',
type: 'P2PKH',
hash: hash,
hash,
format: 'legacy'
}
case info.main.versions.scripthash:
return {
prefix: 'bitcoincash',
type: 'P2SH',
hash: hash,
hash,
format: 'legacy'
}
case info.test.versions.public:
return {
prefix: 'bchtest',
type: 'P2PKH',
hash: hash,
hash,
format: 'legacy'
}
case info.test.versions.scripthash:
return {
prefix: 'bchtest',
type: 'P2SH',
hash: hash,
hash,
format: 'legacy'
}
default:
@@ -842,4 +860,4 @@ class Address {
}
}
module.exports = Address
export default Address
+82 -49
View File
@@ -7,36 +7,45 @@
*/
// bch-api mainnet.
const DEFAULT_REST_API = 'https://api.fullstack.cash/v5/'
// const DEFAULT_REST_API = "http://localhost:3000/v5/"
// Global npm libraries
import axios from 'axios'
import {
createSigner,
withPaymentInterceptor,
createPaymentHeader,
selectPaymentRequirements
} from 'x402-bch-axios'
// local deps
const BitcoinCash = require('./bitcoincash')
const Crypto = require('./crypto')
const Util = require('./util')
const Blockchain = require('./blockchain')
const Control = require('./control')
const Generating = require('./generating')
const Mining = require('./mining')
const RawTransactions = require('./raw-transactions')
const Mnemonic = require('./mnemonic')
const Address = require('./address')
const HDNode = require('./hdnode')
const TransactionBuilder = require('./transaction-builder')
const ECPair = require('./ecpair')
const Script = require('./script')
const Price = require('./price')
const Schnorr = require('./schnorr')
const SLP = require('./slp/slp')
const Encryption = require('./encryption')
const Utxo = require('./utxo')
const Transaction = require('./transaction')
const DSProof = require('./dsproof')
const Ecash = require('./ecash')
import BitcoinCash from './bitcoincash.js'
import Crypto from './crypto.js'
import Util from './util.js'
import Blockchain from './blockchain.js'
import Control from './control.js'
import Generating from './generating.js'
import Mining from './mining.js'
import RawTransactions from './raw-transactions.js'
import Mnemonic from './mnemonic.js'
import Address from './address.js'
import HDNode from './hdnode.js'
import TransactionBuilder from './transaction-builder.js'
import ECPair from './ecpair.js'
import Script from './script.js'
import Price from './price.js'
import Schnorr from './schnorr.js'
import SLP from './slp/slp.js'
import Encryption from './encryption.js'
import Utxo from './utxo.js'
import Transaction from './transaction.js'
import DSProof from './dsproof.js'
import Ecash from './ecash.js'
// Indexers
const Electrumx = require('./electrumx')
const PsfSlpIndexer = require('./psf-slp-indexer')
import Electrumx from './electrumx.js'
import PsfSlpIndexer from './psf-slp-indexer.js'
const DEFAULT_REST_API = 'https://api.fullstack.cash/v6/'
class BCHJS {
constructor (config) {
@@ -47,39 +56,55 @@ class BCHJS {
this.restURL = process.env.RESTURL
} else this.restURL = DEFAULT_REST_API
// Retrieve the apiToken
this.apiToken = '' // default value.
if (config && config.apiToken && config.apiToken !== '') {
this.apiToken = config.apiToken
} else if (process.env.BCHJSTOKEN && process.env.BCHJSTOKEN !== '') {
this.apiToken = process.env.BCHJSTOKEN
// Retrieve the Bearer token for simple token authentication.
this.bearerToken = '' // default value.
if (config && config.bearerToken && config.bearerToken !== '') {
this.bearerToken = config.bearerToken
} else if (process.env.BCHJSBEARERTOKEN && process.env.BCHJSBEARERTOKEN !== '') {
this.bearerToken = process.env.BCHJSBEARERTOKEN
}
// Retrieve the Basic Authentication password.
this.authPass = '' // default value.
if (config && config.authPass && config.authPass !== '') {
this.authPass = config.authPass
} else if (process.env.BCHJSAUTHPASS && process.env.BCHJSAUTHPASS !== '') {
this.authPass = process.env.BCHJSAUTHPASS
}
// Generate a Basic Authentication token from an auth password
// Generate the authentication token for the authorization header.
this.authToken = ''
if (this.authPass) {
// console.log(`bch-js initialized with authPass: ${this.authPass}`)
// Generate the header for Basic Authentication.
const combined = `fullstackcash:${this.authPass}`
const base64Credential = Buffer.from(combined).toString('base64')
this.authToken = `Basic ${base64Credential}`
if (this.bearerToken) {
this.authToken = `Bearer ${this.bearerToken}`
}
// x402 payment configuration
// If a WIF private key is provided, enable x402 automatic payment handling
this.wif = ''
if (config && config.wif && config.wif !== '') {
this.wif = config.wif
} else if (process.env.BCHJSWIF && process.env.BCHJSWIF !== '') {
this.wif = process.env.BCHJSWIF
}
this.paymentAmountSats = (config && config.paymentAmountSats) || 2000 * 10
this.bchServerURL = (config && config.bchServerURL) || 'https://free-bch.fullstack.cash'
const libConfig = {
restURL: this.restURL,
apiToken: this.apiToken,
authToken: this.authToken
}
// console.log(`apiToken: ${this.apiToken}`)
// If WIF is provided, create an axios instance with x402 payment interceptor
// Otherwise, let sub-modules use their own axios import for backwards compatibility
if (this.wif) {
let axiosInstance = axios.create({
baseURL: this.restURL,
headers: {
authorization: this.authToken
}
})
const signer = createSigner(this.wif, this.paymentAmountSats)
axiosInstance = withPaymentInterceptor(
axiosInstance,
signer,
{ bchServerURL: this.bchServerURL }
)
libConfig.axios = axiosInstance
}
// ElectrumX indexer
this.Electrumx = new Electrumx(libConfig)
@@ -117,7 +142,15 @@ class BCHJS {
this.eCash = new Ecash()
this.PsfSlpIndexer = new PsfSlpIndexer(libConfig)
// Expose x402 helper functions for advanced use cases
this.x402 = {
createSigner,
withPaymentInterceptor,
createPaymentHeader,
selectPaymentRequirements
}
}
}
module.exports = BCHJS
export default BCHJS
+10 -11
View File
@@ -1,13 +1,12 @@
const Bitcoin = require('@psf/bitcoincashjs-lib')
const sb = require('satoshi-bitcoin')
const bitcoinMessage = require('bitcoinjs-message')
const bs58 = require('bs58')
const bip21 = require('@psf/bip21')
const coininfo = require('@psf/coininfo')
const bip38 = require('bip38')
const wif = require('wif')
const Buffer = require('safe-buffer').Buffer
import Bitcoin from '@psf/bitcoincashjs-lib'
import sb from 'satoshi-bitcoin'
import bitcoinMessage from 'bitcoinjs-message'
import bs58 from 'bs58'
import bip21 from '@psf/bip21'
import coininfo from '@psf/coininfo'
import bip38 from 'bip38'
import wif from 'wif'
import { Buffer } from 'safe-buffer'
class BitcoinCash {
constructor (address) {
@@ -519,4 +518,4 @@ class BitcoinCash {
}
}
module.exports = BitcoinCash
export default BitcoinCash
+60 -68
View File
@@ -3,33 +3,22 @@
- Add blockhash functionality back into getTxOutProof
*/
const axios = require('axios')
import axios from 'axios'
// let _this
class Blockchain {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.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.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// _this = this
}
/**
@@ -52,8 +41,8 @@ class Blockchain {
*/
async getBestBlockHash () {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBestBlockHash`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getBestBlockHash`,
this.axiosOptions
)
return response.data
@@ -106,8 +95,8 @@ class Blockchain {
if (!blockhash || typeof blockhash !== 'string') {
throw new Error('blockhash must be a string')
}
const response = await axios.post(
`${this.restURL}blockchain/getblock`,
const response = await this.axios.post(
`${this.restURL}full-node/blockchain/getblock`,
{
blockhash,
verbosity
@@ -160,8 +149,8 @@ class Blockchain {
*/
async getBlockchainInfo () {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBlockchainInfo`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getBlockchainInfo`,
this.axiosOptions
)
return response.data
@@ -191,12 +180,15 @@ class Blockchain {
*/
async getBlockCount () {
try {
const response = await axios.get(
`${this.restURL}blockchain/getBlockCount`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getBlockCount`,
this.axiosOptions
)
return response.data
} catch (error) {
console.log('Error in bch-js/blockchain.js/getBlockCount()')
console.log('blockchain.js restURL: ', this.restURL)
if (error.response && error.response.data) throw error.response.data
else throw error
}
@@ -224,8 +216,8 @@ class Blockchain {
if (typeof height !== 'string') height = JSON.stringify(height)
try {
const response = await axios.get(
`${this.restURL}blockchain/getBlockHash/${height}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getBlockHash/${height}`,
this.axiosOptions
)
return response.data
@@ -271,8 +263,8 @@ class Blockchain {
try {
// Handle single hash.
if (typeof hash === 'string') {
const response = await axios.get(
`${this.restURL}blockchain/getBlockHeader/${hash}?verbose=${verbose}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getBlockHeader/${hash}?verbose=${verbose}`,
this.axiosOptions
)
@@ -281,11 +273,11 @@ class Blockchain {
// Handle array of hashes.
} else if (Array.isArray(hash)) {
// Dev note: must use axios.post for unit test stubbing.
const response = await axios.post(
`${this.restURL}blockchain/getBlockHeader`,
const response = await this.axios.post(
`${this.restURL}full-node/blockchain/getBlockHeader`,
{
hashes: hash,
verbose: verbose
verbose
},
this.axiosOptions
)
@@ -332,8 +324,8 @@ class Blockchain {
*/
async getChainTips () {
try {
const response = await axios.get(
`${this.restURL}blockchain/getChainTips`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getChainTips`,
this.axiosOptions
)
return response.data
@@ -364,8 +356,8 @@ class Blockchain {
*/
async getDifficulty () {
try {
const response = await axios.get(
`${this.restURL}blockchain/getDifficulty`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getDifficulty`,
this.axiosOptions
)
return response.data
@@ -380,8 +372,8 @@ class Blockchain {
if (typeof txid !== 'string') txid = JSON.stringify(txid)
try {
const response = await axios.get(
`${this.restURL}blockchain/getMempoolAncestors/${txid}?verbose=${verbose}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getMempoolAncestors/${txid}?verbose=${verbose}`,
this.axiosOptions
)
return response.data
@@ -395,8 +387,8 @@ class Blockchain {
if (typeof txid !== 'string') txid = JSON.stringify(txid)
try {
const response = await axios.get(
`${this.restURL}blockchain/getMempoolDescendants/${txid}?verbose=${verbose}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getMempoolDescendants/${txid}?verbose=${verbose}`,
this.axiosOptions
)
return response.data
@@ -492,16 +484,16 @@ class Blockchain {
try {
if (typeof txid === 'string') {
const response = await axios.get(
`${this.restURL}blockchain/getMempoolEntry/${txid}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getMempoolEntry/${txid}`,
this.axiosOptions
)
return response.data
} else if (Array.isArray(txid)) {
// Dev note: must use axios.post for unit test stubbing.
const response = await axios.post(
`${this.restURL}blockchain/getMempoolEntry`,
const response = await this.axios.post(
`${this.restURL}full-node/blockchain/getMempoolEntry`,
{
txids: txid
},
@@ -543,8 +535,8 @@ class Blockchain {
*/
async getMempoolInfo () {
try {
const response = await axios.get(
`${this.restURL}blockchain/getMempoolInfo`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getMempoolInfo`,
this.axiosOptions
)
return response.data
@@ -590,8 +582,8 @@ class Blockchain {
*/
async getRawMempool (verbose = false) {
try {
const response = await axios.get(
`${this.restURL}blockchain/getRawMempool?vebose=${verbose}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/getRawMempool?vebose=${verbose}`,
this.axiosOptions
)
return response.data
@@ -634,14 +626,14 @@ class Blockchain {
}
// Send the request to the REST API.
// const response = await axios.get(
// const response = await this.axios.get(
// `${this.restURL}blockchain/getTxOut/${txid}/${n}?includeMempool=${includeMempool}`,
// this.axiosOptions
// )
const response = await axios.post(
`${this.restURL}blockchain/getTxOut`,
const response = await this.axios.post(
`${this.restURL}full-node/blockchain/getTxOut`,
{
txid: txid,
txid,
vout: n,
mempool: includeMempool
},
@@ -695,19 +687,19 @@ class Blockchain {
try {
// Single txid.
if (typeof txids === 'string') {
const path = `${this.restURL}blockchain/getTxOutProof/${txids}`
const path = `${this.restURL}full-node/blockchain/getTxOutProof/${txids}`
// if (blockhash) path = `${path}?blockhash=${blockhash}`
const response = await axios.get(path, this.axiosOptions)
const response = await this.axios.get(path, this.axiosOptions)
return response.data
// Array of txids.
} else if (Array.isArray(txids)) {
// Dev note: must use axios.post for unit test stubbing.
const response = await axios.post(
`${this.restURL}blockchain/getTxOutProof`,
const response = await this.axios.post(
`${this.restURL}full-node/blockchain/getTxOutProof`,
{
txids: txids
txids
},
this.axiosOptions
)
@@ -724,8 +716,8 @@ class Blockchain {
async preciousBlock (blockhash) {
try {
const response = await axios.get(
`${this.restURL}blockchain/preciousBlock/${blockhash}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/preciousBlock/${blockhash}`,
this.axiosOptions
)
return response.data
@@ -737,8 +729,8 @@ class Blockchain {
async pruneBlockchain (height) {
try {
const response = await axios.post(
`${this.restURL}blockchain/pruneBlockchain/${height}`,
const response = await this.axios.post(
`${this.restURL}full-node/blockchain/pruneBlockchain/${height}`,
this.axiosOptions
)
return response.data
@@ -750,8 +742,8 @@ class Blockchain {
async verifyChain (checklevel = 3, nblocks = 6) {
try {
const response = await axios.get(
`${this.restURL}blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/verifyChain?checklevel=${checklevel}&nblocks=${nblocks}`,
this.axiosOptions
)
return response.data
@@ -802,8 +794,8 @@ class Blockchain {
try {
// Single block
if (typeof proof === 'string') {
const response = await axios.get(
`${this.restURL}blockchain/verifyTxOutProof/${proof}`,
const response = await this.axios.get(
`${this.restURL}full-node/blockchain/verifyTxOutProof/${proof}`,
this.axiosOptions
)
return response.data
@@ -811,8 +803,8 @@ class Blockchain {
// Array of hashes.
} else if (Array.isArray(proof)) {
// Dev note: must use axios.post for unit test stubbing.
const response = await axios.post(
`${this.restURL}blockchain/verifyTxOutProof`,
const response = await this.axios.post(
`${this.restURL}full-node/blockchain/verifyTxOutProof`,
{
proofs: proof
},
@@ -830,4 +822,4 @@ class Blockchain {
}
}
module.exports = Blockchain
export default Blockchain
+11 -22
View File
@@ -2,33 +2,22 @@
API endpoints for basic control and information of the full node.
*/
const axios = require('axios')
import axios from 'axios'
// let _this // Global reference to the instance of this class.
class Control {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.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.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// _this = this
}
/**
@@ -79,8 +68,8 @@ class Control {
*/
async getNetworkInfo () {
try {
const response = await axios.get(
`${this.restURL}control/getNetworkInfo`,
const response = await this.axios.get(
`${this.restURL}full-node/control/getNetworkInfo`,
this.axiosOptions
)
return response.data
@@ -92,8 +81,8 @@ class Control {
async getMemoryInfo () {
try {
const response = await axios.get(
`${this.restURL}control/getMemoryInfo`,
const response = await this.axios.get(
`${this.restURL}full-node/control/getMemoryInfo`,
this.axiosOptions
)
return response.data
@@ -115,4 +104,4 @@ class Control {
// }
}
module.exports = Control
export default Control
+3 -3
View File
@@ -1,5 +1,5 @@
const randomBytes = require('randombytes')
const Bitcoin = require('@psf/bitcoincashjs-lib')
import randomBytes from 'randombytes'
import Bitcoin from '@psf/bitcoincashjs-lib'
class Crypto {
/**
@@ -135,4 +135,4 @@ class Crypto {
}
}
module.exports = Crypto
export default Crypto
+6 -17
View File
@@ -1,27 +1,16 @@
const axios = require('axios')
import axios from '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.axiosOptions = {
headers: {
authorization: this.authToken
}
}
@@ -67,7 +56,7 @@ class DSProof {
throw new Error(`txid must be of length 64 (not ${txid.length})`)
}
const response = await _this.axios.get(
`${this.restURL}dsproof/getdsproof/${txid}`,
`${this.restURL}full-node/dsproof/getdsproof/${txid}`,
this.axiosOptions
)
return response.data
@@ -78,4 +67,4 @@ class DSProof {
}
}
module.exports = DSProof
export default DSProof
+1 -1
View File
@@ -44,4 +44,4 @@ class eCash {
}
}
module.exports = eCash
export default eCash
+3 -3
View File
@@ -1,5 +1,5 @@
const Bitcoin = require('@psf/bitcoincashjs-lib')
const coininfo = require('@psf/coininfo')
import Bitcoin from '@psf/bitcoincashjs-lib'
import coininfo from '@psf/coininfo'
class ECPair {
static setAddress (address) {
@@ -166,4 +166,4 @@ class ECPair {
}
}
module.exports = ECPair
export default ECPair
+43 -46
View File
@@ -3,38 +3,29 @@
by FullStack.cash
*/
// Public npm libraries
const axios = require('axios')
import axios from 'axios'
// Local libraries.
const Blockchain = require('./blockchain')
import Blockchain from './blockchain.js'
// const Address = require('./address')
// let _this
class ElectrumX {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.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.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// Encapsulate dependencies
this.blockchain = new Blockchain(config)
// _this = this
}
/**
@@ -44,7 +35,7 @@ class ElectrumX {
* @apiDescription Return a list of uxtos for an address.
*
* @apiExample Example usage:
* (async () => {
* (async () => {
* try {
* let utxo = await bchjs.Electrumx.utxo('bitcoincash:qqh793x9au6ehvh7r2zflzguanlme760wuzehgzjh9');
* console.log(utxo);
@@ -104,18 +95,20 @@ class ElectrumX {
*/
async utxo (address) {
try {
// console.log(`electrumx.js/utxo() restURL: ${this.restURL}`)
// Handle single address.
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/utxos/${address}`,
const response = await this.axios.get(
`${this.restURL}fulcrum/utxos/${address}`,
this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}electrumx/utxos`,
const response = await this.axios.post(
`${this.restURL}fulcrum/utxos`,
{
addresses: address
},
@@ -190,16 +183,16 @@ class ElectrumX {
try {
// Handle single address.
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/balance/${address}`,
const response = await this.axios.get(
`${this.restURL}fulcrum/balance/${address}`,
this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}electrumx/balance`,
const response = await this.axios.post(
`${this.restURL}fulcrum/balance`,
{
addresses: address
},
@@ -284,23 +277,24 @@ class ElectrumX {
* }
*
*/
async transactions (address, usrObj = null) {
async transactions (address, usrObj = null, allTxs = false) {
try {
// Handle single address.
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/transactions/${address}`,
const response = await this.axios.get(
`${this.restURL}fulcrum/transactions/${address}/${allTxs}`,
this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}electrumx/transactions`,
const response = await this.axios.post(
`${this.restURL}fulcrum/transactions`,
{
addresses: address,
usrObj // pass user data when making an internal call.
usrObj, // pass user data when making an internal call.
allTxs
},
this.axiosOptions
)
@@ -310,6 +304,7 @@ class ElectrumX {
throw new Error('Input address must be a string or array of strings.')
} catch (error) {
// console.log('Error in transactions(): ', error)
if (error.response && error.response.data) throw error.response.data
else throw error
}
@@ -381,16 +376,16 @@ class ElectrumX {
try {
// Handle single address.
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/unconfirmed/${address}`,
const response = await this.axios.get(
`${this.restURL}fulcrum/unconfirmed/${address}`,
this.axiosOptions
)
return response.data
// Handle array of addresses.
} else if (Array.isArray(address)) {
const response = await axios.post(
`${this.restURL}electrumx/unconfirmed`,
const response = await this.axios.post(
`${this.restURL}fulcrum/unconfirmed`,
{
addresses: address
},
@@ -450,8 +445,8 @@ class ElectrumX {
*/
async blockHeader (height, count = 1) {
try {
const response = await axios.get(
`${this.restURL}electrumx/block/headers/${height}?count=${count}`,
const response = await this.axios.get(
`${this.restURL}fulcrum/block/headers/${height}?count=${count}`,
this.axiosOptions
)
return response.data
@@ -541,14 +536,14 @@ class ElectrumX {
try {
// Handle single transaction.
if (typeof txid === 'string') {
const response = await axios.get(
`${this.restURL}electrumx/tx/data/${txid}`,
const response = await this.axios.get(
`${this.restURL}fulcrum/tx/data/${txid}`,
this.axiosOptions
)
return response.data
} else if (Array.isArray(txid)) {
const response = await axios.post(
`${this.restURL}electrumx/tx/data`,
const response = await this.axios.post(
`${this.restURL}fulcrum/tx/data`,
{
txids: txid
},
@@ -589,8 +584,8 @@ class ElectrumX {
async broadcast (txHex) {
try {
if (typeof txHex === 'string') {
const response = await axios.post(
`${this.restURL}electrumx/tx/broadcast`,
const response = await this.axios.post(
`${this.restURL}fulcrum/tx/broadcast`,
{ txHex },
this.axiosOptions
)
@@ -713,9 +708,11 @@ class ElectrumX {
return this.sortConfTxs(modifiedTxs, sortingOrder)
} catch (err) {
console.log('Error in util.js/sort0ConfTxs')
console.log('electrumx.js restURL: ', this.restURL)
throw err
}
}
}
module.exports = ElectrumX
export default ElectrumX
+30 -17
View File
@@ -2,36 +2,49 @@
This library contains useful functions that deal with encryption.
*/
const axios = require('axios')
import axios from 'axios'
let _this
class Encryption {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.axios = axios
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.axios || axios
_this = this
}
/**
* @api encryption.getPubKey() getPubKey()
* @apiName Encryption getPubKey()
* @apiGroup Encryption
* @apiDescription Get the public key for an address
* Given an address, the command will search the blockchain for a public
* key associated with that address. The address needs to have made at least
* one spend transaction, in order for its public key to be retrievable.
*
* @apiExample Example usage:
*(async () => {
* try {
* const addr = 'bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d'
* const pubkey = await bchjs.encryption.getPubKey(addr);
* console.log(pubkey);
* } catch(err) {
* console.error(err)
* }
*})()
*
*/
// Search the blockchain for a public key associated with a BCH address.
async getPubKey (addr) {
try {
@@ -51,4 +64,4 @@ class Encryption {
}
}
module.exports = Encryption
export default Encryption
+8 -19
View File
@@ -1,35 +1,24 @@
const axios = require('axios')
import axios from 'axios'
// let _this
class Generating {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.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.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// _this = this
}
async generateToAddress (blocks, address, maxtries = 1000000) {
try {
const response = await axios.post(
const response = await this.axios.post(
`${this.restURL}generating/generateToAddress/${blocks}/${address}?maxtries=${maxtries}`,
this.axiosOptions
)
@@ -41,4 +30,4 @@ class Generating {
}
}
module.exports = Generating
export default Generating
+5 -5
View File
@@ -1,7 +1,7 @@
const Bitcoin = require('@psf/bitcoincashjs-lib')
const coininfo = require('@psf/coininfo')
const bip32utils = require('@chris.troutner/bip32-utils')
const bchaddrjs = require('bchaddrjs-slp')
import Bitcoin from '@psf/bitcoincashjs-lib'
import coininfo from '@psf/coininfo'
import bip32utils from '@chris.troutner/bip32-utils'
import bchaddrjs from 'bchaddrjs-slp'
class HDNode {
constructor (address) {
@@ -637,4 +637,4 @@ class HDNode {
}
}
module.exports = HDNode
export default HDNode
+11 -22
View File
@@ -1,35 +1,24 @@
const axios = require('axios')
import axios from 'axios'
// let _this
class Mining {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.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.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// _this = this
}
async getBlockTemplate (templateRequest) {
try {
const response = await axios.get(
const response = await this.axios.get(
`${this.restURL}mining/getBlockTemplate/${templateRequest}`,
this.axiosOptions
)
@@ -42,7 +31,7 @@ class Mining {
async getMiningInfo () {
try {
const response = await axios.get(
const response = await this.axios.get(
`${this.restURL}mining/getMiningInfo`,
this.axiosOptions
)
@@ -55,7 +44,7 @@ class Mining {
async getNetworkHashps (nblocks = 120, height = 1) {
try {
const response = await axios.get(
const response = await this.axios.get(
`${this.restURL}mining/getNetworkHashps?nblocks=${nblocks}&height=${height}`,
this.axiosOptions
)
@@ -71,7 +60,7 @@ class Mining {
if (parameters) path = `${path}?parameters=${parameters}`
try {
const response = await axios.post(path, this.axiosOptions)
const response = await this.axios.post(path, this.axiosOptions)
return response.data
} catch (error) {
if (error.response && error.response.data) throw error.response.data
@@ -80,4 +69,4 @@ class Mining {
}
}
module.exports = Mining
export default Mining
+8 -11
View File
@@ -1,11 +1,8 @@
/* eslint no-prototype-builtins: "off" */
/* eslint node/no-callback-literal: "off" */
const BIP39 = require('bip39')
const randomBytes = require('randombytes')
const Bitcoin = require('@psf/bitcoincashjs-lib')
const Buffer = require('safe-buffer').Buffer
const wif = require('wif')
import BIP39 from 'bip39'
import randomBytes from 'randombytes'
import Bitcoin from '@psf/bitcoincashjs-lib'
import { Buffer } from 'safe-buffer'
import wif from 'wif'
class Mnemonic {
constructor (address) {
@@ -338,7 +335,7 @@ class Mnemonic {
}
}
module.exports = Mnemonic
export default Mnemonic
// The following code is from: https://raw.githubusercontent.com/iancoleman/bip39/7ff86d4c983f1e8c80b87b31acfd69fcf98c1b82/src/js/levenshtein.js
@@ -356,7 +353,7 @@ const _extend = function (dst) {
const sources = Array.prototype.slice.call(arguments, 1)
for (let i = 0; i < sources.length; ++i) {
const src = sources[i]
for (const p in src) if (src.hasOwnProperty(p)) dst[p] = src[p]
for (const p in src) if (Object.prototype.hasOwnProperty.call(src, p)) dst[p] = src[p]
}
return dst
}
@@ -503,7 +500,7 @@ const Levenshtein = {
try {
options.progress.call(null, (i * 100.0) / str1.length)
} catch (err) {
return cb(`Progress callback: ${err.toString()}`)
return cb(new Error(`Progress callback: ${err.toString()}`))
}
}
+43 -108
View File
@@ -1,47 +1,20 @@
const axios = require('axios')
import axios from 'axios'
// let _this
class Price {
constructor (config) {
// _this = this
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
this.axios = axios
}
// This endpoint is deprecated. Documentation removed.
async current (currency = 'usd') {
try {
const response = await this.axios.get(
`https://index-api.bitcoin.com/api/v0/cash/price/${currency.toLowerCase()}`
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return response.data.price
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.axios || axios
}
/**
@@ -122,80 +95,6 @@ class Price {
}
}
/**
* @api price.getBchaUsd() getBchaUsd()
* @apiName Price getBchaUsd()
* @apiGroup Price
* @apiDescription Return current price of BCHA in USD.
* 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 () => {
* try {
* let current = await bchjs.Price.getBchaUsd();
* console.log(current);
* } catch(err) {
* console.error(err)
* }
*})()
*
* // 212.34
*/
async getBchaUsd () {
try {
const response = await this.axios.get(
`${this.restURL}price/bchausd`,
this.axiosOptions
)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
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()
@@ -230,6 +129,42 @@ class Price {
else throw err
}
}
/**
* @api price.getPsffppPrice() getPsffppPrice()
* @apiName Price getPsffppPrice()
* @apiGroup Price
* @apiDescription Return the cost in PSF tokens to write 1MB of data to the PSFFPP
* Find out more at PSFFPP.com. This is a IPFS pinning service that can pin
* up to 100MB per transaction into its network. The cost is denominated in
* PSF SLP tokens. The endpoint returns the cost to pin 1MB of data to the
* PSFFPP network.
*
* @apiExample Example usage:
*(async () => {
* try {
* let current = await bchjs.Price.getPsffppPrice();
* console.log(current);
* } catch(err) {
* console.error(err)
* }
*})()
*
* // 0.08335233
*/
async getPsffppPrice () {
try {
const response = await this.axios.get(
`${this.restURL}price/psffpp`,
this.axiosOptions
)
return response.data.writePrice
} catch (err) {
if (err.response && err.response.data) throw err.response.data
else throw err
}
}
}
module.exports = Price
export default Price
+186 -28
View File
@@ -7,41 +7,30 @@
*/
// Public npm libraries
const axios = require('axios')
import axios from 'axios'
// Local libraries
const RawTransaction = require('./raw-transactions')
const SlpUtils = require('./slp/utils')
import RawTransaction from './raw-transactions.js'
import SlpUtils from './slp/utils.js'
// let _this
class PsfSlpIndexer {
constructor (config = {}) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.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.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// Encapsulate dependencies
this.rawTransaction = new RawTransaction(config)
this.slpUtils = new SlpUtils(config)
// _this = this
}
/**
@@ -71,8 +60,8 @@ class PsfSlpIndexer {
*/
async status () {
try {
const response = await axios.get(
`${this.restURL}psf/slp/status`,
const response = await this.axios.get(
`${this.restURL}slp/status`,
this.axiosOptions
)
return response.data
@@ -136,8 +125,8 @@ class PsfSlpIndexer {
// Handle single address.
if (typeof address === 'string') {
const response = await axios.post(
`${this.restURL}psf/slp/address`,
const response = await this.axios.post(
`${this.restURL}slp/address`,
{ address },
this.axiosOptions
)
@@ -194,14 +183,15 @@ class PsfSlpIndexer {
* }
* }
*
*
*/
async tokenStats (tokenId, withTxHistory = false) {
try {
// Handle single address.
if (typeof tokenId === 'string') {
const response = await axios.post(
`${this.restURL}psf/slp/token`,
const response = await this.axios.post(
`${this.restURL}slp/token`,
{ tokenId, withTxHistory },
this.axiosOptions
)
@@ -288,15 +278,20 @@ class PsfSlpIndexer {
*/
async tx (txid) {
try {
// console.log('txid: ', txid)
// Handle single address.
if (typeof txid === 'string') {
const response = await axios.post(
`${this.restURL}psf/slp/txid`,
const response = await this.axios.post(
`${this.restURL}slp/txid`,
{ txid },
this.axiosOptions
)
// console.log('response: ', response)
return response.data
}
throw new Error('Input txid must be a string.')
} catch (error) {
// console.log('error: ', error)
@@ -316,6 +311,7 @@ class PsfSlpIndexer {
// Check if this txid belongs to a blacklisted token.
const isInBlacklist = await this.checkBlacklist(txid)
// console.log('isInBlacklist: ', isInBlacklist)
// Get the TX Details from the full node.
const txDetails = await this.rawTransaction.getTxData(txid)
@@ -368,6 +364,168 @@ class PsfSlpIndexer {
return false
}
}
/**
* @api PsfSlpIndexer.getTokenData() getTokenData()
* @apiName Token Data
* @apiGroup PSF SLP
* @apiDescription Get mutable and immutable data if the token contains them.
*
* @apiExample Example usage:
* (async () => {
* try {
* let tokenData = await bchjs.PsfSlpIndexer.getTokenData('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
* console.log(tokenData)
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* genesisData: {
* type: 1,
* ticker: 'TROUT',
* name: "Trout's test token",
* tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
* documentUri: 'troutsblog.com',
* documentHash: '',
* decimals: 2,
* mintBatonIsActive: true,
* tokensInCirculationBN: '100098953386',
* tokensInCirculationStr: '100098953386',
* blockCreated: 622414,
* totalBurned: '1046614',
* totalMinted: '100100000000'
* ]
* },
* immutableData :{
* issuer:"FullStack.cash.",
* website:"https://fullstack.cash/",
* dateCreated:"2022-01-11"
* },
* mutableData :{
* "tokenIcon":"https://gateway.ipfs.io/ipfs/bafybeiehitanirn5gmhqjg44xrmdtomn4n5lu5yjoepsvgpswk5mggaw6i/LP_logo-1.png",
* "about":"Mutable data managed with npm package: https://www.npmjs.com/package/slp-mutable-data"
* }
* }
*
*/
async getTokenData (tokenId, withTxHistory = false) {
try {
const url = `${this.restURL}slp/token/data`
// console.log(`url: ${url}`)
// Handle single address.
if (typeof tokenId === 'string') {
const response = await this.axios.post(
// 'https://bchn.fullstack.cash/v5/psf/slp/token/data/',
url,
{ tokenId, withTxHistory },
this.axiosOptions
)
return response.data
}
throw new Error('Input tokenId must be a string.')
} catch (error) {
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
/**
* @api PsfSlpIndexer.getTokenData2() getTokenData2()
* @apiName Token Data
* @apiGroup PSF SLP
* @apiDescription Get token icon and other media associated with a token.
*
* Get the icon for a token, given it's token ID.
* This function expects a string input of a token ID property.
* This function returns an object with a tokenIcon property that contains
* the URL to the icon.
*
* The output object always have these properties:
* - tokenIcon: A url to the token icon, if it exists.
* - tokenStats: Data about the token from psf-slp-indexer.
* - optimizedTokenIcon: An alternative, potentially more optimal, url to the token icon, if it exists.
* - iconRepoCompatible: true if the token icon is available via token.bch.sx
* - ps002Compatible: true if the token icon is compatible with PS007 specification.
*
* @apiExample Example usage:
* (async () => {
* try {
* let tokenData = await bchjs.PsfSlpIndexer.getTokenData2('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2')
* console.log(tokenData)
* } catch(error) {
* console.error(error)
* }
* })()
*
* {
* tokenStats: {
* type: 1,
* ticker: 'CTAIA006',
* name: 'CTAIA006 - AI Art by Chris Troutner',
* tokenId: '0e4543f820699294ab57e02ee2b1815a8bbc7b17a4333e4a138034e4b2324a61',
* documentUri: 'ipfs://bafybeia5yuq7rg6jmwquako7t277cwrobcunz7cumqrv4wn6bgfvthemku',
* documentHash: '78a00e9db312b8fff4e5c37cf592be83e6bab7f3bd5a54c9545bad5d4f3ee0f5',
* decimals: 0,
* mintBatonIsActive: false,
* tokensInCirculationBN: '1',
* tokensInCirculationStr: '1',
* blockCreated: 757507,
* totalBurned: '0',
* totalMinted: '1'
* },
* mutableData: {
* tokenIcon: 'https://bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm.ipfs.w3s.link/whale-night-sky-01.png',
* fullSizedUrl: '',
* about: 'This NFT was created using the PSF Token Studio at https://nft-creator.fullstack.cash',
* userData: ''
* },
* immutableData: {
* issuer: 'http://psfoundation.cash',
* website: 'https://nft-creator.fullstack.cash',
* dateCreated: '9/12/2022, 5:17:38 PM',
* userData: '{\n' +
* ' "title": "CTAIA006 - AI Art by Chris Troutner",\n' +
* ' "about": "AI generated art. Generated from DALL-E at https://labs.openai.com",\n' +
* ' "prompt": "whale swimming through a sky full of stars",\n' +
* ' "algorithm": "DALL-E (stable diffusion)",\n' +
* ' "set": "1-of-2"\n' +
* '}'
* },
* tokenIcon: 'https://bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm.ipfs.w3s.link/whale-night-sky-01.png',
* fullSizedUrl: '',
* optimizedTokenIcon: 'https://p2wdb-gateway-678.fullstack.cash/ipfs/bafybeihiv5jvlhoymmbous3h2akotogj6b7hruhjcj3zq7dsfteimuuttm/whale-night-sky-01.png',
* optimizedFullSizedUrl: '',
* iconRepoCompatible: false,
* ps002Compatible: true
* }
*
*/
async getTokenData2 (tokenId, updateCache = false) {
try {
const url = `${this.restURL}slp/token/data2`
// console.log(`url: ${url}`)
// Handle single address.
if (typeof tokenId === 'string') {
const response = await this.axios.post(
// 'https://bchn.fullstack.cash/v5/psf/slp/token/data/',
url,
{ tokenId, updateCache },
this.axiosOptions
)
return response.data
}
throw new Error('Input tokenId must be a string.')
} catch (error) {
// console.log('error: ', error)
if (error.response && error.response.data) throw error.response.data
else throw error
}
}
}
module.exports = PsfSlpIndexer
export default PsfSlpIndexer
+38 -41
View File
@@ -1,33 +1,20 @@
const axios = require('axios')
import axios from 'axios'
// let _this
class RawTransactions {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// Encapsulate dependencies
this.axios = axios
// this = this
// Use the shared axios instance if provided, otherwise fall back to axios
this.axios = config.axios || axios
}
/**
@@ -96,8 +83,8 @@ class RawTransactions {
try {
// Single hex
if (typeof hex === 'string') {
const response = await axios.get(
`${this.restURL}rawtransactions/decodeRawTransaction/${hex}`,
const response = await this.axios.get(
`${this.restURL}full-node/rawtransactions/decodeRawTransaction/${hex}`,
this.axiosOptions
)
@@ -107,13 +94,13 @@ class RawTransactions {
} else if (Array.isArray(hex)) {
const options = {
method: 'POST',
url: `${this.restURL}rawtransactions/decodeRawTransaction`,
url: `${this.restURL}full-node/rawtransactions/decodeRawTransaction`,
data: {
hexes: hex
},
headers: this.axiosOptions.headers
}
const response = await axios(options)
const response = await this.axios(options)
return response.data
}
@@ -164,8 +151,8 @@ class RawTransactions {
try {
if (typeof script === 'string') {
const response = await axios.get(
`${this.restURL}rawtransactions/decodeScript/${script}`,
const response = await this.axios.get(
`${this.restURL}full-node/rawtransactions/decodeScript/${script}`,
this.axiosOptions
)
@@ -173,13 +160,13 @@ class RawTransactions {
} else if (Array.isArray(script)) {
const options = {
method: 'POST',
url: `${this.restURL}rawtransactions/decodeScript`,
url: `${this.restURL}full-node/rawtransactions/decodeScript`,
data: {
hexes: script
},
headers: this.axiosOptions.headers
}
const response = await axios(options)
const response = await this.axios(options)
return response.data
}
@@ -263,8 +250,8 @@ class RawTransactions {
// 'getRawTransaction() this.axiosOptions: ',
// this.axiosOptions
// )
const response = await axios.get(
`${this.restURL}rawtransactions/getRawTransaction/${txid}?verbose=${verbose}`,
const response = await this.axios.get(
`${this.restURL}full-node/rawtransactions/getRawTransaction/${txid}?verbose=${verbose}`,
this.axiosOptions
)
@@ -272,15 +259,15 @@ class RawTransactions {
} else if (Array.isArray(txid)) {
const options = {
method: 'POST',
url: `${this.restURL}rawtransactions/getRawTransaction`,
url: `${this.restURL}full-node/rawtransactions/getRawTransaction`,
data: {
txids: txid,
verbose: verbose,
verbose,
usrObj // pass user data when making an internal call.
},
headers: this.axiosOptions.headers
}
const response = await axios(options)
const response = await this.axios(options)
return response.data
}
@@ -381,6 +368,8 @@ class RawTransactions {
// Appends the BCH address to the inputs of the transaction.
async getTxData (txid) {
try {
// console.log('getTxData() txid: ', txid)
if (typeof txid !== 'string') {
throw new Error(
'Input to raw-transaction.js/getTxData() must be a string containg a TXID.'
@@ -391,17 +380,25 @@ class RawTransactions {
const txDetails = await this.getRawTransaction(txid, true)
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
const inAddrs = await this._getInputAddrs(txDetails)
// console.log(`inAddrs: ${JSON.stringify(inAddrs, null, 2)}`)
try {
const inAddrs = await this._getInputAddrs(txDetails)
// console.log(`inAddrs: ${JSON.stringify(inAddrs, null, 2)}`)
// Add the input address to the transaction data.
for (let i = 0; i < inAddrs.length; i++) {
txDetails.vin[i].address = inAddrs[i].address
txDetails.vin[i].value = inAddrs[i].value
// Add the input address to the transaction data.
for (let i = 0; i < inAddrs.length; i++) {
txDetails.vin[i].address = inAddrs[i].address
txDetails.vin[i].value = inAddrs[i].value
}
} catch (err) {
// Coinbase transactions will throw an error. Just ignore them and
// pass back the raw transaction data.
/* exit quietly */
}
// console.log(`txDetails: ${JSON.stringify(txDetails, null, 2)}`)
return txDetails
} catch (error) {
// console.log('error: ', error)
if (error.error) throw new Error(error.error)
// This case handles rate limit errors.
@@ -450,7 +447,7 @@ class RawTransactions {
// Single tx hex.
if (typeof hex === 'string') {
const response = await this.axios.get(
`${this.restURL}rawtransactions/sendRawTransaction/${hex}`,
`${this.restURL}full-node/rawtransactions/sendRawTransaction/${hex}`,
this.axiosOptions
)
@@ -468,7 +465,7 @@ class RawTransactions {
} else if (Array.isArray(hex)) {
const options = {
method: 'POST',
url: `${this.restURL}rawtransactions/sendRawTransaction`,
url: `${this.restURL}full-node/rawtransactions/sendRawTransaction`,
data: {
hexes: hex
},
@@ -488,4 +485,4 @@ class RawTransactions {
}
}
module.exports = RawTransactions
export default RawTransactions
+5 -16
View File
@@ -1,24 +1,13 @@
const schnorr = require('bip-schnorr')
import schnorr from 'bip-schnorr'
class Schnorr {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
}
@@ -509,4 +498,4 @@ class Schnorr {
}
}
module.exports = Schnorr
export default Schnorr
+4 -2
View File
@@ -1,4 +1,6 @@
const Bitcoin = require('@psf/bitcoincashjs-lib')
import Bitcoin from '@psf/bitcoincashjs-lib'
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const opcodes = require('@psf/bitcoincash-ops')
class Script {
@@ -240,4 +242,4 @@ class Script {
}
}
module.exports = Script
export default Script
+4 -4
View File
@@ -1,9 +1,9 @@
const BCHJSAddress = require('../address')
import BCHJSAddress from '../address.js'
import bchaddrjs from 'bchaddrjs-slp'
// const bchAddress = new BCHJSAddress()
let bchAddress
const bchaddrjs = require('bchaddrjs-slp')
class Address extends BCHJSAddress {
constructor (config) {
super(config)
@@ -661,4 +661,4 @@ class Address extends BCHJSAddress {
}
}
module.exports = Address
export default Address
+4 -4
View File
@@ -1,9 +1,9 @@
// const BCHJS = require("../bch-js")
// import BCHJS from "../bch-js.js"
// const bchjs = new BCHJS()
const BCHJSECPair = require('../ecpair')
import BCHJSECPair from '../ecpair.js'
const bchaddrjs = require('bchaddrjs-slp')
import bchaddrjs from 'bchaddrjs-slp'
class ECPair extends BCHJSECPair {
/*
@@ -31,4 +31,4 @@ class ECPair extends BCHJSECPair {
}
}
module.exports = ECPair
export default ECPair
+7 -98
View File
@@ -9,18 +9,18 @@
*/
// Public npm libraries
const axios = require('axios')
// import axios from 'axios'
// Local libraries.
const Address = require('./address')
import Address from './address.js'
// const BigNumber = require('bignumber.js')
const slpMdm = require('slp-mdm')
// import BigNumber from 'bignumber.js'
import slpMdm from 'slp-mdm'
import TransactionBuilder from '../transaction-builder.js'
// let _this
// const addy = new Address()
let addy
const TransactionBuilder = require('../transaction-builder')
class Nft1 {
constructor (config) {
@@ -147,7 +147,7 @@ class Nft1 {
// Loop through the tokenUtxos array and find the minting baton.
let mintBatonUtxo
for (let i = 0; i < tokenUtxos.length; i++) {
if (tokenUtxos[i].utxoType === 'minting-baton') {
if (tokenUtxos[i].utxoType === 'minting-baton' || tokenUtxos[i].type === 'baton') {
mintBatonUtxo = tokenUtxos[i]
}
}
@@ -419,97 +419,6 @@ class Nft1 {
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 = Nft1
export default Nft1
+11 -22
View File
@@ -7,34 +7,23 @@
// imports
// require deps
// const BCHJS = require("../bch-js")
const Address = require('./address')
const ECPair = require('./ecpair')
// const HDNode = require("./hdnode")
const TokenType1 = require('./tokentype1')
const NFT1 = require('./nft1')
const Utils = require('./utils')
// import BCHJS from "../bch-js.js"
import Address from './address.js'
import ECPair from './ecpair.js'
// import HDNode from "./hdnode.js"
import TokenType1 from './tokentype1.js'
import NFT1 from './nft1.js'
import Utils from './utils.js'
// SLP is a superset of BITBOX
class SLP {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
@@ -46,4 +35,4 @@ class SLP {
}
}
module.exports = SLP
export default SLP
+18 -24
View File
@@ -2,41 +2,30 @@
This library handles the OP_RETURN of SLP TokenType1 transactions.
*/
// const BCHJS = require("../bch-js")
// import BCHJS from "../bch-js.js"
// const bchjs = new BCHJS()
const Address = require('./address')
const Script = require('../script')
import Address from './address.js'
import Script from '../script.js'
const BigNumber = require('bignumber.js')
const slpMdm = require('slp-mdm')
const axios = require('axios')
import BigNumber from 'bignumber.js'
import slpMdm from 'slp-mdm'
import axios from 'axios'
import TransactionBuilder from '../transaction-builder.js'
// const addy = new Address()
let addy
const TransactionBuilder = require('../transaction-builder')
let _this // local global
class TokenType1 {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
@@ -375,11 +364,14 @@ class TokenType1 {
if (!Array.isArray(tokenUtxos)) {
throw new Error('tokenUtxos must be an array.')
}
if (!mintQty) {
throw new Error('mintQty must be a positive number.')
}
// Loop through the tokenUtxos array and find the minting baton.
let mintBatonUtxo
for (let i = 0; i < tokenUtxos.length; i++) {
if (tokenUtxos[i].utxoType === 'minting-baton') {
if (tokenUtxos[i].utxoType === 'minting-baton' || tokenUtxos[i].type === 'baton') {
mintBatonUtxo = tokenUtxos[i]
}
}
@@ -395,7 +387,7 @@ class TokenType1 {
if (!tokenId) {
throw new Error('tokenId property not found in mint-baton UTXO.')
}
if (!decimals) {
if (!decimals && decimals !== 0) {
throw new Error('decimals property not found in mint-baton UTXO.')
}
@@ -404,6 +396,8 @@ class TokenType1 {
baseQty = Math.floor(baseQty)
baseQty = baseQty.toString()
if (isNaN(baseQty)) throw new Error('baseQty is non a number!')
// Signal that the baton should be passed or detroyed.
let batonVout = 2
if (destroyBaton) batonVout = null
@@ -478,4 +472,4 @@ class TokenType1 {
}
}
module.exports = TokenType1
export default TokenType1
+9 -1781
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -1,7 +1,7 @@
const Bitcoin = require('@psf/bitcoincashjs-lib')
const coininfo = require('@psf/coininfo')
const bip66 = require('bip66')
const bip68 = require('bc-bip68')
import Bitcoin from '@psf/bitcoincashjs-lib'
import coininfo from '@psf/coininfo'
import bip66 from 'bip66'
import bip68 from 'bc-bip68'
class TransactionBuilder {
static setAddress (address) {
@@ -163,4 +163,4 @@ class TransactionBuilder {
}
}
module.exports = TransactionBuilder
export default TransactionBuilder
+15 -472
View File
@@ -3,13 +3,13 @@
*/
// Global npm libraries
const BigNumber = require('bignumber.js')
// const BigNumber = require('bignumber.js')
// Local libraries
const RawTransaction = require('./raw-transactions')
const SlpUtils = require('./slp/utils')
const Blockchain = require('./blockchain')
const PsfSlpIndexer = require('./psf-slp-indexer')
import RawTransaction from './raw-transactions.js'
import SlpUtils from './slp/utils.js'
import Blockchain from './blockchain.js'
import PsfSlpIndexer from './psf-slp-indexer.js'
class Transaction {
constructor (config = {}) {
@@ -20,11 +20,6 @@ class Transaction {
this.psfSlpIndexer = new PsfSlpIndexer(config)
}
// Proxy the call to the psf-slp-indexer.
async get (txid) {
return await this.psfSlpIndexer.tx(txid)
}
/**
* @api Transaction.get() get()
* @apiName get
@@ -34,8 +29,6 @@ class Transaction {
* 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 () => {
@@ -47,481 +40,31 @@ class Transaction {
* }
* })()
*/
// CT 10/31/21: TODO: this function should be refactored to use get2(), but
// add waterfall validation of the TX and its inputs.
async getOld (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
}
async get (txid) {
// console.log('transaction.get() txid: ', txid)
return await this.psfSlpIndexer.tx(txid)
}
/**
* @api Transaction.get3() get3()
* @apiName get3
* @api Transaction.getTokenInfo() getTokenInfo()
* @apiName getTokenInfo
* @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.
* Given the TXID of a token transaction, it will return data about that
* token by retrieving the data from the Genesis transaction and docoding
* the OP_RETURN.
*
* 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");
* let txData = await bchjs.Transaction.getTokenInfo("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) {
@@ -534,4 +77,4 @@ class Transaction {
}
}
module.exports = Transaction
export default Transaction
+5 -97
View File
@@ -1,30 +1,17 @@
const axios = require('axios')
// import axios from 'axios'
// let _this
class Util {
constructor (config) {
this.restURL = config.restURL
this.apiToken = config.apiToken
this.authToken = config.authToken
if (this.authToken) {
// Add Basic Authentication token to the authorization header.
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
} else {
// Add JWT token to the authorization header.
this.axiosOptions = {
headers: {
authorization: `Token ${this.apiToken}`
}
this.axiosOptions = {
headers: {
authorization: this.authToken
}
}
// _this = this
}
/**
@@ -223,85 +210,6 @@ class Util {
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.
*
* @apiExample Example usage:
* (async () => {
* try {
* let validateAddress = await bchjs.Util.validateAddress("bitcoincash:qzc86hrdufhcwlyzk7k82x77kfs2myekn57nv9cw5f");
* console.log(validateAddress);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // { isvalid: true,
* // address: '17fshh33qUze2yifiJ2sXgijSMzJ2KNEwu',
* // scriptPubKey: '76a914492ae280d70af33acf0ae7cd329b961e65e9cbd888ac',
* // ismine: true,
* // iswatchonly: false,
* // isscript: false,
* // pubkey: '0312eeb9ae5f14c3cf43cece11134af860c2ef7d775060e3a578ceec888acada31',
* // iscompressed: true,
* // account: 'Test' }
*
* (async () => {
* try {
* let validateAddress = await bchjs.Util.validateAddress(["bitcoincash:qzc86hrdufhcwlyzk7k82x77kfs2myekn57nv9cw5f"]);
* console.log(validateAddress);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // [{ isvalid: true,
* // address: '17fshh33qUze2yifiJ2sXgijSMzJ2KNEwu',
* // scriptPubKey: '76a914492ae280d70af33acf0ae7cd329b961e65e9cbd888ac',
* // ismine: true,
* // iswatchonly: false,
* // isscript: false,
* // pubkey: '0312eeb9ae5f14c3cf43cece11134af860c2ef7d775060e3a578ceec888acada31',
* // iscompressed: true,
* // account: 'Test' }]
*/
async validateAddress (address) {
try {
// Single block
if (typeof address === 'string') {
const response = await axios.get(
`${this.restURL}util/validateAddress/${address}`,
this.axiosOptions
)
return response.data
// Array of blocks.
} else if (Array.isArray(address)) {
const options = {
method: 'POST',
url: `${this.restURL}util/validateAddress`,
data: {
addresses: address
},
headers: {
authorization: `Token ${this.apiToken}`
}
}
const response = await axios(options)
return response.data
}
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
}
}
}
module.exports = Util
export default Util
+133 -287
View File
@@ -6,10 +6,11 @@
*/
// Local libraries
const Electrumx = require('./electrumx')
const Slp = require('./slp/slp')
const PsfSlpIndexer = require('./psf-slp-indexer')
const BigNumber = require('bignumber.js')
import Electrumx from './electrumx.js'
import Slp from './slp/slp.js'
import PsfSlpIndexer from './psf-slp-indexer.js'
import BigNumber from 'bignumber.js'
import Blockchain from './blockchain.js'
class UTXO {
constructor (config = {}) {
@@ -18,276 +19,7 @@ class UTXO {
this.slp = new Slp(config)
this.psfSlpIndexer = new PsfSlpIndexer(config)
this.BigNumber = BigNumber
}
/**
* @api Utxo.getOld() getOld()
* @apiName getOld
* @apiGroup UTXO
* @apiDescription Get UTXOs for an address (from SLPDB)
*
* 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.getOld('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 getOld (address, useWhitelist = false) {
try {
if (!address) {
throw new Error('Address must be an array or a string')
}
// Convert address to an array if it is a string.
if (typeof address === 'string') address = [address]
// 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
}
this.blockchain = new Blockchain(config)
}
/**
@@ -300,6 +32,8 @@ class UTXO {
* properties:
* - address: "" - the address these UTXOs are associated with
* - bchUtxos: [] - UTXOs confirmed to be spendable as normal BCH
* - infoUtxos: [] - UTXOs under of 1000 sats or less that can not be categorized
* as another type of UTXO (like a token).
* - 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
@@ -350,7 +84,10 @@ class UTXO {
}
// Ensure the address is a BCH address.
const addr = this.slp.Address.toCashAddress(address)
let addr = address
if (!addr.includes('ecash')) {
addr = this.slp.Address.toCashAddress(address)
}
// Get the UTXOs associated with the address.
const utxoData = await this.electrumx.utxo(addr)
@@ -374,6 +111,11 @@ class UTXO {
}
}
// Get the sync status of the SLP indexer
const syncStatus = await this.psfSlpIndexer.status()
const slpIndexerHeight = syncStatus.status.syncedBlockHeight
const chainBlockHeight = syncStatus.status.chainBlockHeight
// Loop through the Fulcrum UTXOs.
for (let i = 0; i < utxos.length; i++) {
const thisUtxo = utxos[i]
@@ -395,6 +137,7 @@ class UTXO {
thisUtxo.qty = thisSlpUtxo.qty
thisUtxo.tokenId = thisSlpUtxo.tokenId
thisUtxo.address = thisSlpUtxo.address
thisUtxo.tokenType = thisSlpUtxo.tokenType
break
}
@@ -410,38 +153,85 @@ class UTXO {
// the indexer.
const txData = await this.psfSlpIndexer.tx(thisUtxo.tx_hash)
// console.log(`txData: ${JSON.stringify(txData, null, 2)}`)
if (txData.txData.isValidSlp === null) {
// txData.txData === undefined if the SLP indexer is syncing.
if (!txData.txData || txData.txData.isValidSlp === null) {
thisUtxo.isSlp = null
} else {
thisUtxo.isSlp = false
}
// console.log(`thisUtxo.isSlp: ${thisUtxo.isSlp}`)
// If the SLP indexer more than 1 block behind the full node, then
// move any BCH UTXOs of 600 sats or less into the null array. This
// protects token UTXOs from being burned accidentally.
if (slpIndexerHeight < chainBlockHeight - 1) {
if (thisUtxo.value < 601) {
thisUtxo.isSlp = null
}
}
}
}
// Get token UTXOs
let type1TokenUtxos = utxos.filter(
x => x.isSlp === true && x.type === 'token'
x => x.isSlp === true && x.type === 'token' && x.tokenType === 1
)
// Hydrate the UTXOs with additional token data.
type1TokenUtxos = await this.hydrateTokenData(type1TokenUtxos)
const bchUtxos = utxos.filter(x => x.isSlp === false)
const type1BatonUtxos = utxos.filter(
x => x.isSlp === true && x.type === 'baton'
// Collect BCH UTXOs above 1000 sats
const bchUtxos = utxos.filter(x => x.isSlp === false && x.value > 1000)
// Collect all BCH UTXOs at 1000 sats or smaller
// The prevents SLP tokens, BCMR, and other 'colored' UTXOs from being
// accidentally included into normal BCH transactions.
const infoUtxos = utxos.filter(x => x.isSlp === false && x.value <= 1000)
// Collect and hydrate any type1 baton UTXOs
let type1BatonUtxos = utxos.filter(
x => x.isSlp === true && x.type === 'baton' && x.tokenType === 1
)
type1BatonUtxos = await this.hydrateTokenData(type1BatonUtxos)
// Collect and hydrate NFT Group tokens
let nftGroupTokenUtxos = utxos.filter(
x => x.isSlp === true && x.type === 'token' && x.tokenType === 129
)
nftGroupTokenUtxos = await this.hydrateTokenData(nftGroupTokenUtxos)
// Collect and hydrate any Group baton UTXOs
let groupBatonUtxos = utxos.filter(
x => x.isSlp === true && x.type === 'baton' && x.tokenType === 129
)
groupBatonUtxos = await this.hydrateTokenData(groupBatonUtxos)
// Collect and hydrate NFT child tokens
let nftChildTokenUtxos = utxos.filter(
x => x.isSlp === true && x.type === 'token' && x.tokenType === 65
)
nftChildTokenUtxos = await this.hydrateTokenData(nftChildTokenUtxos)
// Isolate any UTXOs that are marked null by the SLP indexer.
const nullUtxos = utxos.filter(x => x.isSlp === null)
const outObj = {
address: addr,
bchUtxos,
infoUtxos,
slpUtxos: {
type1: {
tokens: type1TokenUtxos,
mintBatons: type1BatonUtxos
},
nft: {} // Allocated for future support of NFT spec.
group: {
tokens: nftGroupTokenUtxos,
mintBatons: groupBatonUtxos
},
nft: {
tokens: nftChildTokenUtxos
}
},
nullUtxos
}
@@ -495,11 +285,16 @@ class UTXO {
thisUtxo.documentHash = genData[0].tokenData.documentHash
thisUtxo.decimals = genData[0].tokenData.decimals
// Calculate the real token quantity
const qty = new BigNumber(thisUtxo.qty).dividedBy(
10 ** parseInt(thisUtxo.decimals)
)
thisUtxo.qtyStr = qty.toString()
if (thisUtxo.type !== 'baton') {
// Calculate the real token quantity
const qty = new BigNumber(thisUtxo.qty).dividedBy(
10 ** parseInt(thisUtxo.decimals)
)
thisUtxo.qtyStr = qty.toString()
// tokenQty is property expected by SLP.tokentype1.js library
thisUtxo.tokenQty = thisUtxo.qtyStr
}
}
return utxoAry
@@ -572,6 +367,57 @@ class UTXO {
return utxos[largestIndex]
}
/**
* @api Utxo.isValid() isValid()
* @apiName isValid
* @apiGroup UTXO
* @apiDescription Validate that UTXO exists and is still spendable.
*
* Given a UTXO, this method will return true if the UTXO is still in the
* mempool and still valid for spending. It will return false if the UTXO
* has been spent.
*
* @apiExample Example usage:
* (async () => {
* try {
* const utxos = await bchjs.Utxo.get('bitcoincash:qq54fgjn3hz0357n8a6guy4demw9xfkjk5jcj0xr0z');
* const isValid = bchjs.Utxo.isValid(utxos.bchUtxos[0])
* console.log(isValid);
* } catch(error) {
* console.error(error)
* }
* })()
*
* // returns
* true
*/
async isValid (utxo) {
try {
// console.log('utxo: ', utxo)
// Convert different properties from different indexers
const txid = utxo.txid || utxo.tx_hash
const vout = utxo.vout | utxo.tx_pos
// Query the full node
const txOut = await this.blockchain.getTxOut(txid, vout, true)
// console.log('txOut: ', txOut)
// Simplify results to either true or false.
let isValid = null
if (txOut === null) {
isValid = false
} else {
isValid = true
}
return isValid
} catch (err) {
console.log('Error in Utxo.isValid()')
throw err
}
}
}
module.exports = UTXO
export default UTXO
+4 -3
View File
@@ -2,10 +2,11 @@
A Mocha test file for running end-to-end (e2e) tests.
*/
// const mocha = require("mocha")
const assert = require('chai').assert
// import mocha from "mocha"
import chai from 'chai'
const sendToken = require('./send-token/send-token')
import sendToken from './send-token/send-token.js'
const { assert } = chai
describe('#end-to-end tests', () => {
describe('#send-tokens', () => {
+8 -2
View File
@@ -3,12 +3,18 @@
IPFS is working.
*/
process.env.IPFS_API = 'http://localhost:5001'
import { fileURLToPath } from 'url'
import { dirname } from 'path'
// process.env.IPFS_API = `https://ipfs-api.fullstack.cash`
const BCHJS = require('../../../src/bch-js')
import BCHJS from '../../../src/bch-js.js'
process.env.IPFS_API = 'http://localhost:5001'
const bchjs = new BCHJS()
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
describe('#IPFS', () => {
it('should upload a file to the server', async () => {
const path = `${__dirname.toString()}/ipfs-e2e.js`
@@ -12,13 +12,14 @@
mocha --timeout=30000 anonymous-rate-limits.js
*/
const assert = require('chai').assert
import chai from 'chai'
// const RESTURL = `http://localhost:3000/v5/`
import BCHJS from '../../../src/bch-js.js'
const { assert } = chai
// 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 })
describe('#anonymous rate limits', () => {
@@ -7,11 +7,12 @@
- Update the PRO_PASS value with a current PRO_PASSES string used by bch-api.
*/
const assert = require('chai').assert
import chai from 'chai'
import BCHJS from '../../../src/bch-js.js'
const { assert } = chai
const PRO_PASS = 'testpassword'
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https://abc.fullstack.cash/v5/',
+4 -3
View File
@@ -11,12 +11,13 @@
- Update the JWT_TOKEN value with a current free-level JWT token.
*/
const assert = require('chai').assert
import chai from 'chai'
import BCHJS from '../../../src/bch-js.js'
const { assert } = chai
const JWT_TOKEN =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2YyMDIyMWMxMDAxMmFkOTQwMSIsImVtYWlsIjoiZGVtb0BkZW1vLmNvbSIsImFwaUxldmVsIjoxMCwicmF0ZUxpbWl0IjozLCJpYXQiOjE2MDQ4NTQ2OTEsImV4cCI6MTYwNzQ0NjY5MX0.iwse0z0KDKHx9graCxcOwj6lSlfKQAb1zLhmjvygvts'
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
restURL: 'https://api.fullstack.cash/v5/',
// restURL: `http://localhost:3000/v5/`,
@@ -11,12 +11,13 @@
- Update the JWT_TOKEN value with a paid tier JWT token.
*/
const assert = require('chai').assert
import chai from 'chai'
import BCHJS from '../../../src/bch-js.js'
const { assert } = chai
const JWT_TOKEN =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M'
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https:/abc.fullstack.cash/v5/',
+4 -3
View File
@@ -7,12 +7,13 @@
- Update the JWT_TOKEN value with a current indexer-level JWT token.
*/
const assert = require('chai').assert
import chai from 'chai'
import BCHJS from '../../../src/bch-js.js'
const { assert } = chai
const JWT_TOKEN =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMzIyNzEwNCwiZXhwIjoxNjA1ODE5MTA0fQ.CV36grzdD36Ht3BwZGHG4XU40CVDzMRw9Ars1x1r27M'
const BCHJS = require('../../../src/bch-js')
const bchjs = new BCHJS({
// restURL: `https://bchn.fullstack.cash/v5/`,
restURL: 'https://abc.fullstack.cash/v5/',
@@ -18,26 +18,33 @@ const RECV_ADDR2 = 'bchtest:qz6yw0kqgfkknfy6jh2jvlfnkzmre3lt2u0pgcckdk'
const SATOSHIS_TO_SEND = 1000
// Instantiate BITBOX.
const bitboxLib = '../../../lib/BITBOX'
const BITBOXSDK = require(bitboxLib)
const BITBOX = new BITBOXSDK({ restURL: 'https://trest.bitcoin.com/v2/' })
// const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
// const util = require('util')
// Note: Dynamic require with variable - use dynamic import in ESM
const bitboxLib = '../../../lib/BITBOX.js'
let BITBOXSDK, BITBOX, walletInfo
// Open the wallet generated with create-wallet.
let walletInfo = {}
try {
walletInfo = require('./wallet.json')
} catch (err) {
console.log(
'Could not open wallet.json. Generate a wallet with create-wallet first.'
)
process.exit(0)
}
let SEND_ADDR, SEND_MNEMONIC
const SEND_ADDR = walletInfo.cashAddress
const SEND_MNEMONIC = walletInfo.mnemonic
// Load dependencies asynchronously
async function loadDependencies () {
BITBOXSDK = (await import(bitboxLib)).default
BITBOX = new BITBOXSDK({ restURL: 'https://trest.bitcoin.com/v2/' })
// const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
try {
const { createRequire } = await import('module')
const require = createRequire(import.meta.url)
walletInfo = require('./wallet.json')
} catch (err) {
console.log(
'Could not open wallet.json. Generate a wallet with create-wallet first.'
)
process.exit(0)
}
SEND_ADDR = walletInfo.cashAddress
SEND_MNEMONIC = walletInfo.mnemonic
}
async function testSend () {
try {
@@ -57,7 +64,10 @@ async function testSend () {
console.log('Error in testSend: ', err)
}
}
testSend()
// Load dependencies then run test
loadDependencies().then(() => {
testSend()
})
// Build a TX hex with the largest UTXO.
async function buildTx1 (recAddr) {
@@ -17,26 +17,33 @@ const RECV_ADDR1 = 'bchtest:qzfn2mly05t6fjsh5kjj0dqq0jjtct27ng089dgg05'
const SATOSHIS_TO_SEND = 1000
// Instantiate BITBOX.
const bitboxLib = '../../../lib/BITBOX'
const BITBOXSDK = require(bitboxLib)
const BITBOX = new BITBOXSDK({ restURL: 'https://trest.bitcoin.com/v2/' })
// const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
// const util = require('util')
// Note: Dynamic require with variable - use dynamic import in ESM
const bitboxLib = '../../../lib/BITBOX.js'
let BITBOXSDK, BITBOX, walletInfo
// Open the wallet generated with create-wallet.
let walletInfo = {}
try {
walletInfo = require('./wallet.json')
} catch (err) {
console.log(
'Could not open wallet.json. Generate a wallet with create-wallet first.'
)
process.exit(0)
}
let SEND_ADDR, SEND_MNEMONIC
const SEND_ADDR = walletInfo.cashAddress
const SEND_MNEMONIC = walletInfo.mnemonic
// Load dependencies asynchronously
async function loadDependencies () {
BITBOXSDK = (await import(bitboxLib)).default
BITBOX = new BITBOXSDK({ restURL: 'https://trest.bitcoin.com/v2/' })
// const BITBOX = new BITBOXSDK({ restURL: "http://localhost:3000/v2/" })
try {
const { createRequire } = await import('module')
const require = createRequire(import.meta.url)
walletInfo = require('./wallet.json')
} catch (err) {
console.log(
'Could not open wallet.json. Generate a wallet with create-wallet first.'
)
process.exit(0)
}
SEND_ADDR = walletInfo.cashAddress
SEND_MNEMONIC = walletInfo.mnemonic
}
async function testSend () {
try {
@@ -54,7 +61,10 @@ async function testSend () {
console.log('Error in testSend: ', err)
}
}
testSend()
// Load dependencies then run test
loadDependencies().then(() => {
testSend()
})
// Build a TX hex with the largest UTXO.
async function buildTx1 (recAddr) {
+4 -4
View File
@@ -8,7 +8,9 @@
*/
// Inspect utility used for debugging.
const util = require('util')
import util from 'util'
import lib from '../util/e2e-util.js'
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
@@ -21,8 +23,6 @@ util.inspect.defaultOptions = {
const WALLET1 = '../wallet1.json'
const WALLET2 = '../wallet2.json'
const lib = require('../util/e2e-util')
// The main test function.
// Sends a token and reports on how long it takes to show up in SLPDB production.
async function sendTokenTest () {
@@ -95,6 +95,6 @@ function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
module.exports = {
export default {
sendTokenTest
}
+6 -3
View File
@@ -3,14 +3,14 @@
tests.
*/
module.exports = {
import SLPSDK from '../../../lib/SLP.js'
export default {
openWallet,
sendToken,
getTestTokenBalance,
threeDecimals
}
const SLPSDK = require('../../../lib/SLP')
const slpsdk = new SLPSDK()
const testTokenId =
@@ -20,6 +20,9 @@ const testTokenId =
// token balance.
async function openWallet (filename) {
try {
// Note: Dynamic imports for JSON files - use createRequire
const { createRequire } = await import('module')
const require = createRequire(import.meta.url)
const walletInfo = require(filename)
// const walletBalance = await getBalance(walletInfo)
+27
View File
@@ -0,0 +1,27 @@
/*
This test is used to interrogate the behavior of bch-js when a psf-slp-indexer
panicks and starts indexing from SLP genesis. In this corner-case, bch-js
should detect the indexer is out of sync, and protect any token UTXOs by
moving any UTXO under 600 sats into the null array.
TO RUN THIS TEST:
- Reset a local instance of psf-slp-indexer to sync from genesis.
- Start a local copy of bch-api
*/
import BCHJS from '../../../src/bch-js.js'
const bchjs = new BCHJS({
restURL: 'http://localhost:3000/v5/'
})
async function startTest () {
try {
const addr = 'bitcoincash:qzkvas58zag9tjry693mflkjze2m20ps7vx7uw7z9d'
const result = await bchjs.Utxo.get(addr)
console.log(`result: ${JSON.stringify(result, null, 2)}`)
} catch (err) {
console.error('Error in startTest(): ', err)
}
}
startTest()
+8 -6
View File
@@ -7,13 +7,13 @@
of an e2e test to be properly tested.
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
import chai from 'chai'
import BCHJS from '../../src/bch-js.js'
// Inspect utility used for debugging.
const util = require('util')
import util from 'util'
const assert = chai.assert
const bchjs = new BCHJS()
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
@@ -318,7 +318,9 @@ describe('#blockchain', () => {
it('should get info about the blockchain', async () => {
const result = await bchjs.Blockchain.getBlockchainInfo()
console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
// console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'blocks')
})
})
})
@@ -1,73 +0,0 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
TODO
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ restURL: process.env.RESTURL })
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
/*
Testing sentRawTransaction isn't really possible with an integration test,
as the endpoint really needs an e2e test to be properly tested. The tests
below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently.
*/
describe('sendRawTransaction', () => {
it('should send a single transaction hex', async () => {
try {
const hex =
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'bad-txns-inputs-missingorspent')
// assert.include(err.error, 'Missing inputs')
}
})
it('should send an array of tx hexes', async () => {
try {
const hexes = [
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'bad-txns-inputs-missingorspent')
// assert.include(err.error, 'Missing inputs')
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -1,40 +0,0 @@
/*
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))
}
@@ -1,71 +0,0 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
TODO
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
})
/*
Testing sentRawTransaction isn't really possible with an integration test,
as the endpoint really needs an e2e test to be properly tested. The tests
below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently.
*/
describe('sendRawTransaction', () => {
it('should send a single transaction hex', async () => {
try {
const hex =
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'Missing inputs')
}
})
it('should send an array of tx hexes', async () => {
try {
const hexes = [
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'Missing inputs')
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
-301
View File
@@ -1,301 +0,0 @@
/*
Integration tests for the bchjs covering SLP tokens.
These tests are specific to the ABC chain.
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../../../src/bch-js')
let bchjs
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 1
}
describe('#SLP', () => {
// before(() => {
// console.log(`bchjs.SLP.restURL: ${bchjs.SLP.restURL}`)
// console.log(`bchjs.SLP.apiToken: ${bchjs.SLP.apiToken}`)
// })
beforeEach(async () => {
// Introduce a delay so that the BVT doesn't trip the rate limits.
if (process.env.IS_USING_FREE_TIER) await sleep(3000)
bchjs = new BCHJS()
})
if (process.env.TESTSLP) {
describe('#util', () => {
describe('#decodeOpReturn', () => {
it('should decode a NFT Child transaction', async () => {
const txid =
'eeddccc4d716f04157ea132ac93a48040fea34a6b57f3d8f0cccb7d1a731ab2b'
const data = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data, 'tokenType')
assert.property(data, 'txType')
assert.property(data, 'ticker')
assert.property(data, 'name')
assert.property(data, 'tokenId')
assert.property(data, 'documentUri')
assert.property(data, 'documentHash')
assert.property(data, 'decimals')
assert.property(data, 'mintBatonVout')
assert.property(data, 'qty')
assert.equal(data.tokenType, 65)
assert.equal(data.mintBatonVout, 0)
assert.equal(data.qty, '1')
})
})
describe('#tokenUtxoDetails', () => {
it('should handle a range of UTXO types', async () => {
const utxos = [
// Malformed SLP tx
{
note: 'Malformed SLP tx',
tx_hash:
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
tx_pos: 1,
value: 546
},
// Normal TX (non-SLP)
{
note: 'Normal TX (non-SLP)',
tx_hash:
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
tx_pos: 0,
value: 400000
},
// Valid PSF SLP tx
{
note: 'Valid PSF SLP tx',
tx_hash:
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
tx_pos: 1,
value: 546
},
// Valid SLP token not in whitelist
{
note: 'Valid SLP token not in whitelist',
tx_hash:
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1,
value: 546
},
// Token send on BCHN network.
{
note: 'Token send on BCHN network',
tx_hash:
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
tx_pos: 1,
value: 546
},
// Token send on ABC network.
{
note: 'Token send on ABC network',
tx_hash:
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d',
tx_pos: 1,
value: 546
},
// Known invalid SLP token send of PSF tokens.
{
note: 'Known invalid SLP token send of PSF tokens',
tx_hash:
'2bf691ad3679d928fef880b8a45b93b233f8fa0d0a92cf792313dbe77b1deb74',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetails(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
// Malformed SLP tx
assert.equal(data[0].tx_hash, utxos[0].tx_hash)
assert.equal(data[0].isValid, false)
// Normal TX (non-SLP)
assert.equal(data[1].tx_hash, utxos[1].tx_hash)
assert.equal(data[1].isValid, false)
// Valid PSF SLP tx
assert.equal(data[2].tx_hash, utxos[2].tx_hash)
assert.equal(data[2].isValid, true)
// Valid SLP token not in whitelist
assert.equal(data[3].tx_hash, utxos[3].tx_hash)
assert.equal(data[3].isValid, true)
// Token send on BCHN network
assert.equal(data[4].tx_hash, utxos[4].tx_hash)
assert.equal(data[4].isValid, true)
// Token send on ABC network
assert.equal(data[5].tx_hash, utxos[5].tx_hash)
assert.equal(data[5].isValid, null)
// Known invalid SLP token send of PSF tokens
assert.equal(data[6].tx_hash, utxos[6].tx_hash)
assert.equal(data[6].isValid, false)
})
})
describe('#validateTxid3', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, null)
})
it('should validate a known valid TXID', async () => {
const txid =
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, true)
})
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Unprocessed SLP TX
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
]
const result = await bchjs.SLP.Utils.validateTxid3(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, null)
// Note: This should change from null to true once SLPDB finishes indexing.
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, null)
})
})
describe('#validateTxid', () => {
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Token send on BCHN network.
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
// Token send on ABC network.
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
]
const result = await bchjs.SLP.Utils.validateTxid(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, true)
assert.equal(result[5].txid, txids[5])
assert.equal(result[5].valid, null)
})
})
})
describe('#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
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -1,28 +0,0 @@
/*
Integration tests for the transaction.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#Transaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#get', () => {
it('should get a tx details for a non-SLP TX with an OP_RETURN', async () => {
const txid =
'01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
const result = await bchjs.Transaction.get(txid)
// console.log('result: ', result)
assert.property(result.txData, 'txid')
assert.property(result.txData, 'vin')
assert.property(result.txData, 'vout')
assert.equal(result.txData.isValidSlp, false)
})
})
})
-103
View File
@@ -1,103 +0,0 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe('#util', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#validateAddress', () => {
it('should return false for testnet addr on mainnet', async () => {
const address = 'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['isvalid'])
assert.equal(result.isvalid, false)
})
it('should return false for bad address', async () => {
const address = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peu'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['isvalid'])
assert.equal(result.isvalid, false)
})
it('should validate valid address', async () => {
const address = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
'isvalid',
'address',
'scriptPubKey',
// "ismine",
// "iswatchonly",
'isscript'
])
assert.equal(result.isvalid, true)
})
it('should validate an array of addresses', async () => {
const address = [
'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z',
'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
]
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
'isvalid',
'address',
'scriptPubKey',
// "ismine",
// "iswatchonly",
'isscript'
])
})
it('should throw error on array size rate limit', async () => {
try {
const dataMock =
'bitcoincash:qp4k8fjtgunhdr7yq30ha4peuwupzan2vcnwrmpy0z'
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.Util.validateAddress(data)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -1,260 +0,0 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
TODO
- getMempoolEntry() only works on TXs in the mempool, so it needs to be part
of an e2e test to be properly tested.
*/
const chai = require('chai')
const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: '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/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe('#blockchain', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#getBestBlockHash', () => {
it('should GET best block hash', async () => {
const result = await bchjs.Blockchain.getBestBlockHash()
// console.log(`result: ${util.inspect(result)}`)
assert.isString(result)
assert.equal(result.length, 64, 'Specific hash length')
})
})
describe('#getBlockHeader', () => {
it('should GET block header for a single hash', async () => {
const hash =
'000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf'
const result = await bchjs.Blockchain.getBlockHeader(hash)
assert.hasAllKeys(result, [
'hash',
'confirmations',
'height',
'version',
'versionHex',
'merkleroot',
'time',
'mediantime',
'nonce',
'bits',
'difficulty',
'chainwork',
'previousblockhash',
'nextblockhash',
'nTx'
])
})
it('should GET block headers for an array of hashes', async () => {
const hash = [
'000000000000c57178ace90210289e6b5383134c5b5306e1cdd8395176e10aaf',
'00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2'
]
const result = await bchjs.Blockchain.getBlockHeader(hash)
assert.isArray(result)
assert.hasAllKeys(result[0], [
'hash',
'confirmations',
'height',
'version',
'versionHex',
'merkleroot',
'time',
'mediantime',
'nonce',
'bits',
'difficulty',
'chainwork',
'previousblockhash',
'nextblockhash',
'nTx'
])
})
it('should throw error on array size rate limit', async () => {
try {
const data = []
for (let i = 0; i < 25; i++) {
data.push(
'00000000000b7db4cbae48d852fcbef32f728014582094ad613fe12af6600ff2'
)
}
await bchjs.Blockchain.getBlockHeader(data)
// console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
describe('#getMempoolEntry', () => {
/*
// To run this test, the txid must be unconfirmed.
const txid =
"defea04c38ee00cf73ad402984714ed22dc0dd99b2ae5cb50d791d94343ba79b"
it(`should GET single mempool entry`, async () => {
const result = await bchjs.Blockchain.getMempoolEntry(txid)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
"size",
"fee",
"modifiedfee",
"time",
"height",
"startingpriority",
"currentpriority",
"descendantcount",
"descendantsize",
"descendantfees",
"ancestorcount",
"ancestorsize",
"ancestorfees",
"depends"
])
})
it(`should get an array of mempool entries`, async () => {
const result = await bchjs.Blockchain.getMempoolEntry([txid, txid])
console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
"size",
"fee",
"modifiedfee",
"time",
"height",
"startingpriority",
"currentpriority",
"descendantcount",
"descendantsize",
"descendantfees",
"ancestorcount",
"ancestorsize",
"ancestorfees",
"depends"
])
})
*/
it('should throw an error if txid is not in mempool', async () => {
try {
const txid =
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
await bchjs.Blockchain.getMempoolEntry(txid)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Transaction not in mempool')
}
})
})
describe('#getTxOutProof', () => {
it('should get single tx out proof', async () => {
const txid =
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
const result = await bchjs.Blockchain.getTxOutProof(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result)
})
it('should get an array of tx out proofs', async () => {
const txid = [
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04',
'fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752'
]
const result = await bchjs.Blockchain.getTxOutProof(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
})
})
describe('#verifyTxOutProof', () => {
const mockTxOutProof =
'00000020ac86ce8f2bda235c0dc135d18f6a777c44b121e8f41db8a51ca65000000000000cd4de3c49337712f3a1092c47c3bf73ec2f9b1cfd289ee991ede0b6eab4df229d5b8c5dffff001d82ce005b0700000003ec55d9142eac2c1d2229e5af24898b2590c111b62e2787fa1998d3d713fd432fd557124ed758fa156a6cdc6d317d5575aec2b86dfb159c62ecb4743f28bef1cb52e7a32fbec31e367ec152fdb952f75ee75bd8575f97b394093dbb0e6c694ffc0135'
it('should verify a single proof', async () => {
const result = await bchjs.Blockchain.verifyTxOutProof(mockTxOutProof)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
assert.equal(
result[0],
'fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752'
)
})
it('should verify an array of proofs', async () => {
const proofs = [mockTxOutProof, mockTxOutProof]
const result = await bchjs.Blockchain.verifyTxOutProof(proofs)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
assert.equal(
result[0],
'fc4f696c0ebb3d0994b3975f57d85be75ef752b9fd52c17e361ec3be2fa3e752'
)
})
it('should throw error on array size rate limit', async () => {
try {
const data = []
for (let i = 0; i < 25; i++) data.push(mockTxOutProof)
const result = await bchjs.Blockchain.verifyTxOutProof(data)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -1,32 +0,0 @@
/*
Integration tests for bchjs control library.
*/
const chai = require('chai')
const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: '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(1500)
})
describe('#getNetworkInfo', () => {
it('should get info on the full node', async () => {
const result = await bchjs.Control.getNetworkInfo()
console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'version')
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -1,376 +0,0 @@
const chai = require('chai')
const assert = chai.assert
const sinon = require('sinon')
const util = require('util')
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: 'https://testnet3.fullstack.cash/v5/'
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
describe('#ElectrumX', () => {
let sandbox
beforeEach(async () => {
sandbox = sinon.createSandbox()
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
afterEach(() => sandbox.restore())
describe('#utxo', () => {
it('should GET utxos for a single address', async () => {
const addr = 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'utxos')
assert.isArray(result.utxos)
assert.property(result.utxos[0], 'height')
assert.property(result.utxos[0], 'tx_hash')
assert.property(result.utxos[0], 'tx_pos')
assert.property(result.utxos[0], 'value')
})
it('should POST utxo details for an array of addresses', async () => {
const addr = [
'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2',
'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
]
const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'utxos')
assert.isArray(result.utxos)
assert.property(result.utxos[0], 'utxos')
assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], 'address')
assert.property(result.utxos[0].utxos[0], 'height')
assert.property(result.utxos[0].utxos[0], 'tx_hash')
assert.property(result.utxos[0].utxos[0], 'tx_pos')
assert.property(result.utxos[0].utxos[0], 'value')
})
it('should throw error on array size rate limit', async () => {
try {
const addr = []
for (let i = 0; i < 25; i++) {
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
// const result = await bchjs.Electrumx.utxo(addr)
// console.log(`result: ${util.inspect(result)}`)
await bchjs.Electrumx.utxo(addr)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
describe('#balance', () => {
it('should GET balance for a single address', async () => {
const addr = 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'balance')
assert.property(result.balance, 'confirmed')
assert.property(result.balance, 'unconfirmed')
})
it('should POST request for balances for an array of addresses', async () => {
const addr = [
'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2',
'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
]
const result = await bchjs.Electrumx.balance(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'balances')
assert.isArray(result.balances)
assert.property(result.balances[0], 'address')
assert.property(result.balances[0], 'balance')
assert.property(result.balances[0].balance, 'confirmed')
assert.property(result.balances[0].balance, 'unconfirmed')
})
it('should throw error on array size rate limit', async () => {
try {
const addr = []
for (let i = 0; i < 25; i++) {
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
const result = await bchjs.Electrumx.balance(addr)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
describe('#transactions', () => {
it('should GET transaction history for a single address', async () => {
const addr = 'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'transactions')
assert.isArray(result.transactions)
assert.property(result.transactions[0], 'height')
assert.property(result.transactions[0], 'tx_hash')
})
it('should POST request for transaction history for an array of addresses', async () => {
const addr = [
'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2',
'bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2'
]
const result = await bchjs.Electrumx.transactions(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'transactions')
assert.isArray(result.transactions)
assert.property(result.transactions[0], 'address')
assert.property(result.transactions[0], 'transactions')
assert.isArray(result.transactions[0].transactions)
assert.property(result.transactions[0].transactions[0], 'height')
assert.property(result.transactions[0].transactions[0], 'tx_hash')
})
it('should throw error on array size rate limit', async () => {
try {
const addr = []
for (let i = 0; i < 25; i++) {
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
const result = await bchjs.Electrumx.transactions(addr)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
describe('#unconfirmed', () => {
// These tests won't work because unconfirmed transactions are transient in nature.
/*
it(`should GET unconfirmed UTXOs (mempool) for a single address`, async () => {
const addr = "bchtest:qp25k20dgcljrz4hkdz43partam3j5httyprjp23qd"
const result = await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "height")
assert.property(result.utxos[0], "tx_hash")
assert.property(result.utxos[0], "fee")
})
it(`should POST unconfirmed UTXO details for an array of addresses`, async () => {
const addr = [
"bchtest:qp25k20dgcljrz4hkdz43partam3j5httyprjp23qd",
"bchtest:qpkl3xylrjx4jup6m66e7zg7whlaucsxeudxeqawdj"
]
const result = await bchjs.Electrumx.unconfirmed(addr)
//console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, "success")
assert.equal(result.success, true)
assert.property(result, "utxos")
assert.isArray(result.utxos)
assert.property(result.utxos[0], "utxos")
assert.isArray(result.utxos[0].utxos)
assert.property(result.utxos[0], "address")
assert.property(result.utxos[0].utxos[0], "height")
assert.property(result.utxos[0].utxos[0], "tx_hash")
assert.property(result.utxos[0].utxos[0], "fee")
})
*/
it('should throw error on array size rate limit', async () => {
try {
const addr = []
for (let i = 0; i < 25; i++) {
addr.push('bchtest:qrvn2n228aa39xupcw9jw0d3fj8axxky656e4j62z2')
}
await bchjs.Electrumx.unconfirmed(addr)
// console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
describe('#blockHeader', () => {
it('should GET block headers for given height and count', async () => {
const height = 42
const count = 2
const result = await bchjs.Electrumx.blockHeader(height, count)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'headers')
assert.isArray(result.headers)
assert.equal(result.headers.length, 2)
})
it('should GET block headers for given height with default count = 1', async () => {
const height = 42
const result = await bchjs.Electrumx.blockHeader(height)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'headers')
assert.isArray(result.headers)
assert.equal(result.headers.length, 1)
})
})
describe('#txData', () => {
it('should GET details for a single transaction', async () => {
const txid =
'76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee'
const result = await bchjs.Electrumx.txData(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'details')
assert.isObject(result.details)
assert.property(result.details, 'blockhash')
assert.property(result.details, 'hash')
assert.property(result.details, 'hex')
assert.property(result.details, 'vin')
assert.property(result.details, 'vout')
assert.equal(result.details.hash, txid)
})
it('should POST details for an array of transactions', async () => {
const txids = [
'76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee',
'76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee'
]
const result = await bchjs.Electrumx.txData(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'success')
assert.equal(result.success, true)
assert.property(result, 'transactions')
assert.isArray(result.transactions)
assert.property(result.transactions[0], 'txid')
assert.property(result.transactions[0], 'details')
assert.property(result.transactions[0].details, 'blockhash')
assert.property(result.transactions[0].details, 'hash')
assert.property(result.transactions[0].details, 'hex')
assert.property(result.transactions[0].details, 'vin')
assert.property(result.transactions[0].details, 'vout')
})
it('should throw error on array size rate limit', async () => {
try {
const txids = []
for (let i = 0; i < 25; i++) {
txids.push(
'76d2ee0ebd8b978742f6478ff9a12f478c34e4cee0a2b919059101d961bd01ee'
)
}
await bchjs.Electrumx.txData(txids)
// console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
describe('#broadcast', () => {
it('should broadcast a single transaction', async () => {
const txHex =
'020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000'
try {
await bchjs.Electrumx.broadcast(txHex)
} catch (err) {
assert.property(err, 'success')
assert.equal(err.success, false)
assert.include(
err.error.error,
'the transaction was rejected by network rules'
)
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -1,296 +0,0 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
TODO
*/
const chai = require('chai')
const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: '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/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe('#rawtransaction', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#decodeRawTransaction', () => {
it('should decode tx for a single hex', async () => {
const hex =
'0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
const result = await bchjs.RawTransactions.decodeRawTransaction(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
'txid',
'hash',
'size',
'version',
'locktime',
'vin',
'vout'
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
it('should decode an array of tx hexes', async () => {
const hexes = [
'0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000',
'0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
]
const result = await bchjs.RawTransactions.decodeRawTransaction(hexes)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
'txid',
'hash',
'size',
'version',
'locktime',
'vin',
'vout'
])
assert.isArray(result[0].vin)
assert.isArray(result[0].vout)
})
it('should throw error on array size rate limit', async () => {
try {
const data = []
for (let i = 0; i < 25; i++) {
data.push(
'0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000'
)
}
const result = await bchjs.RawTransactions.decodeRawTransaction(data)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
describe('#getRawTransaction', () => {
it('should decode a single txid, with concise output', async () => {
const txid =
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
const verbose = false
const result = await bchjs.RawTransactions.getRawTransaction(
txid,
verbose
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isString(result)
})
it('should decode a single txid, with verbose output', async () => {
const txid =
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
const verbose = true
const result = await bchjs.RawTransactions.getRawTransaction(
txid,
verbose
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
'hex',
'txid',
'hash',
'size',
'version',
'locktime',
'vin',
'vout',
'blockhash',
'confirmations',
'time',
'blocktime'
])
assert.isArray(result.vin)
assert.isArray(result.vout)
})
it('should decode an array of txids, with a concise output', async () => {
const txid = [
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04',
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
]
const verbose = false
const result = await bchjs.RawTransactions.getRawTransaction(
txid,
verbose
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.isString(result[0])
})
it('should decode an array of txids, with a verbose output', async () => {
const txid = [
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04',
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
]
const verbose = true
const result = await bchjs.RawTransactions.getRawTransaction(
txid,
verbose
)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
'hex',
'txid',
'hash',
'size',
'version',
'locktime',
'vin',
'vout',
'blockhash',
'confirmations',
'time',
'blocktime'
])
assert.isArray(result[0].vin)
assert.isArray(result[0].vout)
})
it('should throw error on array size limit', async () => {
try {
const dataMock =
'1f121fb6a33f48cd426dde06aa20ce589dd97becabb835a8d33071cbf40c7d04'
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.RawTransactions.getRawTransaction(data)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
describe('#decodeScript', () => {
it('should decode script for a single hex', async () => {
const hex =
'4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16'
const result = await bchjs.RawTransactions.decodeScript(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['asm', 'type', 'p2sh'])
})
// CT 2/20/19 - Waiting for this PR to be merged complete the test:
// https://github.com/Bitcoin-com/rest.bitcoin.com/pull/312
/*
it("should decode an array of tx hexes", async () => {
const hexes = [
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16",
"4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16"
]
const result = await bchjs.RawTransactions.decodeScript(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
})
*/
})
/*
Testing sentRawTransaction isn't really possible with an integration test,
as the endpoint really needs an e2e test to be properly tested. The tests
below expect error messages returned from the server, but at least test
that the server is responding on those endpoints, and responds consistently.
*/
describe('sendRawTransaction', () => {
it('should send a single transaction hex', async () => {
try {
const hex =
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'Missing inputs')
}
})
it('should send an array of tx hexes', async () => {
try {
const hexes = [
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'Missing inputs')
}
})
it('should throw error on array size rate limit', async () => {
try {
const dataMock =
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.RawTransactions.sendRawTransaction(data)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
-172
View File
@@ -1,172 +0,0 @@
/*
Integration tests for the bchjs covering SLP tokens.
*/
const chai = require('chai')
const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: '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/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 1
}
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(1500)
})
describe('#util', () => {
it('should get information on the Oasis token', async () => {
const tokenId =
'a371e9934c7695d08a5eb7f31d3bceb4f3644860cc67520cda1e149423b9ec39'
const result = await bchjs.SLP.Utils.list(tokenId)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
'decimals',
'timestamp',
'timestamp_unix',
'versionType',
'documentUri',
'symbol',
'name',
'containsBaton',
'id',
'documentHash',
'initialTokenQty',
'blockCreated',
'blockLastActiveSend',
'blockLastActiveMint',
'txnsSinceGenesis',
'validAddress',
'totalMinted',
'totalBurned',
'circulatingSupply',
'mintingBatonStatus'
])
})
})
describe('#decodeOpReturn', () => {
it('should decode the OP_RETURN for a SEND txid', async () => {
const txid =
'ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1'
const result = await bchjs.SLP.Utils.decodeOpReturn(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['amounts', 'tokenType', 'tokenId', 'txType'])
})
})
describe('#balancesForAddress', () => {
it('should fetch all balances for address: slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9', async () => {
const balances = await bchjs.SLP.Utils.balancesForAddress(
'slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9'
)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.hasAllKeys(balances[0], [
'tokenId',
'balanceString',
'balance',
'decimalCount',
'slpAddress'
])
})
it('should fetch balances for multiple addresses', async () => {
const addresses = [
'slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9',
'slptest:qr5uy4h8ysyhwkp9s245scckdnc7teyjqc5ft2z43h'
]
const balances = await bchjs.SLP.Utils.balancesForAddress(addresses)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.isArray(balances[0])
assert.hasAllKeys(balances[0][0], [
'tokenId',
'balanceString',
'balance',
'decimalCount',
'slpAddress'
])
})
})
describe('#tokenUtxoDetails', () => {
it('should hydrate UTXOs', async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress(
'slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9'
)
const utxos = await bchjs.Electrumx.utxo([bchAddr])
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const utxoInfo = await bchjs.SLP.Utils.tokenUtxoDetails(
utxos.utxos[0].utxos
)
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
assert.isArray(utxoInfo)
// first UTXO should be a PSF test token.
assert.equal(utxoInfo[0].isValid, true)
})
})
describe('#hydrateUtxos', () => {
// This test will error out if the LOCAL_RESTURL settings is not set properly
// in bch-api.
it('should hydrate UTXOs', async () => {
const bchAddr = bchjs.SLP.Address.toCashAddress(
'slptest:qz0kc67pm4emjyr3gaaa2wstdaykg9m4yqwlzpj3w9'
)
const utxos = await bchjs.Electrumx.utxo([bchAddr])
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const utxoInfo = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
// console.log(`utxoInfo: ${JSON.stringify(utxoInfo, null, 2)}`)
assert.isArray(utxoInfo.slpUtxos[0].utxos)
// first UTXO should be a PSF test token.
assert.equal(utxoInfo.slpUtxos[0].utxos[0].isValid, true)
})
})
describe('#validateTxid2', () => {
it('should validate a token txid', async () => {
const txid =
'ad28116e0818339342dddfc5f58ca8a5379ceb9679b4e4cbd72f4de905415ec1'
const validated = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(validated)
assert.equal(validated[0].valid, true)
})
})
})
// Promise-based sleep function
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
@@ -1,9 +0,0 @@
#!/bin/bash
export RESTURL=https://free-test.fullstack.cash/v5/
export NETWORK=testnet
export IS_USING_FREE_TIER=true
cd test/integration/testnet/
mocha --timeout 30000 blockchain.js control.js electrumx.js rawtransaction.js slp.js util.js
#mocha --timeout 30000 blockchain.js
-109
View File
@@ -1,109 +0,0 @@
/*
Integration tests for the bchjs. Only covers calls made to
rest.bitcoin.com.
*/
const chai = require('chai')
const assert = chai.assert
const RESTURL = process.env.RESTURL
? process.env.RESTURL
: '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/v5/` })
const bchjs = new BCHJS({ restURL: RESTURL, apiToken: process.env.BCHJSTOKEN })
// Inspect utility used for debugging.
const util = require('util')
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
depth: 3
}
describe('#util', () => {
beforeEach(async () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#validateAddress', () => {
it('should return false for testnet addr on mainnet', async () => {
const address = 'bitcoincash:qp4k8fjtgunhdr7yq30ha4peu'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['isvalid'])
assert.equal(result.isvalid, false)
})
it('should return false for bad address', async () => {
const address = 'bchtest:qqqk4y6lsl5da64sg53xezmplyu5kmpyz2ysaa5y'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['isvalid'])
assert.equal(result.isvalid, false)
})
it('should validate valid address', async () => {
const address = 'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y'
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.hasAnyKeys(result, [
'isvalid',
'address',
'scriptPubKey',
// "ismine",
// "iswatchonly",
'isscript'
])
assert.equal(result.isvalid, true)
})
it('should validate an array of addresses', async () => {
const address = [
'bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y',
'bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh'
]
const result = await bchjs.Util.validateAddress(address)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.hasAnyKeys(result[0], [
'isvalid',
'address',
'scriptPubKey',
// "ismine",
// "iswatchonly",
'isscript'
])
})
it('should throw error on array size rate limit', async () => {
try {
const dataMock = 'bchtest:pq6k9969f6v6sg7a75jkru4n7wn9sknv5cztcp0dnh'
const data = []
for (let i = 0; i < 25; i++) data.push(dataMock)
const result = await bchjs.Util.validateAddress(data)
console.log(`result: ${util.inspect(result)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
assert.hasAnyKeys(err, ['error'])
assert.include(err.error, 'Array too large')
}
})
})
})
function sleep (ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
+3 -3
View File
@@ -2,9 +2,9 @@
Integration tests for bchjs control library.
*/
const chai = require('chai')
import chai from 'chai'
import BCHJS from '../../src/bch-js.js'
const assert = chai.assert
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
describe('#control', () => {
@@ -15,7 +15,7 @@ describe('#control', () => {
describe('#getNetworkInfo', () => {
it('should get info on the full node', async () => {
const result = await bchjs.Control.getNetworkInfo()
console.log(`result: ${JSON.stringify(result, null, 2)}`)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'version')
})
@@ -2,9 +2,13 @@
Integration tests for bchjs dsproof library.
*/
const assert = require('chai').assert
// Global npm libraries
import chai from 'chai'
// Local libraries
import BCHJS from '../../src/bch-js.js'
const { assert } = chai
const BCHJS = require('../../../../src/bch-js')
const bchjs = new BCHJS()
describe('#DSProof', () => {
+8 -9
View File
@@ -1,8 +1,8 @@
const chai = require('chai')
const assert = chai.assert
const sinon = require('sinon')
import chai from 'chai'
import sinon from 'sinon'
const BCHJS = require('../../src/bch-js')
import BCHJS from '../../src/bch-js.js'
const assert = chai.assert
const bchjs = new BCHJS()
describe('#ElectrumX', () => {
@@ -359,12 +359,8 @@ 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.error,
err.error,
'the transaction was rejected by network rules'
)
}
@@ -373,6 +369,9 @@ describe('#ElectrumX', () => {
describe('#sortAllTxs', () => {
it('should GET transaction history for a single address', async () => {
// Add delay for this endpoint.
await sleep(6000)
const addr = 'bitcoincash:qpdh9s677ya8tnx7zdhfrn8qfyvy22wj4qa7nwqa5v'
const txs = await bchjs.Electrumx.transactions(addr)
+12 -3
View File
@@ -1,7 +1,7 @@
const assert = require('chai').assert
// const assert = require('chai').assert
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
// const BCHJS = require('../../src/bch-js')
// const bchjs = new BCHJS()
describe('#Encryption', () => {
beforeEach(async () => {
@@ -9,7 +9,12 @@ describe('#Encryption', () => {
})
describe('#getPubKey', () => {
// Commenting out these tests since they are failing in BVT due to 429 errors
/*
it('should get a public key', async () => {
// Add delay for this endpoint.
await sleep(8000)
const addr = 'bitcoincash:qpf8jv9hmqcda0502gjp7nm3g24y5h5s4unutghsxq'
const result = await bchjs.encryption.getPubKey(addr)
@@ -21,6 +26,9 @@ describe('#Encryption', () => {
})
it('should report when public key can not be found', async () => {
// Add delay for this endpoint.
await sleep(8000)
const addr = 'bitcoincash:qrgqqkky28jdkv3w0ctrah0mz3jcsnsklc34gtukrh'
const result = await bchjs.encryption.getPubKey(addr)
@@ -31,6 +39,7 @@ describe('#Encryption', () => {
assert.property(result, 'publicKey')
assert.equal(result.publicKey, 'not found')
})
*/
})
})
+6 -32
View File
@@ -1,5 +1,6 @@
const assert = require('chai').assert
const BCHJS = require('../../src/bch-js')
import chai from 'chai'
import BCHJS from '../../src/bch-js.js'
const { assert } = chai
const bchjs = new BCHJS()
describe('#price', () => {
@@ -7,32 +8,6 @@ describe('#price', () => {
if (process.env.IS_USING_FREE_TIER) await sleep(1500)
})
describe('#current', () => {
describe('#single currency', () => {
it('should get current price for single currency', async () => {
const result = await bchjs.Price.current('usd')
assert.notEqual(0, result)
})
})
})
describe('#getUsd', () => {
it('should get the USD price of BCH', async () => {
const result = await bchjs.Price.getUsd()
// console.log(result)
assert.isNumber(result)
})
})
describe('#getBchaUsd', () => {
it('should get the USD price of BCHA', async () => {
const result = await bchjs.Price.getBchaUsd()
console.log(result)
assert.isNumber(result)
})
})
describe('#getBchUsd', () => {
it('should get the USD price of BCH', async () => {
const result = await bchjs.Price.getBchUsd()
@@ -42,13 +17,12 @@ describe('#price', () => {
})
})
describe('#rates', () => {
describe('#getPsffppPrice', () => {
it('should get the price of BCH in several currencies', async () => {
const result = await bchjs.Price.rates()
const result = await bchjs.Price.getPsffppPrice()
// console.log(result)
assert.property(result, 'USD')
assert.property(result, 'CAD')
assert.isNumber(result)
})
})
})
@@ -1,10 +1,14 @@
/*
Integration tests for the psf-slp-indexer.js library
Integration tests for the psf-slp-indexer.js library, specific to the BCH
blockchain.
*/
const assert = require('chai').assert
// Global npm libraries
import chai from 'chai'
const BCHJS = require('../../../../src/bch-js')
// Local libraries
import BCHJS from '../../src/bch-js.js'
const { assert } = chai
let bchjs
describe('#psf-slp-indexer', () => {
@@ -92,9 +96,7 @@ describe('#psf-slp-indexer', () => {
})
// FlexUSD transactions
// Currently FlexUSD UTXOs are reported as invalid SLP UTXOs, which means
// the wallet will burn them. There is a TODO in the code. This test will
// need to be changed when it is done.
// FlexUSD is blacklisted by the SLP indexer.
it('should mark blacklisted token as null', async () => {
const txid =
'302113c11b90edc5f36c073d2f8a75e1e0eaf59b56235491a843d3819cd6a85f'
@@ -105,7 +107,7 @@ describe('#psf-slp-indexer', () => {
assert.property(result.txData, 'vin')
assert.property(result.txData, 'vout')
assert.property(result.txData, 'isValidSlp')
assert.equal(result.txData.isValidSlp, null)
assert.equal(result.txData.isValidSlp, false)
})
it('should throw error for non-existent txid', async () => {
@@ -124,6 +126,57 @@ describe('#psf-slp-indexer', () => {
}
})
})
describe('#getTokenData', () => {
it('should get token data', async () => {
const tokenId =
'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b'
// bchjs.PsfSlpIndexer.restURL = 'http://localhost:3000/v5/'
const result = await bchjs.PsfSlpIndexer.getTokenData(tokenId)
// console.log('result: ', result)
assert.property(result, 'genesisData')
assert.property(result, 'immutableData')
assert.property(result, 'mutableData')
assert.isObject(result.genesisData)
assert.isString(result.immutableData)
assert.isString(result.mutableData)
})
it('should get token data with a transaction history', async () => {
const tokenId = '43eddfb11c9941edffb8c8815574bb0a43969a7b1de39ad14cd043eaa24fd38d'
const result = await bchjs.PsfSlpIndexer.getTokenData(tokenId, true)
// console.log('result: ', result)
assert.isArray(result.genesisData.txs)
})
})
// This test is commented out because it can not succeed in the BVT without
// tripping rate limits.
// describe('#getTokenData2', () => {
// it('should get token data', async () => {
// const tokenId =
// 'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b'
//
// // bchjs.PsfSlpIndexer.restURL = 'http://localhost:3000/v5/'
// const result = await bchjs.PsfSlpIndexer.getTokenData2(tokenId)
// // console.log('result: ', result)
//
// assert.property(result, 'tokenStats')
// assert.property(result, 'mutableData')
// assert.property(result, 'immutableData')
// assert.property(result, 'tokenIcon')
// assert.property(result, 'fullSizedUrl')
// assert.property(result, 'optimizedTokenIcon')
// assert.property(result, 'optimizedFullSizedUrl')
// assert.property(result, 'iconRepoCompatible')
// assert.property(result, 'ps002Compatible')
// })
// })
})
// Promise-based sleep function
+42 -6
View File
@@ -4,13 +4,13 @@
TODO
*/
const chai = require('chai')
const assert = chai.assert
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
import chai from 'chai'
import BCHJS from '../../src/bch-js.js'
// Inspect utility used for debugging.
const util = require('util')
import util from 'util'
const assert = chai.assert
const bchjs = new BCHJS()
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
@@ -204,7 +204,9 @@ describe('#rawtransaction', () => {
const result = await bchjs.RawTransactions.decodeScript(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.hasAllKeys(result, ['asm', 'type', 'p2sh'])
assert.property(result, 'asm')
assert.property(result, 'type')
assert.property(result, 'p2sh')
})
// CT 2/20/19 - Waiting for this PR to be merged complete the test:
@@ -229,6 +231,40 @@ describe('#rawtransaction', () => {
that the server is responding on those endpoints, and responds consistently.
*/
describe('#sendRawTransaction', () => {
it('should send a single transaction hex', async () => {
try {
const hex =
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
await bchjs.RawTransactions.sendRawTransaction(hex)
// console.log(`result ${JSON.stringify(result, null, 2)}`)
assert.equal(true, false, 'Unexpected result!')
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'Missing inputs')
}
})
it('should send an array of tx hexes', async () => {
try {
const hexes = [
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000',
'01000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000006a4730440220540986d1c58d6e76f8f05501c520c38ce55393d0ed7ed3c3a82c69af04221232022058ea43ed6c05fec0eccce749a63332ed4525460105346f11108b9c26df93cd72012103083dfc5a0254613941ddc91af39ff90cd711cdcde03a87b144b883b524660c39ffffffff01807c814a000000001976a914d7e7c4e0b70eaa67ceff9d2823d1bbb9f6df9a5188ac00000000'
]
const result = await bchjs.RawTransactions.sendRawTransaction(hexes)
console.log(`result ${JSON.stringify(result, null, 2)}`)
} catch (err) {
// console.log(`err: ${util.inspect(err)}`)
assert.hasAllKeys(err, ['error'])
assert.include(err.error, 'Missing inputs')
}
})
it('should throw error on array size rate limit', async () => {
try {
const dataMock =
+147 -475
View File
@@ -2,14 +2,14 @@
Integration tests for the bchjs covering SLP tokens.
*/
const chai = require('chai')
const assert = chai.assert
import chai from 'chai'
const BCHJS = require('../../src/bch-js')
let bchjs
import BCHJS from '../../src/bch-js.js'
// Inspect utility used for debugging.
const util = require('util')
import util from 'util'
const assert = chai.assert
let bchjs
util.inspect.defaultOptions = {
showHidden: true,
colors: true,
@@ -497,476 +497,6 @@ describe('#SLP', () => {
})
})
describe('#tokenUtxoDetailsWL', () => {
it('should return details for a simple SEND SLP token utxo', async () => {
const utxos = [
{
height: 660554,
tx_hash:
'89b3f0c84efe8b01b24e2d7ac08636de5781f31dbb84478e3de868ca0a7ed93a',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.property(data[0], 'txid')
assert.property(data[0], 'vout')
assert.property(data[0], 'height')
assert.property(data[0], 'utxoType')
assert.property(data[0], 'tokenId')
assert.property(data[0], 'tokenTicker')
assert.property(data[0], 'tokenName')
assert.property(data[0], 'tokenDocumentUrl')
assert.property(data[0], 'tokenDocumentHash')
assert.property(data[0], 'decimals')
assert.property(data[0], 'tokenQty')
assert.property(data[0], 'isValid')
assert.equal(data[0].isValid, true)
})
it('should return false for BCH-only UTXOs', async () => {
const utxos = [
{
txid:
'a937f792c7c9eb23b4f344ce5c233d1ac0909217d0a504d71e6b1e4efb864a3b',
vout: 0,
amount: 0.00001,
satoshis: 1000,
confirmations: 0,
ts: 1578424704
},
{
txid:
'53fd141c2e999e080a5860887441a2c45e9cbe262027e2bd2ac998fc76e43c44',
vout: 0,
amount: 0.00001,
satoshis: 1000,
confirmations: 0,
ts: 1578424634
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data)
assert.equal(data[0].isValid, false)
assert.equal(data[1].isValid, false)
})
it('should handle a dust attack', async () => {
// it("#dustattack", async () => {
const utxos = [
{
height: 655965,
tx_hash:
'a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e',
tx_pos: 151,
value: 547,
satoshis: 547,
txid:
'a675af87dcd8d39be782737aa52e0076b52eb2f5ce355ffcb5567a64dd96b77e',
vout: 151,
address: 'bitcoincash:qq4dw3sm8qvglspy6w2qg0u2ugsy9zcfcqrpeflwww',
hdIndex: 11
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.equal(data[0].isValid, false)
})
it('should handle null SLPDB validations', async () => {
const utxos = [
{
height: 665577,
tx_hash:
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419',
tx_pos: 1,
value: 546
},
{
height: 665577,
tx_hash:
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
tx_pos: 1,
value: 546
}
]
const data = await bchjs.SLP.Utils.tokenUtxoDetailsWL(utxos)
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data)
// assert.equal(data[0].isValid, null)
})
})
describe('#balancesForAddress', () => {
it('should fetch all balances for address: simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9', async () => {
const balances = await bchjs.SLP.Utils.balancesForAddress(
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9'
)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.hasAllKeys(balances[0], [
'tokenId',
'balanceString',
'balance',
'decimalCount',
'slpAddress'
])
})
it('should fetch balances for multiple addresses', async () => {
const addresses = [
'simpleledger:qzv3zz2trz0xgp6a96lu4m6vp2nkwag0kvyucjzqt9',
'simpleledger:qqss4zp80hn6szsa4jg2s9fupe7g5tcg5ucdyl3r57'
]
const balances = await bchjs.SLP.Utils.balancesForAddress(addresses)
// console.log(`balances: ${JSON.stringify(balances, null, 2)}`)
assert.isArray(balances)
assert.isArray(balances[0])
assert.hasAllKeys(balances[0][0], [
'tokenId',
'balanceString',
'balance',
'decimalCount',
'slpAddress'
])
})
})
describe('#hydrateUtxos-', () => {
it('should hydrate UTXOs', async () => {
const utxos = [
{
utxos: [
{
txid:
'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
vout: 3,
value: '6816',
height: 606848,
confirmations: 13,
satoshis: 6816
},
{
txid:
'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56',
vout: 2,
value: '546',
height: 606848,
confirmations: 13,
satoshis: 546
}
]
}
]
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 1)
assert.equal(result.slpUtxos[0].utxos.length, 2)
// Test the non-slp UTXO.
assert.property(result.slpUtxos[0].utxos[0], 'txid')
assert.property(result.slpUtxos[0].utxos[0], 'vout')
assert.property(result.slpUtxos[0].utxos[0], 'value')
assert.property(result.slpUtxos[0].utxos[0], 'height')
assert.property(result.slpUtxos[0].utxos[0], 'confirmations')
assert.property(result.slpUtxos[0].utxos[0], 'satoshis')
assert.property(result.slpUtxos[0].utxos[0], 'isValid')
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
// Test the slp UTXO.
assert.property(result.slpUtxos[0].utxos[1], 'txid')
assert.property(result.slpUtxos[0].utxos[1], 'vout')
assert.property(result.slpUtxos[0].utxos[1], 'value')
assert.property(result.slpUtxos[0].utxos[1], 'height')
assert.property(result.slpUtxos[0].utxos[1], 'confirmations')
assert.property(result.slpUtxos[0].utxos[1], 'satoshis')
assert.property(result.slpUtxos[0].utxos[1], 'isValid')
assert.equal(result.slpUtxos[0].utxos[1].isValid, true)
assert.property(result.slpUtxos[0].utxos[1], 'transactionType')
assert.property(result.slpUtxos[0].utxos[1], 'tokenId')
assert.property(result.slpUtxos[0].utxos[1], 'tokenTicker')
assert.property(result.slpUtxos[0].utxos[1], 'tokenName')
assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentUrl')
assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentHash')
assert.property(result.slpUtxos[0].utxos[1], 'decimals')
assert.property(result.slpUtxos[0].utxos[1], 'tokenType')
assert.property(result.slpUtxos[0].utxos[1], 'tokenQty')
})
it('should process data directly from Electrumx', async () => {
const addrs = [
'bitcoincash:qqnt53cfw990u6y38xgezm0lfa8hknw0wueezcpagp',
'bitcoincash:qrh3qgtax6adegzc7zxm6m4sgrv9wq28yqnfn33ce5',
'bitcoincash:qqcaee5w4ws77n8s2gzy6fwtma6uxd7ctq8553e495'
]
const utxos = await bchjs.Electrumx.utxo(addrs)
// console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`)
const result = await bchjs.SLP.Utils.hydrateUtxos(utxos.utxos)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
// Test the general structure of the output.
assert.isArray(result.slpUtxos)
assert.equal(result.slpUtxos.length, 3)
assert.equal(result.slpUtxos[0].utxos.length, 1)
assert.equal(result.slpUtxos[1].utxos.length, 1)
assert.equal(result.slpUtxos[2].utxos.length, 2)
try {
// Test the expected values.
assert.equal(result.slpUtxos[0].utxos[0].isValid, false)
assert.equal(result.slpUtxos[1].utxos[0].isValid, true)
assert.equal(result.slpUtxos[1].utxos[0].tokenTicker, 'TROUT')
assert.equal(result.slpUtxos[2].utxos[0].isValid, false)
assert.equal(result.slpUtxos[2].utxos[1].isValid, true)
assert.equal(result.slpUtxos[2].utxos[1].tokenTicker, 'VALENTINE')
} catch (err) {
console.error(
'The hydrateUtxos call may hitting rate limits, or SLPDB may be having issues if "isValid" results are "null"'
)
console.log('Error: ', err)
}
})
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.hydrateUtxos([{ utxos: utxos }])
// console.log(`data: ${JSON.stringify(data, null, 2)}`)
assert.isArray(data.slpUtxos)
assert.equal(data.slpUtxos[0].isValid, null)
})
})
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 () => {
const txid =
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419'
const result = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txid)
// assert.equal(result[0].valid, null)
})
it('should handle a null response from SLPDB', async () => {
const txid = [
'4b89405c54d1c0bde8aa476a47561a42a6e7a5e927daa2ec69d428810eae3419',
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
]
const result = await bchjs.SLP.Utils.validateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txid[0])
// assert.equal(result[0].valid, null)
})
})
describe('#validateTxid2', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'txid')
assert.equal(result.txid, txid)
assert.property(result, 'isValid')
assert.equal(result.isValid, false)
})
it('should validate a known valid TXID', async () => {
const txid =
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
const result = await bchjs.SLP.Utils.validateTxid2(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'txid')
assert.equal(result.txid, txid)
assert.property(result, 'isValid')
assert.equal(result.isValid, true)
})
})
describe('#getWhitelist', () => {
it('should get the whitelist', async () => {
const result = await bchjs.SLP.Utils.getWhitelist()
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'name')
assert.property(result[1], 'tokenId')
})
})
describe('#getStatus', () => {
it('should return the current block height of the SLPDB indexer', async () => {
const result = await bchjs.SLP.Utils.getStatus()
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.property(result, 'bchBlockHeight')
assert.property(result, 'slpProcessedBlockHeight')
})
})
describe('#waterfallValidateTxid', () => {
it('should validate known good txid not in whitelist', async () => {
const txid =
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
const result = await bchjs.SLP.Utils.waterfallValidateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, true)
})
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.waterfallValidateTxid(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result, false)
})
})
describe('#tokentype1', () => {
describe('#getHexOpReturn', () => {
it('should return OP_RETURN object ', async () => {
@@ -994,6 +524,148 @@ describe('#SLP', () => {
})
})
})
describe('#validateTxid3', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, null)
})
it('should validate a known valid TXID', async () => {
const txid =
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd'
const result = await bchjs.SLP.Utils.validateTxid3(txid)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.property(result[0], 'txid')
assert.equal(result[0].txid, txid)
assert.property(result[0], 'valid')
assert.equal(result[0].valid, true)
})
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Unprocessed SLP TX
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019'
]
const result = await bchjs.SLP.Utils.validateTxid3(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, null)
// Note: This should change from null to true once SLPDB finishes indexing.
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, null)
})
})
describe('#validateTxid', () => {
it('should handle a mix of valid, invalid, and non-SLP txs', async () => {
const txids = [
// Malformed SLP tx
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a',
// Normal TX (non-SLP)
'01cdaec2f8b311fc2d6ecc930247bd45fa696dc204ab684596e281fe1b06c1f0',
// Valid PSF SLP tx
'daf4d8b8045e7a90b7af81bfe2370178f687da0e545511bce1c9ae539eba5ffd',
// Valid SLP token not in whitelist
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488',
// Token send on BCHN network.
'402c663379d9699b6e2dd38737061e5888c5a49fca77c97ab98e79e08959e019',
// Token send on ABC network.
'336bfe2168aac4c3303508a9e8548a0d33797a83b85b76a12d845c8d6674f79d'
]
const result = await bchjs.SLP.Utils.validateTxid(txids)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isArray(result)
assert.equal(result[0].txid, txids[0])
assert.equal(result[0].valid, null)
assert.equal(result[1].txid, txids[1])
assert.equal(result[1].valid, null)
assert.equal(result[2].txid, txids[2])
assert.equal(result[2].valid, true)
// True in validateTxid() but null in validateTxid3()
assert.equal(result[3].txid, txids[3])
assert.equal(result[3].valid, true)
assert.equal(result[4].txid, txids[4])
assert.equal(result[4].valid, true)
assert.equal(result[5].txid, txids[5])
assert.equal(result[5].valid, null)
})
})
describe('#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')
})
})
})
}
})
})
+27 -2
View File
@@ -2,8 +2,9 @@
Integration tests for the transaction.js library.
*/
const assert = require('chai').assert
const BCHJS = require('../../src/bch-js')
import chai from 'chai'
import BCHJS from '../../src/bch-js.js'
const { assert } = chai
const bchjs = new BCHJS()
describe('#Transaction', () => {
@@ -11,6 +12,30 @@ describe('#Transaction', () => {
if (process.env.IS_USING_FREE_TIER) await bchjs.Util.sleep(1000)
})
describe('#get', () => {
it('should get a tx details for a non-SLP TX with an OP_RETURN', async () => {
const txid =
'01517ff1587fa5ffe6f5eb91c99cf3f2d22330cd7ee847e928ce90ca95bf781b'
const result = await bchjs.Transaction.get(txid)
// console.log('result: ', result)
assert.property(result.txData, 'txid')
assert.property(result.txData, 'vin')
assert.property(result.txData, 'vout')
assert.equal(result.txData.isValidSlp, false)
})
it('should handle a coinbase transaction', async () => {
const txid = 'cca1d2dd3a533d2f501448dec03face2cb2814afd59a533a611e9a2909f2302b'
const details = await bchjs.Transaction.get(txid)
// console.log(`details: ${JSON.stringify(details, null, 2)}`)
assert.property(details.txData, 'txid')
})
})
if (process.env.TESTSLP) {
describe('#getOld', () => {
it('should get details about a non-SLP transaction', async () => {
@@ -2,9 +2,10 @@
Integration tests for the utxo.js library.
*/
const assert = require('chai').assert
import chai from 'chai'
const BCHJS = require('../../../../src/bch-js')
import BCHJS from '../../src/bch-js.js'
const { assert } = chai
const bchjs = new BCHJS()
// const bchjs = new BCHJS({ restURL: 'http://192.168.2.129:3000/v5/' })
@@ -79,14 +80,13 @@ describe('#UTXO', () => {
assert.property(result, 'nullUtxos')
assert.isAbove(result.bchUtxos.length, 0)
assert.isAbove(result.infoUtxos.length, 0)
assert.isAbove(result.slpUtxos.type1.tokens.length, 0)
assert.equal(result.slpUtxos.type1.mintBatons.length, 0)
})
// TODO: NFTs are currently not identified as different than normal BCH UTXOs.
// The psf-slp-indexer needs to be updated to fix this issue.
it('should handle minting batons', async () => {
const addr = 'simpleledger:qrm0c67wwqh0w7wjxua2gdt2xggnm90xwsr5k22euj'
it('should handle Type1 minting batons', async () => {
const addr = 'simpleledger:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpv4awvv7m7'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
@@ -105,13 +105,82 @@ describe('#UTXO', () => {
assert.equal(result.slpUtxos.type1.tokens.length, 0)
})
it('should handle Group NFTs', async () => {
const addr = 'bitcoincash:qrnghwrfgccf3s5e9wnglzxegcnhje9rkcwv2eka33'
it('should filter Group tokens and mint batons', async () => {
const addr = 'bitcoincash:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpvex9he79q'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.nullUtxos.length, 0)
assert.isAbove(result.slpUtxos.group.tokens.length, 0)
assert.isAbove(result.slpUtxos.group.mintBatons.length, 0)
})
it('should filter NFTs', async () => {
const addr = 'bitcoincash:qrp4mlmsrtwlvjn4seuchvtmus06tuqmpvex9he79q'
const result = await bchjs.Utxo.get(addr)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(result.slpUtxos.nft.tokens.length, 0)
})
})
describe('#isValid', () => {
it('should return true for valid UTXO with fullnode properties', async () => {
const utxo = {
txid: '260d4fb4006a330660c805327c840d569d7547b7e3e9659fb423b3a041c2e254',
vout: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result)
assert.equal(result, true)
})
it('should return true for valid UTXO with fulcrum properties', async () => {
const utxo = {
tx_hash: '260d4fb4006a330660c805327c840d569d7547b7e3e9659fb423b3a041c2e254',
tx_pos: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result)
assert.equal(result, true)
})
it('should return true for valid UTXO with fullnode properties', async () => {
const utxo = {
txid: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
vout: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result)
assert.equal(result, false)
})
it('should return true for valid UTXO with fulcrum properties', async () => {
const utxo = {
tx_hash: '17754221b29f189532d4fc2ae89fb467ad2dede30fdec4854eb2129b3ba90d7a',
tx_pos: 0
}
const result = await bchjs.Utxo.isValid(utxo)
// console.log('result: ', result
assert.equal(result, false)
})
it('should process output of Utxo.get()', async () => {
const utxo = await bchjs.Utxo.get('bitcoincash:qqgahqa5zkknmhmvsc98jndpwn3r77gqgc02x03jce')
// console.log(`utxo: ${JSON.stringify(utxo, null, 2)}`)
const result = await bchjs.Utxo.isValid(utxo.bchUtxos[0])
assert.equal(result, true)
})
})
})
+47 -4
View File
@@ -1,12 +1,15 @@
// Public npm libraries.
const assert = require('assert')
const Bitcoin = require('@psf/bitcoincashjs-lib')
import assert from 'assert'
// import { assert } from 'chai'
import Bitcoin from '@psf/bitcoincashjs-lib'
// Mocks
const fixtures = require('./fixtures/address.json')
import { createRequire } from 'module'
// Unit under test (uut)
const BCHJS = require('../../src/bch-js')
import BCHJS from '../../src/bch-js.js'
const require = createRequire(import.meta.url)
const fixtures = require('./fixtures/address.json')
let bchjs
function flatten (arrays) {
@@ -935,4 +938,44 @@ describe('#address.js', () => {
})
})
})
describe('#toEcashAddress', () => {
it('should convert a BCH address to an eCash address', () => {
const cashAddr = 'bitcoincash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s7yw984vk'
const result = bchjs.Address.toEcashAddress(cashAddr)
// console.log('result: ', result)
assert.equal(result, 'ecash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s8f6wu02p')
})
it('should convert a BCH address to an eCash address without a prefix', () => {
const cashAddr = 'bitcoincash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s7yw984vk'
const result = bchjs.Address.toEcashAddress(cashAddr, false)
// console.log('result: ', result)
assert.equal(result, 'qr24z7q6s26d9wr078lr2pxsmxg22cyn9s8f6wu02p')
})
})
describe('#toEtokenAddress', () => {
it('should convert a BCH address to an eToken address', () => {
const cashAddr = 'bitcoincash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s7yw984vk'
const result = bchjs.Address.toEtokenAddress(cashAddr)
// console.log('result: ', result)
assert.equal(result, 'etoken:qr24z7q6s26d9wr078lr2pxsmxg22cyn9sfhnv2gwk')
})
it('should convert a BCH address to an eToken address without a prefix', () => {
const cashAddr = 'bitcoincash:qr24z7q6s26d9wr078lr2pxsmxg22cyn9s7yw984vk'
const result = bchjs.Address.toEtokenAddress(cashAddr, false)
// console.log('result: ', result)
assert.equal(result, 'qr24z7q6s26d9wr078lr2pxsmxg22cyn9sfhnv2gwk')
})
})
})
+5 -3
View File
@@ -1,11 +1,13 @@
// Public npm libraries
const assert = require('assert')
import assert from 'assert'
// Mocks
const fixtures = require('./fixtures/bitcoincash.json')
import { createRequire } from 'module'
// Unit under test (uut)
const BCHJS = require('../../src/bch-js')
import BCHJS from '../../src/bch-js.js'
const require = createRequire(import.meta.url)
const fixtures = require('./fixtures/bitcoincash.json')
// const bchjs = new BCHJS()
let bchjs
+23 -22
View File
@@ -1,11 +1,12 @@
const assert = require('assert')
const assert2 = require('chai').assert
const axios = require('axios')
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
const sinon = require('sinon')
import assert from 'assert'
import chai from 'chai'
import axios from 'axios'
import BCHJS from '../../src/bch-js.js'
import sinon from 'sinon'
const mockData = require('./fixtures/blockchain-mock')
import mockData from './fixtures/blockchain-mock.js'
const { assert: assert2 } = chai
const bchjs = new BCHJS()
describe('#Blockchain', () => {
describe('#getBestBlockHash', () => {
@@ -60,7 +61,7 @@ describe('#Blockchain', () => {
}
it('should get block by hash', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'post').returns(resolved)
const blockhash =
@@ -129,7 +130,7 @@ describe('#Blockchain', () => {
}
it('should get blockchain info', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBlockchainInfo()
@@ -147,7 +148,7 @@ describe('#Blockchain', () => {
const data = 527810
it('should get block count', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBlockCount()
@@ -166,7 +167,7 @@ describe('#Blockchain', () => {
'000000000000000001d127592d091d4c45062504663c9acab27a1b16c028e3c0'
it('should get block hash by height', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBlockHash(527810)
@@ -201,7 +202,7 @@ describe('#Blockchain', () => {
}
it('should get block header by hash', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getBlockHeader(
@@ -222,7 +223,7 @@ describe('#Blockchain', () => {
const data = '577528469277.1339'
it('should get difficulty', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getDifficulty()
@@ -240,7 +241,7 @@ describe('#Blockchain', () => {
const data = 'Transaction not in mempool'
it('should get mempool ancestors', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getMempoolAncestors(
@@ -263,7 +264,7 @@ describe('#Blockchain', () => {
}
it('should get mempool descendants', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getMempoolDescendants(
@@ -286,7 +287,7 @@ describe('#Blockchain', () => {
}
it('should get mempool entry', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getMempoolEntry(
@@ -314,7 +315,7 @@ describe('#Blockchain', () => {
}
it('should get mempool info', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getMempoolInfo()
@@ -355,7 +356,7 @@ describe('#Blockchain', () => {
}
it('should get mempool info', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.getRawMempool()
@@ -451,7 +452,7 @@ describe('#Blockchain', () => {
}
it('should get TODO', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.preciousBlock()
@@ -469,7 +470,7 @@ describe('#Blockchain', () => {
const data = 'Cannot prune blocks because node is not in prune mode.'
it('should prune blockchain', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'post').returns(resolved)
bchjs.Blockchain.pruneBlockchain(507)
@@ -487,7 +488,7 @@ describe('#Blockchain', () => {
const data = true
it('should verify blockchain', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.verifyChain(3, 6)
@@ -505,7 +506,7 @@ describe('#Blockchain', () => {
const data = "proof must be hexadecimal string (not '')"
it('should verify utxo proof', done => {
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Blockchain.verifyTxOutProof('3')
+6 -6
View File
@@ -1,8 +1,8 @@
const assert = require('assert')
const axios = require('axios')
const BCHJS = require('../../src/bch-js')
import assert from 'assert'
import axios from 'axios'
import BCHJS from '../../src/bch-js.js'
import sinon from 'sinon'
const bchjs = new BCHJS()
const sinon = require('sinon')
describe('#Control', () => {
let sandbox
@@ -24,7 +24,7 @@ describe('#Control', () => {
relayfee: 0.00001,
errors: ''
}
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Control.getNetworkInfo()
@@ -47,7 +47,7 @@ describe('#Control', () => {
chunks_free: 1
}
}
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'get').returns(resolved)
bchjs.Control.getMemoryInfo()
+5 -3
View File
@@ -1,8 +1,10 @@
import { createRequire } from 'module'
import assert from 'assert'
import BCHJS from '../../src/bch-js.js'
import { Buffer } from 'safe-buffer'
const require = createRequire(import.meta.url)
const fixtures = require('./fixtures/crypto.json')
const assert = require('assert')
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
const Buffer = require('safe-buffer').Buffer
describe('#Crypto', () => {
describe('#sha256', () => {
+5 -4
View File
@@ -1,10 +1,11 @@
// Public npm libraries
const assert = require('chai').assert
const sinon = require('sinon')
import chai from 'chai'
import sinon from 'sinon'
// Unit under test (uut)
const BCHJS = require('../../src/bch-js')
const mockData = require('./fixtures/dsproof-mock')
import BCHJS from '../../src/bch-js.js'
import mockData from './fixtures/dsproof-mock.js'
const { assert } = chai
let bchjs
const txid = 'ee0df780b58f6f24467605b2589c44c3a50fc849fb8f91b89669a4ae0d86bc7e'
+3 -2
View File
@@ -3,8 +3,9 @@
*/
// Global npm libraries
const assert = require('chai').assert
const Ecash = require('../../src/ecash')
import chai from 'chai'
import Ecash from '../../src/ecash.js'
const { assert } = chai
const uut = new Ecash()
describe('#eCash', () => {
+6 -4
View File
@@ -1,12 +1,14 @@
// Public npm libraries.
const assert = require('assert')
const Buffer = require('safe-buffer').Buffer
import assert from 'assert'
import { Buffer } from 'safe-buffer'
// Mocks
const fixtures = require('./fixtures/ecpair.json')
import { createRequire } from 'module'
// Unit under test (uut)
const BCHJS = require('../../src/bch-js')
import BCHJS from '../../src/bch-js.js'
const require = createRequire(import.meta.url)
const fixtures = require('./fixtures/ecpair.json')
// const bchjs = new BCHJS()
let bchjs
+7 -7
View File
@@ -1,13 +1,13 @@
const chai = require('chai')
import chai from 'chai'
import axios from 'axios'
import sinon from 'sinon'
import BCHJS from '../../src/bch-js.js'
import mockData from './fixtures/electrumx-mock.js'
const assert = chai.assert
const axios = require('axios')
const sinon = require('sinon')
const BCHJS = require('../../src/bch-js')
const bchjs = new BCHJS()
const mockData = require('./fixtures/electrumx-mock')
describe('#ElectrumX', () => {
let sandbox
beforeEach(() => (sandbox = sinon.createSandbox()))
+6 -5
View File
@@ -1,12 +1,13 @@
const assert = require('chai').assert
const sinon = require('sinon')
import chai from 'chai'
import sinon from 'sinon'
const BCHJS = require('../../src/bch-js')
import BCHJS from '../../src/bch-js.js'
import mockData from './fixtures/encryption-mock.js'
const { assert } = chai
// const bchjs = new BCHJS()
let bchjs
const mockData = require('./fixtures/encryption-mock')
describe('#Encryption', () => {
let sandbox
+1 -1
View File
@@ -39,7 +39,7 @@ const utxo = [
}
]
module.exports = {
export default {
balance,
utxo
}
+1 -1
View File
@@ -2,7 +2,7 @@
Mock data used for unit testing.
*/
module.exports = {
export default {
details: {
hash: '000000001c6aeec19265e9cc3ded8ba5ef5e63fae7747f30bf9c02c7bc8883f0',
size: 216,
+1 -1
View File
@@ -2,7 +2,7 @@
Mock data used for unit testing.
*/
module.exports = {
export default {
bestBlockHash:
'0000000000000000008e1f65f875703872544aa888c7ca6587f055f8f5fbd4bf',
+1 -1
View File
@@ -14,6 +14,6 @@ const dsproof = {
]
}
module.exports = {
export default {
dsproof
}
+1 -1
View File
@@ -290,7 +290,7 @@ const txHistoryWithUnconfirmed = {
]
}
module.exports = {
export default {
utxo,
utxos,
balance,
+1 -1
View File
@@ -13,7 +13,7 @@ const failureMock = {
publicKey: 'not found'
}
module.exports = {
export default {
successMock,
failureMock
}
+1 -1
View File
@@ -118,7 +118,7 @@ const paidFileData = {
}
}
module.exports = {
export default {
uploadData,
paymentInfo,
mockNewFileModel,
+1 -1
View File
@@ -77,7 +77,7 @@ const tx = {
'0200000001ac1e2e38764f9b81f3ff4317b4bd1a381dbdf311277a736f375d4ce217d3095f010000006a473044022000dd11c41a472f2e54348db996e60864d489429f12d1e044d49ff600b880c9590220715a926404bb0e2731a3795afb341ec1dad3f84ead7d27cd31fcc59abb14738c4121038476128287ac37c7a3cf7e8625fd5f024db1bc3d8e37395abe7bf42fda78d0d9ffffffff02e8030000000000001976a9142fe2c4c5ef359bb2fe1a849f891cecffbcfb4f7788acb0b40000000000001976a9142dbf5e1804c39a497b908c876097d63210c8490288ac00000000'
}
module.exports = {
export default {
balance,
utxo,
tx
+1 -1
View File
@@ -209,6 +209,6 @@ const mockRates = {
ZWL: '80215.03'
}
module.exports = {
export default {
mockRates
}
+63 -2
View File
@@ -144,10 +144,71 @@ const tokenData01 = {
qty: {}
}
module.exports = {
const tokenData = {
genesisData: tokenStats.tokenData,
immutableData: 'ipfs://bafybeidzfi3sictag4noy746gniyiwwcgio565xuzelmjfwxnga6qyiyai',
mutableData: 'ipfs://bafybeie6t5uyupddc7azms737xg4hxrj7i5t5ov3lb5g2qeehaujj6ak64'
}
const tokenMedia01 = {
tokenStats: {
type: 65,
ticker: 'TEST02',
name: 'How to Send BCH and Tokens',
tokenId: 'd9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b',
documentUri: 'ipfs://bafybeigjujbb55wqr5lns7z7vleg5cfp4yjwqp4swisxpgxl4xsp5tngai',
documentHash: '845f803d8dd3c2b6be94a6cf3a445b54c36f2e01bd5c9ab4c23f37dbd531489e',
decimals: 0,
mintBatonIsActive: false,
tokensInCirculationBN: '0',
tokensInCirculationStr: '0',
blockCreated: 740089,
totalBurned: '1',
totalMinted: '1',
parentGroupId: 'c9c425f2c6352697c6665a53e035cbad8a44c4b1e36491a1838dc4655479aa09'
},
mutableData: {
updated: '2022-05-14T15:27:44.429Z',
tokenIcon: 'https://bafybeiefg3nd5iognbqztkpi5hj34dmwkqfe7v7xeayn2nhhatinmjmzcy.ipfs.dweb.link/send-bch-token-icon.png',
tokenInfo: 'https://token.fullstack.cash/?tokenid=d9aafa7acb514c597caf440ae268b5e4e955f2687e05f044cdf8fd9550d9a27b',
description: 'This is an NFT representing a video. This is a test token.',
issuer: 'Chris Troutner',
forSale: false,
display: true,
currentOwner: {
bchAddr: 'bitcoincash:qqy7jcrm3vmtqs96r878hy2kx90mn84f25ujqw9z5h',
name: 'Chris Troutner',
contactEmail: 'chris.troutner@gmail.com'
},
content: {
youtube: 'https://youtu.be/WZRwkLPtkaI',
rumble: 'https://rumble.com/v14n00h-how-to-send-bch-and-tokens.html',
odysee: 'https://odysee.com/@trout:5/how-to-send-bch-tokens:0',
lbry: 'lbry://@trout#5/how-to-send-bch-tokens#0',
ipfs: 'bafybeigf3ky5i6fyxwk5bjmtsr6urqmlx4zq2lqathgguytey67iinh4be',
filecoin: 'https://bafybeigf3ky5i6fyxwk5bjmtsr6urqmlx4zq2lqathgguytey67iinh4be.ipfs.dweb.link/send-bch-2022-02-20_08.22.45.mp4'
}
},
immutableData: {
creationDate: '2022-05-13T21:32:11.619Z',
issuer: 'Chris Troutner',
license: 'https://bafybeidnkhjfsbihp4gquwqrs6y35jfpcriafymceszwvkundjkwk546pi.ipfs.dweb.link/copyright.txt',
name: 'How to Send BCH and Tokens'
},
tokenIcon: 'https://bafybeiefg3nd5iognbqztkpi5hj34dmwkqfe7v7xeayn2nhhatinmjmzcy.ipfs.dweb.link/send-bch-token-icon.png',
fullSizedUrl: '',
optimizedTokenIcon: 'https://p2wdb-gateway-678.fullstack.cash/ipfs/bafybeiefg3nd5iognbqztkpi5hj34dmwkqfe7v7xeayn2nhhatinmjmzcy/send-bch-token-icon.png',
optimizedFullSizedUrl: '',
iconRepoCompatible: false,
ps002Compatible: true
}
export default {
tokenStats,
txData,
balance,
status,
tokenData01
tokenData01,
tokenData,
tokenMedia01
}
+1 -1
View File
@@ -55,7 +55,7 @@ const mockGetInputAddrsOutput = [
}
]
module.exports = {
export default {
mockTx,
mockParentTx1,
mockGetInputAddrsOutput
+1 -1
View File
@@ -1411,7 +1411,7 @@ mock503Error.response = {
}
}
module.exports = {
export default {
mockList,
mockToken,
mockTokens,
+1 -1
View File
@@ -769,7 +769,7 @@ const genesisTestOpReturn03 = {
qty: '100000000000000000'
}
module.exports = {
export default {
nonSlpTxDetails,
slpTxDetails,
mockOpReturnData01,
+1 -1
View File
@@ -476,7 +476,7 @@ const noUtxoErr = {
'Key not found in database [bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7]'
}
module.exports = {
export default {
mockUtxoData,
mockHydratedUtxos,
mockTwoHydratedAddrs,
+5 -5
View File
@@ -1,10 +1,10 @@
// Public npm libraries
const assert = require('assert')
const axios = require('axios')
const sinon = require('sinon')
import assert from 'assert'
import axios from 'axios'
import sinon from 'sinon'
// Unit under test (uut)
const BCHJS = require('../../src/bch-js')
import BCHJS from '../../src/bch-js.js'
// const bchjs = new BCHJS()
let bchjs
@@ -20,7 +20,7 @@ describe('#Generating', () => {
it('should generate', done => {
const data = []
const resolved = new Promise(resolve => resolve({ data: data }))
const resolved = new Promise(resolve => resolve({ data }))
sandbox.stub(axios, 'post').returns(resolved)
bchjs.Generating.generateToAddress(

Some files were not shown because too many files have changed in this diff Show More