mirror of
https://github.com/Permissionless-Software-Foundation/bch-js.git
synced 2026-09-22 09:02:01 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
430f5f015e | ||
|
|
9e76b34d05 | ||
|
|
61078b2e91 | ||
|
|
82733a7e1a | ||
|
|
3685ea1541 | ||
|
|
5f217af3e9 | ||
|
|
e2ca3cfb00 | ||
|
|
690520d72e | ||
|
|
36660f3bc9 | ||
|
|
80ee5c8d31 | ||
|
|
b7e04522ce | ||
|
|
19f243428d | ||
|
|
16079c8d04 | ||
|
|
6743ca5fa9 | ||
|
|
ec046d3ef7 | ||
|
|
85405d8e70 | ||
|
|
1989b72b2a | ||
|
|
f1f2bf42d9 | ||
|
|
348a073714 | ||
|
|
e822b91e2c | ||
|
|
29a5173036 | ||
|
|
0bffff6f9b | ||
|
|
8c603d3886 | ||
|
|
542e59461f | ||
|
|
0912aadc1a | ||
|
|
638804de42 | ||
|
|
fe33ba3381 | ||
|
|
6f913cf3ad | ||
|
|
4d94ad13ff | ||
|
|
c02080d12a | ||
|
|
cc1809b242 | ||
|
|
13a7917d9b | ||
|
|
1573e0bc4c | ||
|
|
3fb5377acd | ||
|
|
a1ba2ac2f7 | ||
|
|
7dc29230da | ||
|
|
3b013b6215 | ||
|
|
9129c594dd | ||
|
|
64d99531de | ||
|
|
c9a8c5008e |
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright 2022 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:
|
||||
|
||||
|
||||
@@ -5,23 +5,21 @@
|
||||
[](https://github.com/Permissionless-Software-Foundation/bch-js/blob/master/LICENSE.md)
|
||||
[](https://github.com/feross/standard) [](https://gitter.im/Permissionless-Software-Foundation/bch-js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
[bch-js](https://www.npmjs.com/package/@psf/bch-js) is a JavaScript npm library for creating web and mobile apps that can interact with the Bitcoin Cash (BCH) and eCash (XEC) blockchains. 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).
|
||||
[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.
|
||||
|
||||
@@ -39,34 +37,84 @@ let bchjs = new BCHJS() // Defaults to BCHN network.
|
||||
This library is intended to be paired with
|
||||
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/v6/
|
||||
- ABC Mainnet REST API server: https://abc.fullstack.cash/v6/
|
||||
- BCHN Mainnet REST API server: https://x402-bch.fullstack.cash/v7/
|
||||
- Check server status: https://metrics.fullstack.cash
|
||||
|
||||
### API Key (JWT Token)
|
||||
## Configuration
|
||||
|
||||
The [psf-bch-api](https://github.com/Permissionless-Software-Foundation/psf-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://cashstack.info) if you want to understand the system-as-a-whole. The JWT token can be fed to bch-js _implicitly_ or _explicitly_.
|
||||
bch-js can be configured through constructor options or environment variables. Configuration options passed to the constructor take precedence over environment variables.
|
||||
|
||||
- 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:
|
||||
### Constructor Options
|
||||
|
||||
When instantiating BCHJS, you can pass a configuration object:
|
||||
|
||||
```javascript
|
||||
import BCHJS from "@psf/bch-js"
|
||||
let bchjs = new BCHJS({
|
||||
restURL: 'https://bchn.fullstack.cash/v6/',
|
||||
apiToken: 'eyJhbGciO...' // Your JWT token here.
|
||||
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
||||
bearerToken: 'your-bearer-token',
|
||||
wif: 'your-private-key-wif',
|
||||
paymentAmountSats: 20000,
|
||||
bchServerURL: 'https://bch.fullstack.cash'
|
||||
})
|
||||
```
|
||||
|
||||
### Gatsby & Web Apps
|
||||
### Configuration Options
|
||||
|
||||
| Option | Type | Required | Default | Description |
|
||||
|--------|------|----------|---------|-------------|
|
||||
| `restURL` | string | Yes* | - | The REST API server URL for making API calls. Must include trailing slash. *Required unless `RESTURL` environment variable is set. |
|
||||
| `bearerToken` | string | No | `''` | Bearer token for authentication with the REST API server. |
|
||||
| `wif` | string | No | `''` | Private key in WIF format. When provided, enables automatic x402 payment handling. |
|
||||
| `paymentAmountSats` | number | No | `20000` | Default amount of satoshis to send when making x402 payments. |
|
||||
| `bchServerURL` | string | No | `'https://bch.fullstack.cash'` | BCH server URL used for broadcasting payment transactions to the blockchain. This is separate from `restURL` and is specifically for x402 payment processing. |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
You can also configure bch-js using environment variables:
|
||||
|
||||
| Environment Variable | Config Option | Description |
|
||||
|---------------------|---------------|-------------|
|
||||
| `RESTURL` | `restURL` | REST API server URL for making API calls. |
|
||||
| `BCHJSBEARERTOKEN` | `bearerToken` | Bearer token for API authentication. |
|
||||
| `BCHJSWIF` | `wif` | Private key in WIF format for x402 payments. |
|
||||
| `BCHJSBCHSERVERURL` | `bchServerURL` | BCH server URL for x402 payment transactions. |
|
||||
|
||||
### Understanding restURL vs bchServerURL
|
||||
|
||||
These two configuration options serve different purposes:
|
||||
|
||||
- **`restURL`**: The REST API server used for all regular API calls (utxo queries, transaction history, etc.). This can be any bch-api compatible server, such as `https://x402-bch.fullstack.cash/v5/` or `https://bch.fullstack.cash/v5/`.
|
||||
|
||||
- **`bchServerURL`**: The BCH infrastructure server used specifically for broadcasting x402 payment transactions to the blockchain. This defaults to `https://bch.fullstack.cash` and should typically remain unchanged unless you have specific infrastructure requirements.
|
||||
|
||||
**Example Use Case**: Most users will use `https://x402-bch.fullstack.cash/v5/` as their `restURL` to access x402-protected APIs. However, when bch-js needs to make an x402 payment, it uses the `bchServerURL` (default: `https://bch.fullstack.cash`) to broadcast the payment transaction. This ensures payment transactions are sent through a reliable BCH infrastructure endpoint.
|
||||
|
||||
```javascript
|
||||
// Use x402-bch server for API calls, but bch.fullstack.cash for payments
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
||||
wif: 'your-private-key-wif'
|
||||
// bchServerURL defaults to 'https://bch.fullstack.cash'
|
||||
})
|
||||
|
||||
// Or explicitly set both
|
||||
const bchjs2 = new BCHJS({
|
||||
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
||||
bchServerURL: 'https://bch.fullstack.cash',
|
||||
wif: 'your-private-key-wif'
|
||||
})
|
||||
```
|
||||
|
||||
### Web Apps
|
||||
|
||||
[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.
|
||||
|
||||
[gatsby-theme-bch-wallet](https://github.com/Permissionless-Software-Foundation/gatsby-theme-bch-wallet) is a Gatsby Theme and [bch-wallet-starter](https://github.com/Permissionless-Software-Foundation/bch-wallet-starter) is a Gatsby Starter for building web wallets using minimal-slp-wallet.
|
||||
|
||||
[This gist](https://gist.github.com/christroutner/6cb9d1b615f3f9363af79723157bc434) shows how to include minimal-slp-wallet into a basic web page without using a framework.
|
||||
|
||||
[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
|
||||
|
||||
Generated
+652
-470
File diff suppressed because it is too large
Load Diff
+10
-11
@@ -2,22 +2,22 @@
|
||||
"name": "@psf/bch-js",
|
||||
"version": "7.0.0",
|
||||
"type": "module",
|
||||
"description": "A JavaScript library for working with Bitcoin Cash, eCash, and SLP Tokens",
|
||||
"description": "A JavaScript library for working with Bitcoin Cash 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": "c8 mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
|
||||
"test": "export RESTURL=http://localhost:5942/v6 && 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/",
|
||||
"test:integration:fullstack:free": "export RESTURL=https://bch.fullstack.cash/v6 && mocha --timeout 60000 test/integration/",
|
||||
"test:integration:fullstack:x402": "export RESTURL=https://x402-bch.fullstack.cash/v6 && export BCHJSWIF=L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4 && mocha --timeout 120000 test/integration/",
|
||||
"test:integration:local:noauth": "export RESTURL=http://localhost:5942/v6 && mocha --timeout 30000 test/integration/",
|
||||
"test:integration:local:auth": "export RESTURL=http://192.168.1.115:5942/v6 && export BCHJSBEARERTOKEN=temp01 && mocha --timeout 30000 test/integration/",
|
||||
"test:integration:local:x402": "export RESTURL=http://localhost:5942/v6 && export BCHJSWIF=L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4 && mocha --timeout 1200000 test/integration/",
|
||||
"test:integration:decatur": "export RESTURL=http://192.168.2.127:5942/v6 && 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"
|
||||
@@ -31,7 +31,6 @@
|
||||
"dependencies": {
|
||||
"@chris.troutner/bip32-utils": "1.0.5",
|
||||
"@psf/bip21": "2.0.1",
|
||||
"@psf/bitcoincash-ops": "2.0.0",
|
||||
"@psf/bitcoincashjs-lib": "4.0.3",
|
||||
"@psf/coininfo": "4.0.0",
|
||||
"axios": "1.13.2",
|
||||
@@ -53,7 +52,7 @@
|
||||
"slp-mdm": "0.0.7",
|
||||
"slp-parser": "0.0.4",
|
||||
"wif": "2.0.6",
|
||||
"x402-bch-axios": "1.1.1"
|
||||
"x402-bch-axios": "2.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apidoc": "1.2.0",
|
||||
|
||||
+28
-5
@@ -45,7 +45,6 @@ import Ecash from './ecash.js'
|
||||
// Indexers
|
||||
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) {
|
||||
@@ -54,7 +53,16 @@ class BCHJS {
|
||||
this.restURL = config.restURL
|
||||
} else if (process.env.RESTURL && process.env.RESTURL !== '') {
|
||||
this.restURL = process.env.RESTURL
|
||||
} else this.restURL = DEFAULT_REST_API
|
||||
} else {
|
||||
throw new Error(
|
||||
'REST API URL is required. Provide config.restURL or set the RESTURL environment variable.'
|
||||
)
|
||||
}
|
||||
|
||||
// Normalize restURL to always have a trailing slash
|
||||
if (!this.restURL.endsWith('/')) {
|
||||
this.restURL = this.restURL + '/'
|
||||
}
|
||||
|
||||
// Retrieve the Bearer token for simple token authentication.
|
||||
this.bearerToken = '' // default value.
|
||||
@@ -78,8 +86,20 @@ class BCHJS {
|
||||
} 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'
|
||||
|
||||
// Most TXs cost about 250 sats. So we'll set the default to 25000 sats,
|
||||
// so the mining fee is 1% of the transaction.
|
||||
this.paymentAmountSats = (config && config.paymentAmountSats) || 25000
|
||||
|
||||
// BCH server URL for x402 payments (separate from REST API server)
|
||||
// This is used when broadcasting payment transactions to the blockchain
|
||||
if (config && config.bchServerURL && config.bchServerURL !== '') {
|
||||
this.bchServerURL = config.bchServerURL
|
||||
} else if (process.env.BCHJSBCHSERVERURL && process.env.BCHJSBCHSERVERURL !== '') {
|
||||
this.bchServerURL = process.env.BCHJSBCHSERVERURL
|
||||
} else {
|
||||
this.bchServerURL = 'https://bch.fullstack.cash/v6'
|
||||
}
|
||||
|
||||
const libConfig = {
|
||||
restURL: this.restURL,
|
||||
@@ -100,7 +120,10 @@ class BCHJS {
|
||||
axiosInstance = withPaymentInterceptor(
|
||||
axiosInstance,
|
||||
signer,
|
||||
{ bchServerURL: this.bchServerURL }
|
||||
{
|
||||
apiType: 'rest-api',
|
||||
bchServerURL: this.bchServerURL
|
||||
}
|
||||
)
|
||||
|
||||
libConfig.axios = axiosInstance
|
||||
|
||||
+2
-1
@@ -6,7 +6,8 @@ class DSProof {
|
||||
constructor (config) {
|
||||
this.restURL = config.restURL
|
||||
this.authToken = config.authToken
|
||||
this.axios = axios
|
||||
// Use the shared axios instance if provided, otherwise fall back to axios
|
||||
this.axios = config.axios || axios
|
||||
|
||||
this.axiosOptions = {
|
||||
headers: {
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ class Price {
|
||||
async getUsd () {
|
||||
try {
|
||||
const response = await this.axios.get(
|
||||
`${this.restURL}price/usd`,
|
||||
`${this.restURL}price/bchusd`,
|
||||
this.axiosOptions
|
||||
)
|
||||
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
|
||||
|
||||
@@ -66,7 +66,7 @@ class PsfSlpIndexer {
|
||||
)
|
||||
return response.data
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
@@ -134,7 +134,7 @@ class PsfSlpIndexer {
|
||||
}
|
||||
throw new Error('Input address must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
@@ -199,7 +199,7 @@ class PsfSlpIndexer {
|
||||
}
|
||||
throw new Error('Input tokenId must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
@@ -427,7 +427,7 @@ class PsfSlpIndexer {
|
||||
}
|
||||
throw new Error('Input tokenId must be a string.')
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) throw error.response.data
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
@@ -522,7 +522,7 @@ class PsfSlpIndexer {
|
||||
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
|
||||
if (error.response && error.response.data && error.response.data.error) throw new Error(error.response.data.error)
|
||||
else throw error
|
||||
}
|
||||
}
|
||||
|
||||
+126
-3
@@ -1,7 +1,130 @@
|
||||
import Bitcoin from '@psf/bitcoincashjs-lib'
|
||||
import { createRequire } from 'module'
|
||||
const require = createRequire(import.meta.url)
|
||||
const opcodes = require('@psf/bitcoincash-ops')
|
||||
|
||||
// Inline opcodes data to avoid fs/import.meta issues in browser builds.
|
||||
// This data is from @psf/bitcoincash-ops/index.json
|
||||
const opcodes = {
|
||||
OP_FALSE: 0,
|
||||
OP_0: 0,
|
||||
OP_PUSHDATA1: 76,
|
||||
OP_PUSHDATA2: 77,
|
||||
OP_PUSHDATA4: 78,
|
||||
OP_1NEGATE: 79,
|
||||
OP_RESERVED: 80,
|
||||
OP_TRUE: 81,
|
||||
OP_1: 81,
|
||||
OP_2: 82,
|
||||
OP_3: 83,
|
||||
OP_4: 84,
|
||||
OP_5: 85,
|
||||
OP_6: 86,
|
||||
OP_7: 87,
|
||||
OP_8: 88,
|
||||
OP_9: 89,
|
||||
OP_10: 90,
|
||||
OP_11: 91,
|
||||
OP_12: 92,
|
||||
OP_13: 93,
|
||||
OP_14: 94,
|
||||
OP_15: 95,
|
||||
OP_16: 96,
|
||||
OP_NOP: 97,
|
||||
OP_VER: 98,
|
||||
OP_IF: 99,
|
||||
OP_NOTIF: 100,
|
||||
OP_VERIF: 101,
|
||||
OP_VERNOTIF: 102,
|
||||
OP_ELSE: 103,
|
||||
OP_ENDIF: 104,
|
||||
OP_VERIFY: 105,
|
||||
OP_RETURN: 106,
|
||||
OP_TOALTSTACK: 107,
|
||||
OP_FROMALTSTACK: 108,
|
||||
OP_2DROP: 109,
|
||||
OP_2DUP: 110,
|
||||
OP_3DUP: 111,
|
||||
OP_2OVER: 112,
|
||||
OP_2ROT: 113,
|
||||
OP_2SWAP: 114,
|
||||
OP_IFDUP: 115,
|
||||
OP_DEPTH: 116,
|
||||
OP_DROP: 117,
|
||||
OP_DUP: 118,
|
||||
OP_NIP: 119,
|
||||
OP_OVER: 120,
|
||||
OP_PICK: 121,
|
||||
OP_ROLL: 122,
|
||||
OP_ROT: 123,
|
||||
OP_SWAP: 124,
|
||||
OP_TUCK: 125,
|
||||
OP_CAT: 126,
|
||||
OP_SPLIT: 127,
|
||||
OP_NUM2BIN: 128,
|
||||
OP_BIN2NUM: 129,
|
||||
OP_SIZE: 130,
|
||||
OP_INVERT: 131,
|
||||
OP_AND: 132,
|
||||
OP_OR: 133,
|
||||
OP_XOR: 134,
|
||||
OP_EQUAL: 135,
|
||||
OP_EQUALVERIFY: 136,
|
||||
OP_RESERVED1: 137,
|
||||
OP_RESERVED2: 138,
|
||||
OP_1ADD: 139,
|
||||
OP_1SUB: 140,
|
||||
OP_2MUL: 141,
|
||||
OP_2DIV: 142,
|
||||
OP_NEGATE: 143,
|
||||
OP_ABS: 144,
|
||||
OP_NOT: 145,
|
||||
OP_0NOTEQUAL: 146,
|
||||
OP_ADD: 147,
|
||||
OP_SUB: 148,
|
||||
OP_MUL: 149,
|
||||
OP_DIV: 150,
|
||||
OP_MOD: 151,
|
||||
OP_LSHIFT: 152,
|
||||
OP_RSHIFT: 153,
|
||||
OP_BOOLAND: 154,
|
||||
OP_BOOLOR: 155,
|
||||
OP_NUMEQUAL: 156,
|
||||
OP_NUMEQUALVERIFY: 157,
|
||||
OP_NUMNOTEQUAL: 158,
|
||||
OP_LESSTHAN: 159,
|
||||
OP_GREATERTHAN: 160,
|
||||
OP_LESSTHANOREQUAL: 161,
|
||||
OP_GREATERTHANOREQUAL: 162,
|
||||
OP_MIN: 163,
|
||||
OP_MAX: 164,
|
||||
OP_WITHIN: 165,
|
||||
OP_RIPEMD160: 166,
|
||||
OP_SHA1: 167,
|
||||
OP_SHA256: 168,
|
||||
OP_HASH160: 169,
|
||||
OP_HASH256: 170,
|
||||
OP_CODESEPARATOR: 171,
|
||||
OP_CHECKSIG: 172,
|
||||
OP_CHECKSIGVERIFY: 173,
|
||||
OP_CHECKMULTISIG: 174,
|
||||
OP_CHECKMULTISIGVERIFY: 175,
|
||||
OP_NOP1: 176,
|
||||
OP_NOP2: 177,
|
||||
OP_CHECKLOCKTIMEVERIFY: 177,
|
||||
OP_NOP3: 178,
|
||||
OP_CHECKSEQUENCEVERIFY: 178,
|
||||
OP_NOP4: 179,
|
||||
OP_NOP5: 180,
|
||||
OP_NOP6: 181,
|
||||
OP_NOP7: 182,
|
||||
OP_NOP8: 183,
|
||||
OP_NOP9: 184,
|
||||
OP_NOP10: 185,
|
||||
OP_CHECKDATASIG: 186,
|
||||
OP_CHECKDATASIGVERIFY: 187,
|
||||
OP_REVERSEBYTES: 188,
|
||||
OP_PUBKEYHASH: 253,
|
||||
OP_PUBKEY: 254,
|
||||
OP_INVALIDOPCODE: 255
|
||||
}
|
||||
|
||||
class Script {
|
||||
constructor () {
|
||||
|
||||
@@ -20,6 +20,12 @@ class UTXO {
|
||||
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
||||
this.BigNumber = BigNumber
|
||||
this.blockchain = new Blockchain(config)
|
||||
|
||||
// Bind 'this' object to all subfunctions.
|
||||
this.get = this.get.bind(this)
|
||||
this.hydrateTokenData = this.hydrateTokenData.bind(this)
|
||||
this.findBiggestUtxo = this.findBiggestUtxo.bind(this)
|
||||
this.isValid = this.isValid.bind(this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,14 +43,14 @@ describe('#PsfSlpIndexer', () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
testErr.response = { data: { error: 'Status 422' } }
|
||||
sandbox.stub(axios, 'get').throws(testErr)
|
||||
|
||||
await bchjs.PsfSlpIndexer.status()
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
assert.equal(err.message, 'Status 422')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -103,7 +103,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
testErr.response = { data: { error: 'Status 422' } }
|
||||
sandbox.stub(axios, 'post').throws(testErr)
|
||||
|
||||
const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
|
||||
@@ -112,7 +112,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
assert.equal(err.message, 'Status 422')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -174,7 +174,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
testErr.response = { data: { error: 'Status 422' } }
|
||||
sandbox.stub(axios, 'post').throws(testErr)
|
||||
|
||||
const tokenId =
|
||||
@@ -182,7 +182,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
await bchjs.PsfSlpIndexer.tokenStats(tokenId)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
assert.equal(err.message, 'Status 422')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -399,7 +399,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
try {
|
||||
// Stub the network call.
|
||||
const testErr = new Error()
|
||||
testErr.response = { data: { status: 422 } }
|
||||
testErr.response = { data: { error: 'Status 422' } }
|
||||
sandbox.stub(axios, 'post').throws(testErr)
|
||||
|
||||
const tokenId =
|
||||
@@ -407,7 +407,7 @@ describe('#PsfSlpIndexer', () => {
|
||||
await bchjs.PsfSlpIndexer.getTokenData(tokenId)
|
||||
assert.equal(true, false, 'Unexpected result!')
|
||||
} catch (err) {
|
||||
assert.equal(err.status, 422)
|
||||
assert.equal(err.message, 'Status 422')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
+109
-14
@@ -15,16 +15,21 @@ describe('#X402 Integration', () => {
|
||||
|
||||
describe('#Constructor Configuration', () => {
|
||||
it('should initialize without x402 by default', () => {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
assert.strictEqual(bchjs.wif, '')
|
||||
assert.strictEqual(bchjs.paymentAmountSats, 20000)
|
||||
assert.strictEqual(bchjs.bchServerURL, 'https://free-bch.fullstack.cash')
|
||||
// assert.strictEqual(bchjs.paymentAmountSats, 10000)
|
||||
assert.strictEqual(typeof bchjs.paymentAmountSats, 'number')
|
||||
assert.ok(Number.isInteger(bchjs.paymentAmountSats) && bchjs.paymentAmountSats > 0)
|
||||
assert.strictEqual(bchjs.bchServerURL, 'https://bch.fullstack.cash/v6')
|
||||
})
|
||||
|
||||
it('should accept wif in config', () => {
|
||||
const testWif = 'L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/',
|
||||
wif: testWif
|
||||
})
|
||||
|
||||
@@ -33,6 +38,7 @@ describe('#X402 Integration', () => {
|
||||
|
||||
it('should accept paymentAmountSats in config', () => {
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/',
|
||||
paymentAmountSats: 5000
|
||||
})
|
||||
|
||||
@@ -42,6 +48,7 @@ describe('#X402 Integration', () => {
|
||||
it('should accept bchServerURL in config', () => {
|
||||
const customUrl = 'http://localhost:5000'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/',
|
||||
bchServerURL: customUrl
|
||||
})
|
||||
|
||||
@@ -54,8 +61,10 @@ describe('#X402 Integration', () => {
|
||||
})
|
||||
|
||||
assert.strictEqual(bchjs.wif, '')
|
||||
assert.strictEqual(bchjs.paymentAmountSats, 20000)
|
||||
assert.strictEqual(bchjs.bchServerURL, 'https://free-bch.fullstack.cash')
|
||||
// assert.strictEqual(bchjs.paymentAmountSats, 10000)
|
||||
assert.strictEqual(typeof bchjs.paymentAmountSats, 'number')
|
||||
assert.ok(Number.isInteger(bchjs.paymentAmountSats) && bchjs.paymentAmountSats > 0)
|
||||
assert.strictEqual(bchjs.bchServerURL, 'https://bch.fullstack.cash/v6')
|
||||
})
|
||||
|
||||
it('should read WIF from BCHJSWIF environment variable', () => {
|
||||
@@ -64,7 +73,9 @@ describe('#X402 Integration', () => {
|
||||
process.env.BCHJSWIF = testWif
|
||||
|
||||
try {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
assert.strictEqual(bchjs.wif, testWif)
|
||||
} finally {
|
||||
// Restore original env value
|
||||
@@ -83,7 +94,10 @@ describe('#X402 Integration', () => {
|
||||
process.env.BCHJSWIF = envWif
|
||||
|
||||
try {
|
||||
const bchjs = new BCHJS({ wif: configWif })
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/',
|
||||
wif: configWif
|
||||
})
|
||||
assert.strictEqual(bchjs.wif, configWif)
|
||||
} finally {
|
||||
// Restore original env value
|
||||
@@ -94,29 +108,103 @@ describe('#X402 Integration', () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should read bchServerURL from BCHJSBCHSERVERURL environment variable', () => {
|
||||
const testUrl = 'https://custom-bch-server.example.com'
|
||||
const originalEnv = process.env.BCHJSBCHSERVERURL
|
||||
process.env.BCHJSBCHSERVERURL = testUrl
|
||||
|
||||
try {
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
assert.strictEqual(bchjs.bchServerURL, testUrl)
|
||||
} finally {
|
||||
// Restore original env value
|
||||
if (originalEnv === undefined) {
|
||||
delete process.env.BCHJSBCHSERVERURL
|
||||
} else {
|
||||
process.env.BCHJSBCHSERVERURL = originalEnv
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should prefer config.bchServerURL over BCHJSBCHSERVERURL environment variable', () => {
|
||||
const configUrl = 'https://config-server.example.com'
|
||||
const envUrl = 'https://env-server.example.com'
|
||||
const originalEnv = process.env.BCHJSBCHSERVERURL
|
||||
process.env.BCHJSBCHSERVERURL = envUrl
|
||||
|
||||
try {
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/',
|
||||
bchServerURL: configUrl
|
||||
})
|
||||
assert.strictEqual(bchjs.bchServerURL, configUrl)
|
||||
} finally {
|
||||
// Restore original env value
|
||||
if (originalEnv === undefined) {
|
||||
delete process.env.BCHJSBCHSERVERURL
|
||||
} else {
|
||||
process.env.BCHJSBCHSERVERURL = originalEnv
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should keep restURL and bchServerURL independent', () => {
|
||||
const customRestURL = 'https://x402-bch.fullstack.cash/v5/'
|
||||
const customBchServerURL = 'https://bch.fullstack.cash'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: customRestURL,
|
||||
bchServerURL: customBchServerURL
|
||||
})
|
||||
|
||||
assert.strictEqual(bchjs.restURL, customRestURL)
|
||||
assert.strictEqual(bchjs.bchServerURL, customBchServerURL)
|
||||
// Verify they are different values
|
||||
assert.notStrictEqual(bchjs.restURL, bchjs.bchServerURL)
|
||||
})
|
||||
|
||||
it('should use default bchServerURL when restURL is customized', () => {
|
||||
const customRestURL = 'https://x402-bch.fullstack.cash/v5/'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: customRestURL
|
||||
})
|
||||
|
||||
assert.strictEqual(bchjs.restURL, customRestURL)
|
||||
assert.strictEqual(bchjs.bchServerURL, 'https://bch.fullstack.cash/v6')
|
||||
})
|
||||
})
|
||||
|
||||
describe('#x402 Helper Functions', () => {
|
||||
it('should expose createSigner function', () => {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
assert.strictEqual(typeof bchjs.x402.createSigner, 'function')
|
||||
})
|
||||
|
||||
it('should expose withPaymentInterceptor function', () => {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
assert.strictEqual(typeof bchjs.x402.withPaymentInterceptor, 'function')
|
||||
})
|
||||
|
||||
it('should expose createPaymentHeader function', () => {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
assert.strictEqual(typeof bchjs.x402.createPaymentHeader, 'function')
|
||||
})
|
||||
|
||||
it('should expose selectPaymentRequirements function', () => {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
assert.strictEqual(typeof bchjs.x402.selectPaymentRequirements, 'function')
|
||||
})
|
||||
@@ -124,7 +212,9 @@ describe('#X402 Integration', () => {
|
||||
|
||||
describe('#Axios Instance', () => {
|
||||
it('should have axios available in sub-modules', () => {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
// Check that sub-modules have axios available (from their own import or config)
|
||||
assert.ok(bchjs.Control.axios)
|
||||
@@ -138,6 +228,7 @@ describe('#X402 Integration', () => {
|
||||
it('should pass x402-wrapped axios instance when WIF is provided', () => {
|
||||
const testWif = 'L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4'
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/',
|
||||
wif: testWif
|
||||
})
|
||||
|
||||
@@ -151,7 +242,9 @@ describe('#X402 Integration', () => {
|
||||
|
||||
describe('#selectPaymentRequirements', () => {
|
||||
it('should select BCH utxo payment requirements from accepts array', () => {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
const accepts = [
|
||||
{
|
||||
@@ -175,7 +268,9 @@ describe('#X402 Integration', () => {
|
||||
})
|
||||
|
||||
it('should throw an error when no BCH requirements found', () => {
|
||||
const bchjs = new BCHJS()
|
||||
const bchjs = new BCHJS({
|
||||
restURL: 'http://localhost:3000/v5/'
|
||||
})
|
||||
|
||||
const accepts = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user