@psf/bch-js 7.1.0 → 7.1.1

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 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.0",
3
+ "version": "7.1.1",
4
4
  "type": "module",
5
- "description": "A JavaScript library for working with Bitcoin Cash, eCash, and SLP Tokens",
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,9 +12,10 @@
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:bchn": "export RESTURL=https://bchn.fullstack.cash/v6/ && export IS_USING_FREE_TIER=true && mocha --timeout 30000 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://5.78.147.3:5942/v6 && export BCHJSBEARERTOKEN=temp01 && mocha --timeout 30000 test/integration/",
18
+ "test:integration:local:auth": "export RESTURL=http://192.168.1.115:5942/v6 && export BCHJSBEARERTOKEN=temp01 && mocha --timeout 30000 test/integration/",
18
19
  "test:integration:decatur": "export RESTURL=http://192.168.2.127:5942/v6 && mocha --timeout 30000 test/integration/",
19
20
  "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/",
package/src/bch-js.js CHANGED
@@ -87,7 +87,16 @@ class BCHJS {
87
87
  this.wif = process.env.BCHJSWIF
88
88
  }
89
89
  this.paymentAmountSats = (config && config.paymentAmountSats) || 2000 * 10
90
- this.bchServerURL = (config && config.bchServerURL) || 'https://free-bch.fullstack.cash'
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'
99
+ }
91
100
 
92
101
  const libConfig = {
93
102
  restURL: this.restURL,
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
23
  assert.strictEqual(bchjs.paymentAmountSats, 20000)
22
- assert.strictEqual(bchjs.bchServerURL, 'https://free-bch.fullstack.cash')
24
+ assert.strictEqual(bchjs.bchServerURL, 'https://bch.fullstack.cash')
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
 
@@ -55,7 +60,7 @@ describe('#X402 Integration', () => {
55
60
 
56
61
  assert.strictEqual(bchjs.wif, '')
57
62
  assert.strictEqual(bchjs.paymentAmountSats, 20000)
58
- assert.strictEqual(bchjs.bchServerURL, 'https://free-bch.fullstack.cash')
63
+ assert.strictEqual(bchjs.bchServerURL, 'https://bch.fullstack.cash')
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({ wif: configWif })
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')
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
  {