mainnet-js 2.3.15 → 2.4.0

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.
Files changed (41) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/{mainnet-2.3.15.js → mainnet-2.4.0.js} +15 -5
  3. package/dist/module/history/electrumTransformer.d.ts +11 -3
  4. package/dist/module/history/electrumTransformer.d.ts.map +1 -1
  5. package/dist/module/history/electrumTransformer.js +199 -195
  6. package/dist/module/history/electrumTransformer.js.map +1 -1
  7. package/dist/module/history/interface.d.ts +19 -13
  8. package/dist/module/history/interface.d.ts.map +1 -1
  9. package/dist/module/interface.d.ts +10 -1
  10. package/dist/module/interface.d.ts.map +1 -1
  11. package/dist/module/interface.js.map +1 -1
  12. package/dist/module/network/ElectrumNetworkProvider.d.ts +8 -6
  13. package/dist/module/network/ElectrumNetworkProvider.d.ts.map +1 -1
  14. package/dist/module/network/ElectrumNetworkProvider.js +28 -6
  15. package/dist/module/network/ElectrumNetworkProvider.js.map +1 -1
  16. package/dist/module/network/NetworkProvider.d.ts +8 -3
  17. package/dist/module/network/NetworkProvider.d.ts.map +1 -1
  18. package/dist/module/util/header.d.ts +3 -0
  19. package/dist/module/util/header.d.ts.map +1 -0
  20. package/dist/module/util/header.js +13 -0
  21. package/dist/module/util/header.js.map +1 -0
  22. package/dist/module/util/index.d.ts +1 -0
  23. package/dist/module/util/index.d.ts.map +1 -1
  24. package/dist/module/util/index.js +1 -0
  25. package/dist/module/util/index.js.map +1 -1
  26. package/dist/module/wallet/Wif.d.ts +27 -6
  27. package/dist/module/wallet/Wif.d.ts.map +1 -1
  28. package/dist/module/wallet/Wif.js +29 -7
  29. package/dist/module/wallet/Wif.js.map +1 -1
  30. package/dist/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +1 -1
  32. package/src/history/electrumTransformer.test.ts +112 -55
  33. package/src/history/electrumTransformer.ts +279 -284
  34. package/src/history/interface.ts +19 -13
  35. package/src/interface.ts +11 -1
  36. package/src/network/ElectrumNetworkProvider.ts +58 -11
  37. package/src/network/NetworkProvider.ts +13 -3
  38. package/src/util/header.test.ts +34 -0
  39. package/src/util/header.ts +16 -0
  40. package/src/util/index.ts +1 -0
  41. package/src/wallet/Wif.ts +55 -21
@@ -5,7 +5,13 @@ import {
5
5
  ConnectionStatus,
6
6
  } from "electrum-cash";
7
7
  import { default as NetworkProvider } from "./NetworkProvider.js";
8
- import { HeaderI, TxI, UtxoI, ElectrumBalanceI } from "../interface.js";
8
+ import {
9
+ HexHeaderI,
10
+ TxI,
11
+ UtxoI,
12
+ ElectrumBalanceI,
13
+ HeaderI,
14
+ } from "../interface.js";
9
15
  import { Network } from "../interface.js";
10
16
  import { delay } from "../util/delay.js";
11
17
  import { ElectrumRawTransaction, ElectrumUtxo } from "./interface.js";
@@ -13,6 +19,7 @@ import { ElectrumRawTransaction, ElectrumUtxo } from "./interface.js";
13
19
  import { CancelWatchFn } from "../wallet/interface.js";
14
20
  import { getTransactionHash } from "../util/transaction.js";
15
21
  import { Config } from "../config.js";
22
+ import { decodeHeader } from "../util/header.js";
16
23
 
17
24
  export default class ElectrumNetworkProvider implements NetworkProvider {
18
25
  public electrum: ElectrumCluster | ElectrumClient;
@@ -105,6 +112,35 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
105
112
  return result.confirmed + result.unconfirmed;
106
113
  }
107
114
 
115
+ static rawHeaderCache = {};
116
+ async getHeader(
117
+ height: number,
118
+ verbose: boolean = false
119
+ ): Promise<HeaderI | HexHeaderI> {
120
+ const key = `header-${this.network}-${height}-${verbose}`;
121
+
122
+ if (Config.UseLocalStorageCache) {
123
+ const cached = localStorage.getItem(key);
124
+ if (cached) {
125
+ return verbose ? decodeHeader(JSON.parse(cached)) : JSON.parse(cached);
126
+ }
127
+ } else {
128
+ ElectrumNetworkProvider.rawTransactionCache[key];
129
+ }
130
+
131
+ const result = (await this.performRequest(
132
+ "blockchain.header.get",
133
+ height
134
+ )) as HexHeaderI;
135
+ if (Config.UseLocalStorageCache) {
136
+ localStorage.setItem(key, JSON.stringify(result));
137
+ } else {
138
+ ElectrumNetworkProvider.rawTransactionCache[key] = result;
139
+ }
140
+
141
+ return verbose ? decodeHeader(result) : result;
142
+ }
143
+
108
144
  async getBlockHeight(): Promise<number> {
109
145
  return ((await this.performRequest("blockchain.headers.get_tip")) as any)
110
146
  .height;
@@ -116,7 +152,7 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
116
152
  verbose: boolean = false,
117
153
  loadInputValues: boolean = false
118
154
  ): Promise<string> {
119
- const key = `${this.network}-${txHash}-${verbose}-${loadInputValues}`;
155
+ const key = `tx-${this.network}-${txHash}-${verbose}-${loadInputValues}`;
120
156
 
121
157
  if (Config.UseLocalStorageCache) {
122
158
  const cached = localStorage.getItem(key);
@@ -219,10 +255,16 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
219
255
  }
220
256
 
221
257
  // Get transaction history of a given cashaddr
222
- async getHistory(cashaddr: string): Promise<TxI[]> {
258
+ async getHistory(
259
+ cashaddr: string,
260
+ fromHeight: number = 0,
261
+ toHeight: number = -1
262
+ ): Promise<TxI[]> {
223
263
  const result = (await this.performRequest(
224
264
  "blockchain.address.get_history",
225
- cashaddr
265
+ cashaddr,
266
+ fromHeight,
267
+ toHeight
226
268
  )) as TxI[];
227
269
 
228
270
  return result;
@@ -316,10 +358,13 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
316
358
  });
317
359
  }
318
360
 
319
- // Wait for the next block or a block at given blockchain height.
320
- public watchBlocks(callback: (header: HeaderI) => void): CancelWatchFn {
321
- let acknowledged = false;
322
- const waitForBlockCallback = (_header: HeaderI | HeaderI[]) => {
361
+ // watch for block headers and block height, if `skipCurrentHeight` is set, the notification about current block will not arrive
362
+ public watchBlocks(
363
+ callback: (header: HexHeaderI) => void,
364
+ skipCurrentHeight: boolean = true
365
+ ): CancelWatchFn {
366
+ let acknowledged = !skipCurrentHeight;
367
+ const waitForBlockCallback = (_header: HexHeaderI | HexHeaderI[]) => {
323
368
  if (!acknowledged) {
324
369
  acknowledged = true;
325
370
  return;
@@ -336,7 +381,7 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
336
381
  }
337
382
 
338
383
  // Wait for the next block or a block at given blockchain height.
339
- public async waitForBlock(height?: number): Promise<HeaderI> {
384
+ public async waitForBlock(height?: number): Promise<HexHeaderI> {
340
385
  return new Promise(async (resolve) => {
341
386
  const cancelWatch = this.watchBlocks(async (header) => {
342
387
  if (height === undefined || header.height >= height!) {
@@ -348,13 +393,15 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
348
393
  }
349
394
 
350
395
  // subscribe to notifications sent when new block is found, the block header is sent to callback
351
- async subscribeToHeaders(callback: (header: HeaderI) => void): Promise<void> {
396
+ async subscribeToHeaders(
397
+ callback: (header: HexHeaderI) => void
398
+ ): Promise<void> {
352
399
  await this.subscribeRequest("blockchain.headers.subscribe", callback);
353
400
  }
354
401
 
355
402
  // unsubscribe to notifications sent when new block is found
356
403
  async unsubscribeFromHeaders(
357
- callback: (header: HeaderI) => void
404
+ callback: (header: HexHeaderI) => void
358
405
  ): Promise<void> {
359
406
  await this.unsubscribeRequest("blockchain.headers.subscribe", callback);
360
407
  }
@@ -1,4 +1,4 @@
1
- import { TxI, UtxoI, Network, HeaderI } from "../interface.js";
1
+ import { TxI, UtxoI, Network, HexHeaderI, HeaderI } from "../interface.js";
2
2
 
3
3
  export default interface NetworkProvider {
4
4
  /**
@@ -20,6 +20,12 @@ export default interface NetworkProvider {
20
20
  */
21
21
  getBalance(cashaddr: string): Promise<number>;
22
22
 
23
+ /**
24
+ * @returns The block header fetched at blockheight @param height.
25
+ * @param verbose steers the response type. If true, a verbose response is returned.
26
+ */
27
+ getHeader(height: number, verbose: boolean): Promise<HeaderI | HexHeaderI>;
28
+
23
29
  /**
24
30
  * @returns The current block height.
25
31
  */
@@ -65,14 +71,18 @@ export default interface NetworkProvider {
65
71
  * @throws {Error} When failing to get history.
66
72
  * @returns Array of transactions.
67
73
  */
68
- getHistory(cashaddr: string): Promise<TxI[]>;
74
+ getHistory(
75
+ cashaddr: string,
76
+ fromHeight?: number,
77
+ toHeight?: number
78
+ ): Promise<TxI[]>;
69
79
 
70
80
  /**
71
81
  * Wait for the next block or a block at given blockchain height.
72
82
  * @param height If specified, waits for blockchain to reach this height.
73
83
  * @returns Block header.
74
84
  */
75
- waitForBlock(height?: number): Promise<HeaderI>;
85
+ waitForBlock(height?: number): Promise<HexHeaderI>;
76
86
 
77
87
  /**
78
88
  * Subscribe to the address change events
@@ -0,0 +1,34 @@
1
+ import { HeaderI, HexHeaderI } from "../interface";
2
+ import { disconnectProviders, initProviders } from "../network";
3
+ import { Wallet } from "../wallet/Wif";
4
+
5
+ beforeAll(async () => {
6
+ await initProviders();
7
+ });
8
+ afterAll(async () => {
9
+ await disconnectProviders();
10
+ });
11
+
12
+ describe("header tests", () => {
13
+ it("decodeHeader", async () => {
14
+ const wallet = await Wallet.newRandom();
15
+ const hexHeader = (await wallet.provider!.getHeader(854724)) as HexHeaderI;
16
+ expect(hexHeader.height).toBe(854724);
17
+ expect(hexHeader.hex).toBe(
18
+ "0080c4339674a81d4e35a5b590b15a6b69f93b7b22bd14845b3517000000000000000000128a8b776c82fda87f60c6fdb0de26f021cdf39ffd835dc309eb1fc6bbfaac343e2f96662d5202184db2428f"
19
+ );
20
+
21
+ const header = (await wallet.provider!.getHeader(854724, true)) as HeaderI;
22
+ expect(header.version).toBe(868515840);
23
+ expect(header.previousBlockHash).toBe(
24
+ "00000000000000000017355b8414bd227b3bf9696b5ab190b5a5354e1da87496"
25
+ );
26
+ expect(header.merkleRoot).toBe(
27
+ "34acfabbc61feb09c35d83fd9ff3cd21f026deb0fdc6607fa8fd826c778b8a12"
28
+ );
29
+ expect(header.timestamp).toBe(1721118526);
30
+ expect(header.bits).toBe(402805293);
31
+ expect(header.nonce).toBe(2403512909);
32
+ expect(header.height).toBe(854724);
33
+ });
34
+ });
@@ -0,0 +1,16 @@
1
+ import { HeaderI, HexHeaderI } from "../interface";
2
+
3
+ export const decodeHeader = (hexHeader: HexHeaderI): HeaderI => {
4
+ const result = {} as HeaderI;
5
+
6
+ const header = Buffer.from(hexHeader.hex, "hex");
7
+ result.version = header.readUInt32LE(0);
8
+ result.previousBlockHash = header.subarray(4, 36).reverse().toString("hex");
9
+ result.merkleRoot = header.subarray(36, 68).reverse().toString("hex");
10
+ result.timestamp = header.readUInt32LE(68);
11
+ result.bits = header.readUInt32LE(72);
12
+ result.nonce = header.readUInt32LE(76);
13
+ result.height = hexHeader.height;
14
+
15
+ return result;
16
+ };
package/src/util/index.ts CHANGED
@@ -34,3 +34,4 @@ export {
34
34
  balanceResponseFromSatoshi,
35
35
  } from "./balanceObjectFromSatoshi.js";
36
36
  export * from "./sumUtxoValue.js";
37
+ export { decodeHeader } from "./header.js";
package/src/wallet/Wif.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  // Stable
3
3
  import {
4
4
  decodeCashAddress,
5
- decodeCashAddressVersionByte,
6
5
  encodeHdPublicKey,
7
6
  HdKeyNetwork,
8
7
  secp256k1,
@@ -25,7 +24,7 @@ import {
25
24
  import { mnemonicToSeedSync, generateMnemonic } from "@scure/bip39";
26
25
  import { NetworkType, prefixFromNetworkMap, UnitEnum } from "../enum.js";
27
26
 
28
- import { Network, HeaderI, TxI, NFTCapability } from "../interface.js";
27
+ import { Network, HexHeaderI, TxI, NFTCapability } from "../interface.js";
29
28
 
30
29
  import { networkPrefixMap } from "../enum.js";
31
30
  import { PrivateKeyI, UtxoI } from "../interface.js";
@@ -104,14 +103,14 @@ import { amountInSatoshi } from "../util/amountInSatoshi.js";
104
103
  import { getXPubKey } from "../util/getXPubKey.js";
105
104
  import { DERIVATION_PATHS, DUST_UTXO_THRESHOLD } from "../constant.js";
106
105
 
107
- import { TransactionHistoryI } from "../history/interface.js";
108
106
  import { getAddressHistory } from "../history/electrumTransformer.js";
109
- import { IdentitySnapshot, Registry } from "./bcmr-v2.schema.js";
107
+ import { IdentitySnapshot } from "./bcmr-v2.schema.js";
110
108
  import { BCMR } from "./Bcmr.js";
111
109
  import { qrAddress } from "../qr/Qr.js";
112
110
  import { ImageI } from "../qr/interface.js";
113
111
  import { Config } from "../config.js";
114
112
  import { checkUtxos } from "../util/checkUtxos.js";
113
+ import { TransactionHistoryItem } from "../history/interface.js";
115
114
 
116
115
  //#endregion Imports
117
116
 
@@ -1282,25 +1281,53 @@ export class Wallet extends BaseWallet {
1282
1281
  }
1283
1282
 
1284
1283
  // gets transaction history of this wallet
1285
- public async getRawHistory(): Promise<TxI[]> {
1286
- return await this.provider!.getHistory(this.cashaddr!);
1284
+ public async getRawHistory(
1285
+ fromHeight: number = 0,
1286
+ toHeight: number = -1
1287
+ ): Promise<TxI[]> {
1288
+ return await this.provider!.getHistory(
1289
+ this.cashaddr!,
1290
+ fromHeight,
1291
+ toHeight
1292
+ );
1287
1293
  }
1288
1294
 
1289
- // gets transaction history of this wallet
1290
- public async getHistory(
1291
- unit: UnitEnum,
1292
- start?: number,
1293
- count?: number,
1294
- collapseChange?: boolean
1295
- ): Promise<TransactionHistoryI> {
1296
- return getAddressHistory(
1297
- this.cashaddr!,
1298
- this.provider!,
1295
+ /**
1296
+ * getHistory gets transaction history of this wallet with most data decoded and ready to present to user
1297
+ * @note balance calculations are valid only if querying to the blockchain tip (`toHeight` === -1, `count` === -1)
1298
+ * @note this method is heavy on network calls, if invoked in browser use of cache is advised, @see `Config.UseLocalStorageCache`
1299
+ * @note this method tries to recreate the history tab view of Electron Cash wallet, however, it may not be 100% accurate if the tnransaction value changes are the same in the same block (ordering)
1300
+ *
1301
+ * @param unit optional, BCH or currency unit to present balance and balance changes. If unit is currency like USD or EUR, balances will be subject to possible rounding errors. Default 0
1302
+ * @param fromHeight optional, if set, history will be limited. Default 0
1303
+ * @param toHeight optional, if set, history will be limited. Default -1, meaning that all history items will be returned, including mempool
1304
+ * @param start optional, if set, the result set will be paginated with offset `start`
1305
+ * @param count optional, if set, the result set will be paginated with `count`. Default -1, meaning that all history items will be returned
1306
+ *
1307
+ * @returns an array of transaction history items, with input values and addresses encoded in cashaddress format. @see `TransactionHistoryItem` type
1308
+ */
1309
+ public async getHistory({
1310
+ unit = "sat",
1311
+ fromHeight = 0,
1312
+ toHeight = -1,
1313
+ start = 0,
1314
+ count = -1,
1315
+ }: {
1316
+ unit?: UnitEnum;
1317
+ fromHeight?: number;
1318
+ toHeight?: number;
1319
+ start?: number;
1320
+ count?: number;
1321
+ }): Promise<TransactionHistoryItem[]> {
1322
+ return getAddressHistory({
1323
+ address: this.cashaddr!,
1324
+ provider: this.provider!,
1299
1325
  unit,
1326
+ fromHeight,
1327
+ toHeight,
1300
1328
  start,
1301
1329
  count,
1302
- collapseChange
1303
- );
1330
+ });
1304
1331
  }
1305
1332
 
1306
1333
  // gets last transaction of this wallet
@@ -1393,11 +1420,18 @@ export class Wallet extends BaseWallet {
1393
1420
  * watchBlocks Watch network blocks
1394
1421
  *
1395
1422
  * @param callback callback with a block header object
1423
+ * @param skipCurrentHeight if set, the notification about current block will not arrive
1396
1424
  *
1397
1425
  * @returns a function which will cancel watching upon evaluation
1398
1426
  */
1399
- public watchBlocks(callback: (header: HeaderI) => void): CancelWatchFn {
1400
- return (this.provider! as ElectrumNetworkProvider).watchBlocks(callback);
1427
+ public watchBlocks(
1428
+ callback: (header: HexHeaderI) => void,
1429
+ skipCurrentHeight: boolean = true
1430
+ ): CancelWatchFn {
1431
+ return (this.provider! as ElectrumNetworkProvider).watchBlocks(
1432
+ callback,
1433
+ skipCurrentHeight
1434
+ );
1401
1435
  }
1402
1436
 
1403
1437
  /**
@@ -1406,7 +1440,7 @@ export class Wallet extends BaseWallet {
1406
1440
  * @param height if specified waits for this exact blockchain height, otherwise resolves with the next block
1407
1441
  *
1408
1442
  */
1409
- public async waitForBlock(height?: number): Promise<HeaderI> {
1443
+ public async waitForBlock(height?: number): Promise<HexHeaderI> {
1410
1444
  return (this.provider! as ElectrumNetworkProvider).waitForBlock(height);
1411
1445
  }
1412
1446
  //#endregion Funds