@psf/bch-js 4.20.27 → 4.22.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@psf/bch-js",
3
- "version": "4.20.27",
3
+ "version": "4.22.1",
4
4
  "description": "The FullStack.cash JavaScript library for Bitcoin Cash and SLP Tokens",
5
5
  "author": "Chris Troutner <chris.troutner@gmail.com>",
6
6
  "contributors": [
@@ -14,12 +14,12 @@
14
14
  "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",
15
15
  "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/",
16
16
  "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/",
17
- "test:integration:testnet": "IS_USING_FREE_TIER=true mocha --timeout 30000 test/integration/chains/testnet/",
17
+ "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/",
18
18
  "test:integration:local:abc": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
19
19
  "test:integration:local:bchn": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
20
20
  "test:integration:local:testnet": "RESTURL=http://localhost:4000/v5/ mocha --timeout 30000 test/integration/chains/testnet",
21
- "test:integration:decatur:bchn": "export RESTURL=http://192.168.0.36:3000/v5/ && mocha --timeout 30000 test/integration/ && mocha --timeout 30000 test/integration/chains/bchn/",
22
- "test:integration:decatur:abc": "export RESTURL=http://192.168.0.38:3000/v5/ && mocha --timeout 30000 test/integration && mocha --timeout 30000 test/integration/chains/abc/",
21
+ "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/",
22
+ "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/",
23
23
  "test:integration:temp:bchn": "export RESTURL=http://157.90.174.219:3000/v5/ && mocha --timeout 30000 test/integration/",
24
24
  "test:temp": "export RESTURL=http://localhost:3000/v5/ && mocha --timeout 30000 -g '#Encryption' test/integration/",
25
25
  "test:temp2": "mocha --timeout=30000 -g '#TransactionLib' test/unit/",
package/src/address.js CHANGED
@@ -186,6 +186,21 @@ class Address {
186
186
  return cashAddress.split(':')[1]
187
187
  }
188
188
 
189
+ /**
190
+ * @api Address.toHash160() toHash160()
191
+ * @apiName toHash160
192
+ * @apiGroup Address
193
+ * @apiDescription Converts any address format to hash160
194
+ *
195
+ * @apiExample Example usage:
196
+ * // cash address mainnet p2pkh
197
+ * bchjs.Address.toHash160("bitcoincash:qptnmya5wkly7xf97wm5ak23yqdsz3l2cyj7k9vyyh")
198
+ * // 573d93b475be4f1925f3b74ed951201b0147eac1
199
+ *
200
+ * // cash address mainnet p2sh
201
+ * bchjs.Address.toHash160("bitcoincash:pp7ushdxf5we8mcpaa3wqgsuqt639cu59ur5xu5fug")
202
+ * // 7dc85da64d1d93ef01ef62e0221c02f512e3942f
203
+ */
189
204
  // Converts any address format to hash160
190
205
  toHash160 (address) {
191
206
  const legacyAddress = this.toLegacyAddress(address)
package/src/bch-js.js CHANGED
@@ -33,10 +33,12 @@ const Encryption = require('./encryption')
33
33
  const Utxo = require('./utxo')
34
34
  const Transaction = require('./transaction')
35
35
  const DSProof = require('./dsproof')
36
+ const Ecash = require('./ecash')
36
37
 
37
38
  // Indexers
38
39
  const Ninsight = require('./ninsight')
39
40
  const Electrumx = require('./electrumx')
41
+ const PsfSlpIndexer = require('./psf-slp-indexer')
40
42
 
41
43
  class BCHJS {
42
44
  constructor (config) {
@@ -118,6 +120,9 @@ class BCHJS {
118
120
  this.Transaction = new Transaction(libConfig)
119
121
 
120
122
  this.DSProof = new DSProof(libConfig)
123
+ this.eCash = new Ecash()
124
+
125
+ this.PsfSlpIndexer = new PsfSlpIndexer(libConfig)
121
126
  }
122
127
  }
123
128
 
package/src/ecash.js ADDED
@@ -0,0 +1,47 @@
1
+ /*
2
+ Utility library for converting units with eCash.
3
+ */
4
+
5
+ class eCash {
6
+ /**
7
+ * @api eCash.toSatoshi() toSatoshi()
8
+ * @apiName toSatoshi
9
+ * @apiGroup eCash
10
+ * @apiDescription
11
+ * Convert XEC units into satoshi units
12
+ *
13
+ * @apiExample Example usage:
14
+ * // convert 10,704.35 XEC to satoshis:
15
+ * bchjs.eCash.toSatoshi(10704.35)
16
+ * // 1070435
17
+ */
18
+ toSatoshi (xec) {
19
+ if (typeof xec !== 'number') {
20
+ throw new Error('input must be a floating number representing XEC')
21
+ }
22
+
23
+ return Math.floor(xec * 100)
24
+ }
25
+
26
+ /**
27
+ * @api eCash.toXec() toXec()
28
+ * @apiName toXec
29
+ * @apiGroup eCash
30
+ * @apiDescription
31
+ * Convert satoshi units to XEC units
32
+ *
33
+ * @apiExample Example usage:
34
+ * // convert 1,070,435 satoshis to XEC:
35
+ * bchjs.eCash.toSatoshi(1070435)
36
+ * // 10704.35
37
+ */
38
+ toXec (sats) {
39
+ if (typeof sats !== 'number') {
40
+ throw new Error('input must be a floating number representing satoshis')
41
+ }
42
+
43
+ return sats / 100
44
+ }
45
+ }
46
+
47
+ module.exports = eCash
@@ -0,0 +1,292 @@
1
+ /*
2
+ This library interacts with the PSF slp indexer REST API endpoints operated
3
+ by FullStack.cash
4
+ */
5
+ // Public npm libraries
6
+ const axios = require('axios')
7
+
8
+ // let _this
9
+
10
+ class PsfSlpIndexer {
11
+ constructor (config) {
12
+ this.restURL = config.restURL
13
+ this.apiToken = config.apiToken
14
+ this.authToken = config.authToken
15
+
16
+ if (this.authToken) {
17
+ // Add Basic Authentication token to the authorization header.
18
+ this.axiosOptions = {
19
+ headers: {
20
+ authorization: this.authToken
21
+ }
22
+ }
23
+ } else {
24
+ // Add JWT token to the authorization header.
25
+ this.axiosOptions = {
26
+ headers: {
27
+ authorization: `Token ${this.apiToken}`
28
+ }
29
+ }
30
+ }
31
+
32
+ // _this = this
33
+ }
34
+
35
+ /**
36
+ * @api PsfSlpIndexer.status() status()
37
+ * @apiName Status
38
+ * @apiGroup PSF SLP
39
+ * @apiDescription Return status from psf slp indexer.
40
+ *
41
+ * @apiExample Example usage:
42
+ * (async () => {
43
+ * try {
44
+ * let status = await bchjs.PsfSlpIndexer.status();
45
+ * console.log(status);
46
+ * } catch(error) {
47
+ * console.error(error)
48
+ * }
49
+ * })()
50
+ *
51
+ * {
52
+ * "status": {
53
+ * "startBlockHeight": 543376,
54
+ * "syncedBlockHeight": 723249,
55
+ * "chainBlockHeight": 722679
56
+ * }
57
+ * }
58
+ *
59
+ */
60
+ async status () {
61
+ try {
62
+ const response = await axios.get(
63
+ `${this.restURL}psf/slp/status`,
64
+ this.axiosOptions
65
+ )
66
+ return response.data
67
+ } catch (error) {
68
+ if (error.response && error.response.data) throw error.response.data
69
+ else throw error
70
+ }
71
+ }
72
+
73
+ /**
74
+ * @api PsfSlpIndexer.balance() balance()
75
+ * @apiName SLP Balance
76
+ * @apiGroup PSF SLP
77
+ * @apiDescription Return slp balance for a single address.
78
+ *
79
+ * @apiExample Example usage:
80
+ * (async () => {
81
+ * try {
82
+ * let balance = await bchjs.PsfSlpIndexer.balance('bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n');
83
+ * console.log(balance);
84
+ * } catch(error) {
85
+ * console.error(error)
86
+ * }
87
+ * })()
88
+ *
89
+ * {
90
+ * balance: {
91
+ * utxos: [
92
+ * {
93
+ * txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
94
+ * vout: 1,
95
+ * type: 'token',
96
+ * qty: '1800',
97
+ * tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
98
+ * address: 'bitcoincash:qrqy3kj7r822ps6628vwqq5k8hyjl6ey3y4eea2m4s'
99
+ * }
100
+ * ],
101
+ * txs: [
102
+ * {
103
+ * txid: '078b2c48ed1db0d5d5996f2889b8d847a49200d0a781f6aa6752f740f312688f',
104
+ * height: 717796
105
+ * },
106
+ * {
107
+ * txid: 'a24a6a4abf06fabd799ecea4f8fac6a9ff21e6a8dd6169a3c2ebc03665329db9',
108
+ * height: 717832
109
+ * }
110
+ * ],
111
+ * balances: [
112
+ * {
113
+ * tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
114
+ * qty: '1800'
115
+ * }
116
+ * ]
117
+ * }
118
+ * }
119
+ *
120
+ */
121
+ async balance (address) {
122
+ try {
123
+ // Handle single address.
124
+ if (typeof address === 'string') {
125
+ const response = await axios.post(
126
+ `${this.restURL}psf/slp/address`,
127
+ { address },
128
+ this.axiosOptions
129
+ )
130
+ return response.data
131
+ }
132
+ throw new Error('Input address must be a string.')
133
+ } catch (error) {
134
+ if (error.response && error.response.data) throw error.response.data
135
+ else throw error
136
+ }
137
+ }
138
+
139
+ /**
140
+ * @api PsfSlpIndexer.tokenStats() tokenStats()
141
+ * @apiName Token Stats
142
+ * @apiGroup PSF SLP
143
+ * @apiDescription Return list stats for a single slp token.
144
+ *
145
+ * @apiExample Example usage:
146
+ * (async () => {
147
+ * try {
148
+ * let tokenStats = await bchjs.PsfSlpIndexer.tokenStats('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
149
+ * console.log(tokenStats);
150
+ * } catch(error) {
151
+ * console.error(error)
152
+ * }
153
+ * })()
154
+ *
155
+ * {
156
+ * tokenData: {
157
+ * type: 1,
158
+ * ticker: 'TROUT',
159
+ * name: "Trout's test token",
160
+ * tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
161
+ * documentUri: 'troutsblog.com',
162
+ * documentHash: '',
163
+ * decimals: 2,
164
+ * mintBatonIsActive: true,
165
+ * tokensInCirculationBN: '100098953386',
166
+ * tokensInCirculationStr: '100098953386',
167
+ * blockCreated: 622414,
168
+ * totalBurned: '1046614',
169
+ * totalMinted: '100100000000'
170
+ * txs: [
171
+ * {
172
+ * txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
173
+ * height: 622414,
174
+ * type: 'GENESIS',
175
+ * qty: '100000000000'
176
+ * }
177
+ * ]
178
+ * }
179
+ * }
180
+ *
181
+ */
182
+
183
+ async tokenStats (tokenId) {
184
+ try {
185
+ // Handle single address.
186
+ if (typeof tokenId === 'string') {
187
+ const response = await axios.post(
188
+ `${this.restURL}psf/slp/token`,
189
+ { tokenId },
190
+ this.axiosOptions
191
+ )
192
+ return response.data
193
+ }
194
+ throw new Error('Input tokenId must be a string.')
195
+ } catch (error) {
196
+ if (error.response && error.response.data) throw error.response.data
197
+ else throw error
198
+ }
199
+ }
200
+
201
+ /**
202
+ * @api PsfSlpIndexer.tx() tx()
203
+ * @apiName SLP Transaction Data
204
+ * @apiGroup PSF SLP
205
+ * @apiDescription Return slp transaction data.
206
+ *
207
+ * @apiExample Example usage:
208
+ * (async () => {
209
+ * try {
210
+ * let txData = await bchjs.PsfSlpIndexer.tx('a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2');
211
+ * console.log(txData);
212
+ * } catch(error) {
213
+ * console.error(error)
214
+ * }
215
+ * })()
216
+ *
217
+ * {
218
+ * txData: {
219
+ * txid: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
220
+ * hash: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
221
+ * version: 2,
222
+ * size: 339,
223
+ * locktime: 0,
224
+ * vin: [
225
+ * {
226
+ * txid: '8370db30d94761ab9a11b71ecd22541151bf6125c8c613f0f6fab8ab794565a7',
227
+ * vout: 0,
228
+ * scriptSig: {
229
+ * asm: '304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4[ALL|FORKID] 02791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851',
230
+ * hex: '47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851'
231
+ * },
232
+ * sequence: 4294967295,
233
+ * address: 'bitcoincash:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qwgaqm3wq',
234
+ * value: 0.00051303,
235
+ * tokenQty: 0,
236
+ * tokenQtyStr: '0',
237
+ * tokenId: null
238
+ * }
239
+ * ],
240
+ * vout: [
241
+ * {
242
+ * value: 0,
243
+ * n: 0,
244
+ * scriptPubKey: {
245
+ * asm: 'OP_RETURN 5262419 1 47454e45534953 54524f5554 54726f75742773207465737420746f6b656e 74726f757473626c6f672e636f6d 0 2 2 000000174876e800',
246
+ * hex: '6a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e800',
247
+ * type: 'nulldata'
248
+ * },
249
+ * tokenQtyStr: '0',
250
+ * tokenQty: 0
251
+ * }
252
+ * ],
253
+ * hex: '0200000001a7654579abb8faf6f013c6c82561bf51115422cd1eb7119aab6147d930db7083000000006a47304402207e9631c53dfc8a9a793d1916469628c6b7c5780c01c2f676d51ef21b0ba4926f022069feb471ec869a49f8d108d0aaba04e7cd36e60a7500109d86537f55698930d4412102791b19a39165dbd83403d6df268d44fd621da30581b0b6e5cb15a7101ed58851ffffffff040000000000000000476a04534c500001010747454e455349530554524f55541254726f75742773207465737420746f6b656e0e74726f757473626c6f672e636f6d4c000102010208000000174876e80022020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088ac22020000000000001976a914db4d39ceb7794ffe5d06855f249e1d3a7f1b024088accec20000000000001976a9145904159f2f69bfa63eefa712633a0d96dc2e7e8888ac00000000',
254
+ * blockhash: '0000000000000000009f65225a3e12e23a7ea057c869047e0f36563a1f410267',
255
+ * confirmations: 97398,
256
+ * time: 1581773131,
257
+ * blocktime: 1581773131,
258
+ * blockheight: 622414,
259
+ * isSlpTx: true,
260
+ * tokenTxType: 'GENESIS',
261
+ * tokenId: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
262
+ * tokenType: 1,
263
+ * tokenTicker: 'TROUT',
264
+ * tokenName: "Trout's test token",
265
+ * tokenDecimals: 2,
266
+ * tokenUri: 'troutsblog.com',
267
+ * tokenDocHash: '',
268
+ * isValidSlp: true
269
+ * }
270
+ * }
271
+ *
272
+ */
273
+ async tx (txid) {
274
+ try {
275
+ // Handle single address.
276
+ if (typeof txid === 'string') {
277
+ const response = await axios.post(
278
+ `${this.restURL}psf/slp/txid`,
279
+ { txid },
280
+ this.axiosOptions
281
+ )
282
+ return response.data
283
+ }
284
+ throw new Error('Input txid must be a string.')
285
+ } catch (error) {
286
+ if (error.response && error.response.data) throw error.response.data
287
+ else throw error
288
+ }
289
+ }
290
+ }
291
+
292
+ module.exports = PsfSlpIndexer
@@ -36,7 +36,7 @@ class Address extends BCHJSAddress {
36
36
  * bchjs.SLP.Address.toSLPAddress('qzm47qz5ue99y9yl4aca7jnz7dwgdenl85jkfx3znl')
37
37
  * // simpleledger:qzm47qz5ue99y9yl4aca7jnz7dwgdenl857dzayzdp
38
38
  *
39
- * // tesnet legacy
39
+ * // testnet legacy
40
40
  * bchjs.SLP.Address.toSLPAddress('msDbtTj7kWXPpYaR7PQmMK84i66fJqQMLx')
41
41
  * // slptest:qzq9je6pntpva3wf6scr7mlnycr54sjgeqauyclpwv
42
42
  *
package/src/utxo.js CHANGED
@@ -183,6 +183,10 @@ class UTXO {
183
183
  */
184
184
  async get (address, useWhitelist = false) {
185
185
  try {
186
+ if (!address) {
187
+ throw new Error('Address must be an array or a string')
188
+ }
189
+
186
190
  // Convert address to an array if it is a string.
187
191
  if (typeof address === 'string') address = [address]
188
192
 
@@ -313,6 +313,14 @@ describe('#blockchain', () => {
313
313
  assert.isString(result)
314
314
  })
315
315
  })
316
+
317
+ describe('#getBlockchainInfo', () => {
318
+ it('should get info about the blockchain', async () => {
319
+ const result = await bchjs.Blockchain.getBlockchainInfo()
320
+
321
+ console.log(`blockchain info: ${JSON.stringify(result, null, 2)}`)
322
+ })
323
+ })
316
324
  })
317
325
 
318
326
  function sleep (ms) {
@@ -0,0 +1,74 @@
1
+ /*
2
+ Integration tests for the psf-slp-indexer.js library
3
+ */
4
+
5
+ const assert = require('chai').assert
6
+
7
+ const BCHJS = require('../../../../src/bch-js')
8
+ let bchjs
9
+
10
+ describe('#psf-slp-indexer', () => {
11
+ beforeEach(async () => {
12
+ // Introduce a delay so that the BVT doesn't trip the rate limits.
13
+ if (process.env.IS_USING_FREE_TIER) await sleep(3000)
14
+
15
+ bchjs = new BCHJS()
16
+ })
17
+
18
+ describe('#status', () => {
19
+ it('should return the status of the indexer.', async () => {
20
+ const result = await bchjs.PsfSlpIndexer.status()
21
+ // console.log('result: ', result)
22
+
23
+ assert.property(result.status, 'startBlockHeight')
24
+ assert.property(result.status, 'syncedBlockHeight')
25
+ assert.property(result.status, 'chainBlockHeight')
26
+ })
27
+ })
28
+
29
+ describe('#balance', () => {
30
+ it('should get balance data for an address.', async () => {
31
+ const addr = 'bitcoincash:qzmd5vxgh9m22m6fgvm57yd6kjnjl9qnwywsf3583n'
32
+
33
+ const result = await bchjs.PsfSlpIndexer.balance(addr)
34
+ // console.log('result: ', result)
35
+
36
+ assert.property(result.balance, 'utxos')
37
+ assert.property(result.balance, 'txs')
38
+ assert.property(result.balance, 'balances')
39
+ })
40
+ })
41
+
42
+ describe('#tokenStats', () => {
43
+ it('should get stats on a token', async () => {
44
+ const tokenId =
45
+ '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0'
46
+
47
+ const result = await bchjs.PsfSlpIndexer.tokenStats(tokenId)
48
+ // console.log('result: ', result)
49
+
50
+ assert.property(result.tokenData, 'documentUri')
51
+ assert.property(result.tokenData, 'txs')
52
+ assert.property(result.tokenData, 'totalBurned')
53
+ })
54
+ })
55
+
56
+ describe('#tx', () => {
57
+ it('should get hydrated tx data', async () => {
58
+ const txid =
59
+ '83361c34cac2ea7f9ca287fca57a96cc0763719f0cdf4850f9696c1e68eb635c'
60
+
61
+ const result = await bchjs.PsfSlpIndexer.tx(txid)
62
+ // console.log('result: ', result)
63
+
64
+ assert.property(result.txData, 'vin')
65
+ assert.property(result.txData, 'vout')
66
+ assert.property(result.txData, 'isValidSlp')
67
+ })
68
+ })
69
+ })
70
+
71
+ // Promise-based sleep function
72
+ function sleep (ms) {
73
+ return new Promise(resolve => setTimeout(resolve, ms))
74
+ }