@psf/bch-js 7.1.0 → 7.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/package.json +7 -6
- package/src/bch-js.js +15 -3
- package/src/utxo.js +6 -0
- package/test/unit/x402.js +105 -14
package/README.md
CHANGED
|
@@ -40,6 +40,72 @@ the [psf-bch-api](https://github.com/Permissionless-Software-Foundation/psf-bch-
|
|
|
40
40
|
- BCHN Mainnet REST API server: https://x402-bch.fullstack.cash/v7/
|
|
41
41
|
- Check server status: https://metrics.fullstack.cash
|
|
42
42
|
|
|
43
|
+
## Configuration
|
|
44
|
+
|
|
45
|
+
bch-js can be configured through constructor options or environment variables. Configuration options passed to the constructor take precedence over environment variables.
|
|
46
|
+
|
|
47
|
+
### Constructor Options
|
|
48
|
+
|
|
49
|
+
When instantiating BCHJS, you can pass a configuration object:
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
import BCHJS from "@psf/bch-js"
|
|
53
|
+
|
|
54
|
+
const bchjs = new BCHJS({
|
|
55
|
+
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
|
56
|
+
bearerToken: 'your-bearer-token',
|
|
57
|
+
wif: 'your-private-key-wif',
|
|
58
|
+
paymentAmountSats: 20000,
|
|
59
|
+
bchServerURL: 'https://bch.fullstack.cash'
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Configuration Options
|
|
64
|
+
|
|
65
|
+
| Option | Type | Required | Default | Description |
|
|
66
|
+
|--------|------|----------|---------|-------------|
|
|
67
|
+
| `restURL` | string | Yes* | - | The REST API server URL for making API calls. Must include trailing slash. *Required unless `RESTURL` environment variable is set. |
|
|
68
|
+
| `bearerToken` | string | No | `''` | Bearer token for authentication with the REST API server. |
|
|
69
|
+
| `wif` | string | No | `''` | Private key in WIF format. When provided, enables automatic x402 payment handling. |
|
|
70
|
+
| `paymentAmountSats` | number | No | `20000` | Default amount of satoshis to send when making x402 payments. |
|
|
71
|
+
| `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. |
|
|
72
|
+
|
|
73
|
+
### Environment Variables
|
|
74
|
+
|
|
75
|
+
You can also configure bch-js using environment variables:
|
|
76
|
+
|
|
77
|
+
| Environment Variable | Config Option | Description |
|
|
78
|
+
|---------------------|---------------|-------------|
|
|
79
|
+
| `RESTURL` | `restURL` | REST API server URL for making API calls. |
|
|
80
|
+
| `BCHJSBEARERTOKEN` | `bearerToken` | Bearer token for API authentication. |
|
|
81
|
+
| `BCHJSWIF` | `wif` | Private key in WIF format for x402 payments. |
|
|
82
|
+
| `BCHJSBCHSERVERURL` | `bchServerURL` | BCH server URL for x402 payment transactions. |
|
|
83
|
+
|
|
84
|
+
### Understanding restURL vs bchServerURL
|
|
85
|
+
|
|
86
|
+
These two configuration options serve different purposes:
|
|
87
|
+
|
|
88
|
+
- **`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/`.
|
|
89
|
+
|
|
90
|
+
- **`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.
|
|
91
|
+
|
|
92
|
+
**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.
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// Use x402-bch server for API calls, but bch.fullstack.cash for payments
|
|
96
|
+
const bchjs = new BCHJS({
|
|
97
|
+
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
|
98
|
+
wif: 'your-private-key-wif'
|
|
99
|
+
// bchServerURL defaults to 'https://bch.fullstack.cash'
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// Or explicitly set both
|
|
103
|
+
const bchjs2 = new BCHJS({
|
|
104
|
+
restURL: 'https://x402-bch.fullstack.cash/v5/',
|
|
105
|
+
bchServerURL: 'https://bch.fullstack.cash',
|
|
106
|
+
wif: 'your-private-key-wif'
|
|
107
|
+
})
|
|
108
|
+
```
|
|
43
109
|
|
|
44
110
|
### Web Apps
|
|
45
111
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@psf/bch-js",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.2",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "A JavaScript library for working with Bitcoin Cash
|
|
5
|
+
"description": "A JavaScript library for working with Bitcoin Cash and SLP Tokens",
|
|
6
6
|
"author": "Chris Troutner <chris.troutner@gmail.com>",
|
|
7
7
|
"contributors": [
|
|
8
8
|
"Gabriel Cardona <gabriel@bitcoin.com>",
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
"scripts": {
|
|
13
13
|
"test": "export RESTURL=http://localhost:5942/v6 && c8 mocha --trace-warnings --unhandled-rejections=strict --timeout 30000 test/unit/",
|
|
14
14
|
"test:integration": "npm run test:integration:local:noauth",
|
|
15
|
-
"test:integration:
|
|
15
|
+
"test:integration:fullstack:free": "export RESTURL=https://bch.fullstack.cash/v6 && mocha --timeout 60000 test/integration/",
|
|
16
|
+
"test:integration:fullstack:x402": "export RESTURL=https://x402-bch.fullstack.cash/v6 && export BCHJSWIF=L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4 && mocha --timeout 30000 test/integration/",
|
|
16
17
|
"test:integration:local:noauth": "export RESTURL=http://localhost:5942/v6 && mocha --timeout 30000 test/integration/",
|
|
17
|
-
"test:integration:local:auth": "export RESTURL=http://
|
|
18
|
+
"test:integration:local:auth": "export RESTURL=http://192.168.1.115:5942/v6 && export BCHJSBEARERTOKEN=temp01 && mocha --timeout 30000 test/integration/",
|
|
19
|
+
"test:integration:local:x402": "export RESTURL=http://localhost:5942/v6 && export BCHJSWIF=L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4 && mocha --timeout 1200000 test/integration/",
|
|
18
20
|
"test:integration:decatur": "export RESTURL=http://192.168.2.127:5942/v6 && mocha --timeout 30000 test/integration/",
|
|
19
|
-
"test:integration:x402": "export RESTURL=http://localhost:5942/v6 && export BCHJSWIF=L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4 && mocha --timeout 30000 test/integration/",
|
|
20
21
|
"coverage": "nyc --reporter=html mocha --timeout 25000 test/unit/",
|
|
21
22
|
"docs": "./node_modules/.bin/apidoc -i src/ -o docs && ./fix-docs-contrast.sh",
|
|
22
23
|
"lint": "standard --env mocha --fix"
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
"slp-mdm": "0.0.7",
|
|
52
53
|
"slp-parser": "0.0.4",
|
|
53
54
|
"wif": "2.0.6",
|
|
54
|
-
"x402-bch-axios": "1.1.
|
|
55
|
+
"x402-bch-axios": "1.1.2"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"apidoc": "1.2.0",
|
package/src/bch-js.js
CHANGED
|
@@ -86,8 +86,17 @@ class BCHJS {
|
|
|
86
86
|
} else if (process.env.BCHJSWIF && process.env.BCHJSWIF !== '') {
|
|
87
87
|
this.wif = process.env.BCHJSWIF
|
|
88
88
|
}
|
|
89
|
-
this.paymentAmountSats = (config && config.paymentAmountSats) || 2000 *
|
|
90
|
-
|
|
89
|
+
this.paymentAmountSats = (config && config.paymentAmountSats) || 2000 * 5
|
|
90
|
+
|
|
91
|
+
// BCH server URL for x402 payments (separate from REST API server)
|
|
92
|
+
// This is used when broadcasting payment transactions to the blockchain
|
|
93
|
+
if (config && config.bchServerURL && config.bchServerURL !== '') {
|
|
94
|
+
this.bchServerURL = config.bchServerURL
|
|
95
|
+
} else if (process.env.BCHJSBCHSERVERURL && process.env.BCHJSBCHSERVERURL !== '') {
|
|
96
|
+
this.bchServerURL = process.env.BCHJSBCHSERVERURL
|
|
97
|
+
} else {
|
|
98
|
+
this.bchServerURL = 'https://bch.fullstack.cash/v6/'
|
|
99
|
+
}
|
|
91
100
|
|
|
92
101
|
const libConfig = {
|
|
93
102
|
restURL: this.restURL,
|
|
@@ -108,7 +117,10 @@ class BCHJS {
|
|
|
108
117
|
axiosInstance = withPaymentInterceptor(
|
|
109
118
|
axiosInstance,
|
|
110
119
|
signer,
|
|
111
|
-
{
|
|
120
|
+
{
|
|
121
|
+
apiType: 'rest-api',
|
|
122
|
+
bchServerURL: this.bchServerURL
|
|
123
|
+
}
|
|
112
124
|
)
|
|
113
125
|
|
|
114
126
|
libConfig.axios = axiosInstance
|
package/src/utxo.js
CHANGED
|
@@ -20,6 +20,12 @@ class UTXO {
|
|
|
20
20
|
this.psfSlpIndexer = new PsfSlpIndexer(config)
|
|
21
21
|
this.BigNumber = BigNumber
|
|
22
22
|
this.blockchain = new Blockchain(config)
|
|
23
|
+
|
|
24
|
+
// Bind 'this' object to all subfunctions.
|
|
25
|
+
this.get = this.get.bind(this)
|
|
26
|
+
this.hydrateTokenData = this.hydrateTokenData.bind(this)
|
|
27
|
+
this.findBiggestUtxo = this.findBiggestUtxo.bind(this)
|
|
28
|
+
this.isValid = this.isValid.bind(this)
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
/**
|
package/test/unit/x402.js
CHANGED
|
@@ -15,16 +15,19 @@ describe('#X402 Integration', () => {
|
|
|
15
15
|
|
|
16
16
|
describe('#Constructor Configuration', () => {
|
|
17
17
|
it('should initialize without x402 by default', () => {
|
|
18
|
-
const bchjs = new BCHJS(
|
|
18
|
+
const bchjs = new BCHJS({
|
|
19
|
+
restURL: 'http://localhost:3000/v5/'
|
|
20
|
+
})
|
|
19
21
|
|
|
20
22
|
assert.strictEqual(bchjs.wif, '')
|
|
21
|
-
assert.strictEqual(bchjs.paymentAmountSats,
|
|
22
|
-
assert.strictEqual(bchjs.bchServerURL, 'https://
|
|
23
|
+
assert.strictEqual(bchjs.paymentAmountSats, 10000)
|
|
24
|
+
assert.strictEqual(bchjs.bchServerURL, 'https://bch.fullstack.cash/v6/')
|
|
23
25
|
})
|
|
24
26
|
|
|
25
27
|
it('should accept wif in config', () => {
|
|
26
28
|
const testWif = 'L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4'
|
|
27
29
|
const bchjs = new BCHJS({
|
|
30
|
+
restURL: 'http://localhost:3000/v5/',
|
|
28
31
|
wif: testWif
|
|
29
32
|
})
|
|
30
33
|
|
|
@@ -33,6 +36,7 @@ describe('#X402 Integration', () => {
|
|
|
33
36
|
|
|
34
37
|
it('should accept paymentAmountSats in config', () => {
|
|
35
38
|
const bchjs = new BCHJS({
|
|
39
|
+
restURL: 'http://localhost:3000/v5/',
|
|
36
40
|
paymentAmountSats: 5000
|
|
37
41
|
})
|
|
38
42
|
|
|
@@ -42,6 +46,7 @@ describe('#X402 Integration', () => {
|
|
|
42
46
|
it('should accept bchServerURL in config', () => {
|
|
43
47
|
const customUrl = 'http://localhost:5000'
|
|
44
48
|
const bchjs = new BCHJS({
|
|
49
|
+
restURL: 'http://localhost:3000/v5/',
|
|
45
50
|
bchServerURL: customUrl
|
|
46
51
|
})
|
|
47
52
|
|
|
@@ -54,8 +59,8 @@ describe('#X402 Integration', () => {
|
|
|
54
59
|
})
|
|
55
60
|
|
|
56
61
|
assert.strictEqual(bchjs.wif, '')
|
|
57
|
-
assert.strictEqual(bchjs.paymentAmountSats,
|
|
58
|
-
assert.strictEqual(bchjs.bchServerURL, 'https://
|
|
62
|
+
assert.strictEqual(bchjs.paymentAmountSats, 10000)
|
|
63
|
+
assert.strictEqual(bchjs.bchServerURL, 'https://bch.fullstack.cash/v6/')
|
|
59
64
|
})
|
|
60
65
|
|
|
61
66
|
it('should read WIF from BCHJSWIF environment variable', () => {
|
|
@@ -64,7 +69,9 @@ describe('#X402 Integration', () => {
|
|
|
64
69
|
process.env.BCHJSWIF = testWif
|
|
65
70
|
|
|
66
71
|
try {
|
|
67
|
-
const bchjs = new BCHJS(
|
|
72
|
+
const bchjs = new BCHJS({
|
|
73
|
+
restURL: 'http://localhost:3000/v5/'
|
|
74
|
+
})
|
|
68
75
|
assert.strictEqual(bchjs.wif, testWif)
|
|
69
76
|
} finally {
|
|
70
77
|
// Restore original env value
|
|
@@ -83,7 +90,10 @@ describe('#X402 Integration', () => {
|
|
|
83
90
|
process.env.BCHJSWIF = envWif
|
|
84
91
|
|
|
85
92
|
try {
|
|
86
|
-
const bchjs = new BCHJS({
|
|
93
|
+
const bchjs = new BCHJS({
|
|
94
|
+
restURL: 'http://localhost:3000/v5/',
|
|
95
|
+
wif: configWif
|
|
96
|
+
})
|
|
87
97
|
assert.strictEqual(bchjs.wif, configWif)
|
|
88
98
|
} finally {
|
|
89
99
|
// Restore original env value
|
|
@@ -94,29 +104,103 @@ describe('#X402 Integration', () => {
|
|
|
94
104
|
}
|
|
95
105
|
}
|
|
96
106
|
})
|
|
107
|
+
|
|
108
|
+
it('should read bchServerURL from BCHJSBCHSERVERURL environment variable', () => {
|
|
109
|
+
const testUrl = 'https://custom-bch-server.example.com'
|
|
110
|
+
const originalEnv = process.env.BCHJSBCHSERVERURL
|
|
111
|
+
process.env.BCHJSBCHSERVERURL = testUrl
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const bchjs = new BCHJS({
|
|
115
|
+
restURL: 'http://localhost:3000/v5/'
|
|
116
|
+
})
|
|
117
|
+
assert.strictEqual(bchjs.bchServerURL, testUrl)
|
|
118
|
+
} finally {
|
|
119
|
+
// Restore original env value
|
|
120
|
+
if (originalEnv === undefined) {
|
|
121
|
+
delete process.env.BCHJSBCHSERVERURL
|
|
122
|
+
} else {
|
|
123
|
+
process.env.BCHJSBCHSERVERURL = originalEnv
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
it('should prefer config.bchServerURL over BCHJSBCHSERVERURL environment variable', () => {
|
|
129
|
+
const configUrl = 'https://config-server.example.com'
|
|
130
|
+
const envUrl = 'https://env-server.example.com'
|
|
131
|
+
const originalEnv = process.env.BCHJSBCHSERVERURL
|
|
132
|
+
process.env.BCHJSBCHSERVERURL = envUrl
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const bchjs = new BCHJS({
|
|
136
|
+
restURL: 'http://localhost:3000/v5/',
|
|
137
|
+
bchServerURL: configUrl
|
|
138
|
+
})
|
|
139
|
+
assert.strictEqual(bchjs.bchServerURL, configUrl)
|
|
140
|
+
} finally {
|
|
141
|
+
// Restore original env value
|
|
142
|
+
if (originalEnv === undefined) {
|
|
143
|
+
delete process.env.BCHJSBCHSERVERURL
|
|
144
|
+
} else {
|
|
145
|
+
process.env.BCHJSBCHSERVERURL = originalEnv
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('should keep restURL and bchServerURL independent', () => {
|
|
151
|
+
const customRestURL = 'https://x402-bch.fullstack.cash/v5/'
|
|
152
|
+
const customBchServerURL = 'https://bch.fullstack.cash'
|
|
153
|
+
const bchjs = new BCHJS({
|
|
154
|
+
restURL: customRestURL,
|
|
155
|
+
bchServerURL: customBchServerURL
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
assert.strictEqual(bchjs.restURL, customRestURL)
|
|
159
|
+
assert.strictEqual(bchjs.bchServerURL, customBchServerURL)
|
|
160
|
+
// Verify they are different values
|
|
161
|
+
assert.notStrictEqual(bchjs.restURL, bchjs.bchServerURL)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should use default bchServerURL when restURL is customized', () => {
|
|
165
|
+
const customRestURL = 'https://x402-bch.fullstack.cash/v5/'
|
|
166
|
+
const bchjs = new BCHJS({
|
|
167
|
+
restURL: customRestURL
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
assert.strictEqual(bchjs.restURL, customRestURL)
|
|
171
|
+
assert.strictEqual(bchjs.bchServerURL, 'https://bch.fullstack.cash/v6/')
|
|
172
|
+
})
|
|
97
173
|
})
|
|
98
174
|
|
|
99
175
|
describe('#x402 Helper Functions', () => {
|
|
100
176
|
it('should expose createSigner function', () => {
|
|
101
|
-
const bchjs = new BCHJS(
|
|
177
|
+
const bchjs = new BCHJS({
|
|
178
|
+
restURL: 'http://localhost:3000/v5/'
|
|
179
|
+
})
|
|
102
180
|
|
|
103
181
|
assert.strictEqual(typeof bchjs.x402.createSigner, 'function')
|
|
104
182
|
})
|
|
105
183
|
|
|
106
184
|
it('should expose withPaymentInterceptor function', () => {
|
|
107
|
-
const bchjs = new BCHJS(
|
|
185
|
+
const bchjs = new BCHJS({
|
|
186
|
+
restURL: 'http://localhost:3000/v5/'
|
|
187
|
+
})
|
|
108
188
|
|
|
109
189
|
assert.strictEqual(typeof bchjs.x402.withPaymentInterceptor, 'function')
|
|
110
190
|
})
|
|
111
191
|
|
|
112
192
|
it('should expose createPaymentHeader function', () => {
|
|
113
|
-
const bchjs = new BCHJS(
|
|
193
|
+
const bchjs = new BCHJS({
|
|
194
|
+
restURL: 'http://localhost:3000/v5/'
|
|
195
|
+
})
|
|
114
196
|
|
|
115
197
|
assert.strictEqual(typeof bchjs.x402.createPaymentHeader, 'function')
|
|
116
198
|
})
|
|
117
199
|
|
|
118
200
|
it('should expose selectPaymentRequirements function', () => {
|
|
119
|
-
const bchjs = new BCHJS(
|
|
201
|
+
const bchjs = new BCHJS({
|
|
202
|
+
restURL: 'http://localhost:3000/v5/'
|
|
203
|
+
})
|
|
120
204
|
|
|
121
205
|
assert.strictEqual(typeof bchjs.x402.selectPaymentRequirements, 'function')
|
|
122
206
|
})
|
|
@@ -124,7 +208,9 @@ describe('#X402 Integration', () => {
|
|
|
124
208
|
|
|
125
209
|
describe('#Axios Instance', () => {
|
|
126
210
|
it('should have axios available in sub-modules', () => {
|
|
127
|
-
const bchjs = new BCHJS(
|
|
211
|
+
const bchjs = new BCHJS({
|
|
212
|
+
restURL: 'http://localhost:3000/v5/'
|
|
213
|
+
})
|
|
128
214
|
|
|
129
215
|
// Check that sub-modules have axios available (from their own import or config)
|
|
130
216
|
assert.ok(bchjs.Control.axios)
|
|
@@ -138,6 +224,7 @@ describe('#X402 Integration', () => {
|
|
|
138
224
|
it('should pass x402-wrapped axios instance when WIF is provided', () => {
|
|
139
225
|
const testWif = 'L1eYaneXDDXy8VDig4Arwe8wYHbhtsA5wuQvwsKwhaYeneoZuKG4'
|
|
140
226
|
const bchjs = new BCHJS({
|
|
227
|
+
restURL: 'http://localhost:3000/v5/',
|
|
141
228
|
wif: testWif
|
|
142
229
|
})
|
|
143
230
|
|
|
@@ -151,7 +238,9 @@ describe('#X402 Integration', () => {
|
|
|
151
238
|
|
|
152
239
|
describe('#selectPaymentRequirements', () => {
|
|
153
240
|
it('should select BCH utxo payment requirements from accepts array', () => {
|
|
154
|
-
const bchjs = new BCHJS(
|
|
241
|
+
const bchjs = new BCHJS({
|
|
242
|
+
restURL: 'http://localhost:3000/v5/'
|
|
243
|
+
})
|
|
155
244
|
|
|
156
245
|
const accepts = [
|
|
157
246
|
{
|
|
@@ -175,7 +264,9 @@ describe('#X402 Integration', () => {
|
|
|
175
264
|
})
|
|
176
265
|
|
|
177
266
|
it('should throw an error when no BCH requirements found', () => {
|
|
178
|
-
const bchjs = new BCHJS(
|
|
267
|
+
const bchjs = new BCHJS({
|
|
268
|
+
restURL: 'http://localhost:3000/v5/'
|
|
269
|
+
})
|
|
179
270
|
|
|
180
271
|
const accepts = [
|
|
181
272
|
{
|